1 /* vi:ai:et:ts=8 sw=2
2  */
3 /*
4  * wzdftpd - a modular and cool ftp server
5  * Copyright (C) 2002-2004  Pierre Chifflier
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  *
21  * As a special exemption, Pierre Chifflier
22  * and other respective copyright holders give permission to link this program
23  * with OpenSSL, and distribute the resulting executable, without including
24  * the source code for OpenSSL in the source distribution.
25  */
26 
27 #include "wzd_all.h"
28 
29 #ifndef WZD_USE_PCH
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35 
36 #ifdef WIN32
37 #include <winsock2.h>
38 #else
39 #include <sys/types.h>
40 #include <sys/socket.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #endif
44 
45 #ifndef HAVE_STRTOK_R
46 # include "libwzd-base/wzd_strtok_r.h"
47 #endif
48 
49 #include "wzd_structs.h"
50 
51 #include "wzd_group.h"
52 #include "wzd_perm.h"
53 #include "wzd_log.h"
54 #include "wzd_misc.h"
55 #include "wzd_user.h"
56 
57 #include "wzd_debug.h"
58 
59 #endif /* WZD_USE_PCH */
60 
61 #define BUFFER_LEN	2048
62 
63 
64 const char * perm_tab[] = {
65   "site",
66   "delete",
67   NULL
68 };
69 
70 
71 /** \brief Create and initialize an empty permission structure
72  * \ return a newly allocated structure
73  */
perm_create_empty_perm(void)74 static wzd_command_perm_t * perm_create_empty_perm(void)
75 {
76   wzd_command_perm_t * perm;
77 
78   perm = malloc(sizeof(wzd_command_perm_t));
79   if (perm == NULL) return NULL;
80 
81   memset(perm->command_name,0,256);
82   perm->entry_list = NULL;
83   perm->next_perm = NULL;
84 
85   return perm;
86 }
87 
88 /** \brief Create and initialize an empty permission entry structure
89  * \ return a newly allocated structure
90  */
perm_create_empty_entry(void)91 static wzd_command_perm_entry_t * perm_create_empty_entry(void)
92 {
93   wzd_command_perm_entry_t * entry;
94 
95   entry = malloc(sizeof(wzd_command_perm_entry_t));
96   if (entry == NULL) return NULL;
97 
98   memset(entry->target,0,256);
99   entry->next_entry = NULL;
100 
101   return entry;
102 }
103 
104 
105 /** \brief Remove the permission structure associated with \a commandname from list
106  * \param[in] commandname command name
107  * \param[in,out] perm_list permission list
108  * \return
109  *  - 0 if ok
110  *  - 1 if the command was not found
111  *  - -1 on error
112  */
perm_remove(const char * commandname,wzd_command_perm_t ** perm_list)113 int perm_remove(const char *commandname, wzd_command_perm_t ** perm_list)
114 {
115   wzd_command_perm_t * perm, * previous;
116   wzd_command_perm_entry_t * entry_current, * entry_next;
117 
118   if ( (!perm_list) || (!*perm_list) ) return -1;
119 
120   perm = *perm_list;
121   if (strcasecmp(perm->command_name,commandname)==0) {
122     /* first element */
123     entry_current = perm->entry_list;
124     while (entry_current) {
125       entry_next = entry_current->next_entry;
126       free(entry_current);
127       entry_current = entry_next;
128     }
129     *perm_list = perm->next_perm;
130     free(perm);
131     return 0;
132   }
133 
134   previous = perm;
135   perm = perm->next_perm;
136 
137   while(perm) {
138     if (strcasecmp(perm->command_name,commandname)==0) {
139       entry_current = perm->entry_list;
140       while (entry_current) {
141         entry_next = entry_current->next_entry;
142         free(entry_current);
143         entry_current = entry_next;
144       }
145       previous->next_perm = perm->next_perm;
146       free(perm);
147       return 0;
148     }
149     previous = perm;
150     perm = perm->next_perm;
151   };
152 
153   return 1; /* not found */
154 }
155 
156 /** \brief Free \a perm and all contained structures recursively
157  * \param perm permission list
158  */
perm_free_recursive(wzd_command_perm_t * perm)159 void perm_free_recursive(wzd_command_perm_t * perm)
160 {
161   wzd_command_perm_t * perm_next;
162   wzd_command_perm_entry_t * entry_current, * entry_next;
163 
164   if (!perm) return;
165   do {
166     perm_next = perm->next_perm;
167     entry_current = perm->entry_list;
168     while (entry_current) {
169       entry_next = entry_current->next_entry;
170       free(entry_current);
171       entry_current = entry_next;
172     }
173     free(perm);
174     perm = perm_next;
175   } while (perm);
176 }
177 
178 
179 /** \brief Convert permission structure to printable string
180  * \note: result string will start with a space
181  * \param[in] perm A wzd_command_perm_t strcture
182  * \param[out] perm_buffer Output buffer
183  * \param[out] max_length Maximum number of bytes that can be written to output buffer
184  * \return 0 if ok
185  */
perm2str(wzd_command_perm_t * perm,char * perm_buffer,unsigned int max_length)186 int perm2str(wzd_command_perm_t * perm, char * perm_buffer, unsigned int max_length)
187 {
188   char *perm_buffer_ptr;
189   unsigned int length;
190   wzd_command_perm_entry_t * entry;
191 
192   if (!perm) return 1;
193 
194   /* parse current->entry_list */
195   perm_buffer_ptr = perm_buffer;
196   length=0;
197   entry = perm->entry_list;
198   while (entry) {
199     *perm_buffer_ptr++ = ' ';
200     length ++;
201     if (strcmp(entry->target,"*")!=0) {
202       switch(entry->cp) {
203         case CPERM_USER: *perm_buffer_ptr++ = '='; break;
204         case CPERM_GROUP: *perm_buffer_ptr++ = '-'; break;
205         case CPERM_FLAG: *perm_buffer_ptr++ = '+'; break;
206       }
207       length ++;
208     }
209     length += strlen(entry->target);
210     if (length >= max_length) return 1;
211     strncpy(perm_buffer_ptr,entry->target,max_length-length);
212 /*    perm_buffer_ptr = perm_buffer+length;*/
213     perm_buffer_ptr += strlen(entry->target);
214     entry = entry->next_entry;
215   }
216   perm_buffer[length]='\0';
217   return 0;
218 }
219 
220 /** \brief Find permission structure (create it if needed) for a command and return the structure
221  * \param[in] commandname the command name
222  * \param[in,out] perm_list permission list
223  * \return
224  *  - the permission structure if found
225  *  - a new structure if none was found
226  *  - NULL on error
227  */
perm_find_create(const char * commandname,wzd_command_perm_t ** perm_list)228 wzd_command_perm_t * perm_find_create(const char *commandname, wzd_command_perm_t ** perm_list)
229 {
230   wzd_command_perm_t * perm, * insert_point;
231 
232   if ( ! *perm_list ) {
233     perm = *perm_list = perm_create_empty_perm();
234     strncpy(perm->command_name,commandname,256);
235     return perm;
236   }
237 
238   perm = *perm_list;
239   do {
240     /* we use strcmp because commandname is lowered in wzd_init_lex.l (readConfigFile, case '-') */
241     if (strcmp(perm->command_name,commandname)==0) {
242       return perm;
243     }
244     perm = perm->next_perm;
245   } while (perm);
246 
247   /* not found, insert a new perm (tail insertion) */
248   perm = perm_create_empty_perm();
249   strncpy(perm->command_name,commandname,256);
250   insert_point = *perm_list;
251   if (insert_point) {
252     while (insert_point->next_perm) insert_point = insert_point->next_perm;
253     insert_point->next_perm = perm;
254   } else {
255     *perm_list = perm;
256   }
257 
258   return perm;
259 }
260 
261 /** \brief Find permission structure for a command and return the structure
262  * \param[in] commandname the command name
263  * \param[in] perm_list permission list
264  * \return
265  *  - the permission structure if found
266  *  - NULL if not found
267  */
perm_find(const char * commandname,wzd_command_perm_t * perm_list)268 wzd_command_perm_t * perm_find(const char *commandname, wzd_command_perm_t * perm_list)
269 {
270   wzd_command_perm_t * perm;
271 
272   if ( ! perm_list ) return NULL;
273 
274   perm = perm_list;
275   do {
276     if (strcasecmp(perm->command_name,commandname)==0) {
277       return perm;
278     }
279     perm = perm->next_perm;
280   } while (perm);
281 
282   return NULL;
283 }
284 
285 /** \brief Find permission entry structure (create it if needed) applying for a command and a target, and return the structure
286  * \param[in] target the target name (user, group or flag)
287  * \param[in] cp the command type
288  * \param[in,out] command_perm the permission to check
289  * \return
290  *  - the permission entry if found
291  *  - a new entry if none was found
292  *  - NULL on error
293  */
perm_find_create_entry(const char * target,wzd_cp_t cp,wzd_command_perm_t * command_perm)294 wzd_command_perm_entry_t * perm_find_create_entry(const char * target, wzd_cp_t cp, wzd_command_perm_t * command_perm)
295 {
296   wzd_command_perm_entry_t * entry, *insert_point;
297 
298   entry = command_perm->entry_list;
299   if (!entry) {
300     entry = command_perm->entry_list = perm_create_empty_entry();
301     strncpy(entry->target,target,256);
302     entry->cp = cp;
303     return entry;
304   }
305 
306   /** \todo TODO compare entries with target (regexp powaa) and if same, simplify or warn */
307 
308   do {
309     if (strcasecmp(entry->target,target)==0 && entry->cp == cp) {
310       return entry;
311     }
312     entry = entry->next_entry;
313   } while (entry);
314 
315   /* not found, insert a new entry (tail insertion, order is important) */
316   entry = perm_create_empty_entry();
317   strncpy(entry->target,target,256);
318   entry->cp = cp;
319   entry->next_entry = NULL;
320   insert_point = command_perm->entry_list;
321   if (insert_point == NULL) {
322     command_perm->entry_list = entry;
323   } else {
324     while (insert_point->next_entry != NULL)
325       insert_point = insert_point->next_entry;
326 
327     insert_point->next_entry = entry;
328   }
329 
330   return entry;
331 }
332 
333 /** \brief Find permission entry applying for a command and a target, and return the structure
334  * \param[in] target the target name (user, group or flag)
335  * \param[in] cp the command type
336  * \param[in] command_perm the permission to check
337  * \return
338  *  - the permission entry if found
339  *  - NULL if not found
340  */
perm_find_entry(const char * target,wzd_cp_t cp,wzd_command_perm_t * command_perm)341 wzd_command_perm_entry_t * perm_find_entry(const char * target, wzd_cp_t cp, wzd_command_perm_t * command_perm)
342 {
343   wzd_command_perm_entry_t * entry;
344   int negate;
345   const char * entry_target;
346 
347   entry = command_perm->entry_list;
348   if (!entry) return NULL;
349 
350   /** \todo TODO compare entries with target (regexp powaa) and if same, ok */
351 
352   do {
353     entry_target = entry->target;
354     negate=0;
355     if (entry_target[0] == '!') {
356       entry_target++;
357       negate = 1;
358     }
359     if (entry_target[0] == '*') return (negate) ? (void*)-1 : entry;
360     if (strcasecmp(entry_target,target)==0 && entry->cp == cp) {
361       return (negate) ? (void*)-1 : entry;
362     }
363     entry = entry->next_entry;
364   } while (entry);
365 
366   return NULL;
367 }
368 
369 /** \brief Create a new permission, parse entries, and add it to permission list
370  * \param[in] permname command name
371  * \param[in] permline text describing permissions
372  * \param[out] perm_list permission list
373  * \return 0 if ok
374  */
perm_add_perm(const char * permname,const char * permline,wzd_command_perm_t ** perm_list)375 int perm_add_perm(const char *permname, const char *permline, wzd_command_perm_t ** perm_list)
376 {
377   char * dyn_buffer;
378   char * token, * ptr;
379   wzd_command_perm_t * command_perm;
380   wzd_command_perm_entry_t * perm_entry;
381   wzd_cp_t cp;
382   char c;
383   int negate;
384   size_t length;
385 
386   if (!permname || !permline) return 1;
387   if (!strlen(permname) || !strlen(permline)) return 1;
388 
389   if ( (length = strlen(permline)) >= BUFFER_LEN) return 1;
390   dyn_buffer = malloc(length+1);
391   strncpy(dyn_buffer,permline,length+1);
392 
393   /* find the perm */
394   command_perm = perm_find_create(permname,perm_list);
395 
396   /* for each element of the permline, add it to the entries */
397   ptr = dyn_buffer;
398   token = strtok_r(dyn_buffer," \t\r\n",&ptr);
399 
400   while (token) {
401     negate=0;
402     /* FIXME split token to find entry type : user, group, flag */
403     WZD_ASSERT ( !(token < dyn_buffer) )
404     WZD_ASSERT (!(token > (dyn_buffer+length)) )
405     c = *token++;
406     if (c == '!') {
407       negate = 1;
408       c = *token++;
409     }
410     switch (c) {
411     case '=':
412       cp = CPERM_USER;
413       break;
414     case '-':
415       cp = CPERM_GROUP;
416       break;
417     case '+':
418       cp = CPERM_FLAG;
419       break;
420     case '*':
421       cp = CPERM_USER;
422       token--;
423       break;
424     default:
425       /* incorrect format */
426 #ifdef DEBUG
427 out_err(LEVEL_HIGH,"Incorrect permission format: %s: %s\n",permname,token);
428 #endif
429       continue;
430     }
431     if (negate)
432       *(--token)='!';
433     if (token < dyn_buffer) out_err(LEVEL_HIGH,"token < dyn_buffer !! %s:%d\n",__FILE__,__LINE__);
434     /* add entry */
435     perm_entry = perm_find_create_entry(token,cp,command_perm);
436 
437     token = strtok_r(NULL," \t\r\n",&ptr);
438   }
439   free(dyn_buffer);
440 
441   return 0;
442 }
443 
444 /** \brief Check if user is authorized to execute command
445  * \note the default choice is to \b deny execution if nothing specific was found
446  * \param[in] permname command name
447  * \param[in] context user context
448  * \param[in] perm_list permission list
449  * \return
450  *  - 0 if ok
451  *  - 1 if denied
452  *  - -1 on error
453  */
perm_check(const char * permname,const wzd_context_t * context,wzd_command_perm_t * perm_list)454 int perm_check(const char *permname, const wzd_context_t * context, wzd_command_perm_t * perm_list)
455 {
456   wzd_command_perm_t * command_perm;
457 
458   if (!permname || !context) return -1;
459   if (!perm_list) return -1;
460   if (!strlen(permname)) return -1;
461 
462   command_perm = perm_find(permname,perm_list);
463 
464   return perm_check_perm(command_perm,context);
465 }
466 
467 /** \brief Check if user is authorized to execute command
468  * \note the default choice is to \b deny execution if nothing specific was found
469  * \param[in] perm permission structure
470  * \param[in] context user context
471  * \return
472  *  - 0 if ok
473  *  - 1 if denied
474  *  - -1 on error
475  */
perm_check_perm(const wzd_command_perm_t * perm,const wzd_context_t * context)476 int perm_check_perm(const wzd_command_perm_t *perm, const wzd_context_t * context)
477 {
478   wzd_command_perm_entry_t * entry;
479   const wzd_user_t * user;
480   wzd_group_t * group;
481   unsigned int i;
482   int negate;
483   const char * entry_target;
484 
485   user = GetUserByID(context->userid);
486 
487   if (!perm || !context) return -1;
488 
489   entry = perm->entry_list;
490   if (!entry) return 1;
491 
492   /** \todo TODO compare entries with target (regexp powaa) and if same, ok */
493 
494   do {
495     entry_target = entry->target;
496     negate=0;
497     if (entry_target[0] == '!') {
498       entry_target++;
499       negate = 1;
500     }
501     if (entry_target[0] == '*') return (negate) ? 1 : 0;
502     switch (entry->cp) {
503       case CPERM_USER:
504         if (strcasecmp(entry_target,user->username)==0) return (negate) ? 1 : 0;
505         break;
506       case CPERM_GROUP:
507         for (i=0; i<user->group_num; i++) {
508           group = GetGroupByID(user->groups[i]);
509           if (strcasecmp(entry_target,group->groupname)==0) return (negate) ? 1 : 0;
510         }
511         break;
512       case CPERM_FLAG:
513         if (user->flags && strchr(user->flags,*entry_target)) return (negate) ? 1 : 0;
514         break;
515     }
516     entry = entry->next_entry;
517   } while (entry);
518 
519   return 1;
520 }
521 
522