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