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 <errno.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37
38 #ifdef WIN32
39 #include <winsock2.h>
40 #else
41 #include <unistd.h>
42
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47
48 #include <dlfcn.h>
49 #endif
50
51 #if defined(BSD) && !(defined(__MACH__) && defined(__APPLE__))
52 #define DL_ARG DL_LAZY
53 #else
54 #define DL_ARG RTLD_NOW
55 #endif
56
57 #include "wzd_structs.h"
58
59 #include "wzd_cache.h"
60 #include "wzd_fs.h"
61 #include "wzd_log.h"
62 #include "wzd_misc.h"
63 #include "wzd_messages.h"
64 #include "wzd_mod.h"
65 #include "wzd_user.h"
66
67 #include "wzd_debug.h"
68
69 #ifdef NEED_UNDERSCORE
70 #define DL_PREFIX "_"
71 #else
72 #define DL_PREFIX
73 #endif
74
75 #endif /* WZD_USE_PCH */
76
77 struct event_entry_t {
78 unsigned long mask;
79 const char *name;
80 };
81
82 struct event_entry_t event_tab[] = {
83 { EVENT_LOGIN, "LOGIN" },
84 { EVENT_LOGOUT, "LOGOUT" },
85 { EVENT_PREUPLOAD, "PREUPLOAD" },
86 { EVENT_POSTUPLOAD, "POSTUPLOAD" },
87 { EVENT_PREDOWNLOAD, "PREDOWNLOAD" },
88 { EVENT_POSTDOWNLOAD, "POSTDOWNLOAD" },
89 { EVENT_PREMKDIR, "PREMKDIR" },
90 { EVENT_MKDIR, "MKDIR" },
91 { EVENT_RMDIR, "RMDIR" },
92 { EVENT_DELE, "DELE" },
93 { EVENT_SITE, "SITE" },
94 { EVENT_WIPE, "WIPE" },
95 { EVENT_PREWIPE, "PREWIPE" },
96 { 0, NULL },
97 };
98
99 extern void _cleanup_shell_command(char * buffer, size_t length);
100
101 static protocol_handler_t * proto_handler_list=NULL;
102 static unsigned int _reply_code;
103
hook_add_protocol(const char * signature,unsigned int sig_len,fcn_handler handler)104 int hook_add_protocol(const char *signature, unsigned int sig_len, fcn_handler handler)
105 {
106 protocol_handler_t * proto;
107
108 if (!signature || !handler || sig_len==0) return -1;
109
110 proto = wzd_malloc (sizeof(protocol_handler_t));
111 proto->sig = wzd_malloc(sig_len+1);
112 memcpy(proto->sig,signature,sig_len);
113 proto->sig[sig_len] = '\0';
114 proto->siglen = sig_len;
115 proto->handler = handler;
116 proto->next_proto = proto_handler_list;
117
118 proto_handler_list = proto;
119
120 return 0;
121 }
122
hook_free_protocols(void)123 void hook_free_protocols(void)
124 {
125 protocol_handler_t * proto, * next_proto;
126 proto = proto_handler_list;
127
128 while (proto)
129 {
130 next_proto = proto->next_proto;
131 if (proto->sig) wzd_free(proto->sig);
132 wzd_free(proto);
133 proto = next_proto;
134 }
135 proto_handler_list = NULL;
136 }
137
hook_check_protocol(const char * str)138 protocol_handler_t * hook_check_protocol(const char *str)
139 {
140 protocol_handler_t * proto;
141
142 proto = proto_handler_list;
143 while (proto)
144 {
145 if (strncmp(str,proto->sig,proto->siglen)==0)
146 return proto;
147 proto = proto->next_proto;
148 }
149
150 return NULL;
151 }
152
153
154
155 /** free hook list */
hook_free(wzd_hook_t ** hook_list)156 void hook_free(wzd_hook_t **hook_list)
157 {
158 wzd_hook_t * current_hook, * next_hook;
159
160 current_hook = *hook_list;
161
162 while (current_hook) {
163 next_hook = current_hook->next_hook;
164
165 if (current_hook->external_command)
166 free(current_hook->external_command);
167 if (current_hook->opt) free(current_hook->opt);
168 #ifdef DEBUG
169 current_hook->mask = 0;
170 current_hook->hook = NULL;
171 current_hook->external_command = NULL;
172 current_hook->opt=NULL;
173 current_hook->next_hook = NULL;
174 #endif /* DEBUG */
175 free(current_hook);
176
177 current_hook = next_hook;
178 }
179
180 *hook_list = NULL;
181 }
182
183 /** register a new hook */
hook_add(wzd_hook_t ** hook_list,unsigned long mask,void_fct hook)184 int hook_add(wzd_hook_t ** hook_list, unsigned long mask, void_fct hook)
185 {
186 wzd_hook_t * current_hook, * new_hook;
187
188 new_hook = malloc(sizeof(wzd_hook_t));
189 if (!new_hook) return 1;
190
191 new_hook->mask = mask;
192 new_hook->hook = hook;
193 new_hook->opt = NULL;
194 new_hook->external_command = NULL;
195 new_hook->next_hook = NULL;
196
197 current_hook = *hook_list;
198
199 if (!current_hook) {
200 *hook_list = new_hook;
201 return 0;
202 }
203
204 while (current_hook->next_hook) {
205 current_hook = current_hook->next_hook;
206 }
207
208 current_hook->next_hook = new_hook;
209
210 return 0;
211 }
212
hook_add_external(wzd_hook_t ** hook_list,unsigned long mask,const char * command)213 int hook_add_external(wzd_hook_t ** hook_list, unsigned long mask, const char *command)
214 {
215 wzd_hook_t * current_hook, * new_hook;
216
217 new_hook = malloc(sizeof(wzd_hook_t));
218 if (!new_hook) return 1;
219
220 new_hook->mask = mask;
221 new_hook->hook = NULL;
222 new_hook->opt = NULL;
223 new_hook->external_command = strdup(command);
224 new_hook->next_hook = NULL;
225
226 current_hook = *hook_list;
227
228 if (!current_hook) {
229 *hook_list = new_hook;
230 return 0;
231 }
232
233 while (current_hook->next_hook) {
234 current_hook = current_hook->next_hook;
235 }
236
237 current_hook->next_hook = new_hook;
238
239 return 0;
240 }
241
242 /** remove hook from list */
hook_remove(wzd_hook_t ** hook_list,unsigned long mask,void_fct hook)243 int hook_remove(wzd_hook_t **hook_list, unsigned long mask, void_fct hook)
244 {
245 wzd_hook_t * current_hook, * previous_hook=NULL;
246
247 if (!hook_list || !hook) return 1;
248
249 current_hook = *hook_list;
250
251 while (current_hook)
252 {
253 if (current_hook->mask == mask && current_hook->hook == hook)
254 {
255 if (previous_hook)
256 previous_hook->next_hook = current_hook->next_hook;
257 else
258 *hook_list = current_hook->next_hook;
259
260 if (current_hook->external_command)
261 free(current_hook->external_command);
262 if (current_hook->opt) free(current_hook->opt);
263 #ifdef DEBUG
264 current_hook->mask = 0;
265 current_hook->hook = NULL;
266 current_hook->external_command = NULL;
267 current_hook->opt=NULL;
268 current_hook->next_hook = NULL;
269 #endif /* DEBUG */
270 free(current_hook);
271
272 return 0;
273 }
274 previous_hook = current_hook;
275 current_hook = current_hook->next_hook;
276 }
277
278 return 1; /* not found */
279 }
280
281 /** hook_call_external: events */
hook_call_external(wzd_hook_t * hook,unsigned int code)282 int hook_call_external(wzd_hook_t *hook, unsigned int code)
283 {
284 char buffer[1024];
285 char *buffer_args;
286 FILE *command_output;
287 size_t l_command;
288 protocol_handler_t * proto;
289
290 if (!hook || !hook->external_command) return 1;
291 l_command = strlen(hook->external_command);
292 if (l_command>=1022) return 1;
293 /* replace cookies in args */
294 {
295 wzd_context_t * context = GetMyContext();
296 wzd_user_t * user = context ? GetUserByID(context->userid) : NULL;
297 wzd_group_t * group = context ? GetGroupByID(user->groups[0]) : NULL;
298
299 cookie_parse_buffer(hook->external_command,user,group,context,buffer,sizeof(buffer));
300 }
301 l_command = strlen(buffer);
302 while (l_command>0 && (buffer[l_command-1]=='\n' || buffer[l_command-1]=='\r'))
303 buffer[--l_command] = '\0';
304 _reply_code = code;
305 /* we can use protocol hooks here */
306 proto = hook_check_protocol(buffer);
307 if (proto)
308 {
309 /* we need to reformat args */
310 if ( *(buffer+proto->siglen) == '"' ) { /* search matching " */
311 buffer_args = strchr(buffer+proto->siglen+1,'"');
312 *buffer_args++ = '\0'; /* eat trailing " */
313 if (*buffer_args==' ') *buffer_args++ = '\0';
314 return (*proto->handler)(buffer+proto->siglen+1,buffer_args);
315 } else
316 buffer_args = strchr(buffer+proto->siglen,' ');
317 if (buffer_args) {
318 *buffer_args++ = '\0';
319 } else {
320 buffer_args = NULL;
321 }
322 return (*proto->handler)(buffer+proto->siglen,buffer_args);
323 }
324 else
325 {
326 /* *(buffer+l_command++) = ' ';*/
327 /* SECURITY filter buffer for shell special characters ! */
328 _cleanup_shell_command(buffer,sizeof(buffer));
329 if ( (command_output = popen(buffer,"r")) == NULL ) {
330 out_log(LEVEL_HIGH,"Hook '%s': unable to popen\n",hook->external_command);
331 out_log(LEVEL_INFO,"Failed command: '%s'\n",buffer);
332 return 1;
333 }
334 while (fgets(buffer,1023,command_output) != NULL)
335 {
336 out_log(LEVEL_INFO,"hook: %s\n",buffer);
337 }
338 return pclose(command_output);
339 }
340
341 return 0;
342 }
343
event2str(const unsigned long mask)344 char * event2str(const unsigned long mask)
345 {
346 int i=0;
347
348 while (event_tab[i].mask != 0)
349 {
350 if (event_tab[i].mask == mask) return (char*)event_tab[i].name;
351 i++;
352 }
353 return 0;
354 }
355
str2event(const char * s)356 unsigned long str2event(const char *s)
357 {
358 int i=0;
359
360 while (event_tab[i].mask != 0)
361 {
362 if (strcasecmp(s,event_tab[i].name)==0) return event_tab[i].mask;
363 i++;
364 }
365 return 0;
366 }
367
368
369
370 /** check a module file */
module_check(const char * filename)371 int module_check(const char *filename)
372 {
373 char path[1024];
374 void * handle;
375 void * ptr;
376 char * error;
377 fs_filestat_t st;
378 int ret;
379 int mode = DL_ARG;
380
381 if (!filename || filename[0]=='\0') return -1;
382 #ifndef _MSC_VER
383 if (filename[0] == '/')
384 #else
385 if (filename[0] == '/' || filename[1] == ':')
386 #endif
387 strncpy(path,filename,1023);
388 else
389 { /* relative path */
390 if (strlen(filename) >= 1022) return -1;
391 path[0] = '.';
392 path[1] = '/';
393 strcpy(path+2,filename);
394 }
395
396 ret = fs_file_lstat(path,&st);
397 if (ret) {
398 out_err(LEVEL_HIGH,"Could not stat module '%s'\n",filename);
399 out_err(LEVEL_HIGH,"errno: %d error: %s\n",errno, strerror(errno));
400 return -1;
401 }
402
403 #ifdef RTLD_GLOBAL
404 mode |= RTLD_GLOBAL;
405 #else
406 out_log(LEVEL_NORMAL,"WARNING Can't make loaded symbols global on this platform while loading %s\n",path);
407 #endif
408 /* test dlopen */
409 handle = dlopen(path,mode);
410 if (!handle) {
411 out_err(LEVEL_HIGH,"Could not dlopen module '%s'\n",filename);
412 out_err(LEVEL_HIGH,"errno: %d error: %s\n",errno, strerror(errno));
413 out_err(LEVEL_HIGH,"dlerror: %s\n",dlerror());
414 return 1;
415 }
416
417 /* check basic functions */
418 ptr = dlsym(handle,DL_PREFIX STR_MODULE_INIT);
419 #ifndef _MSC_VER
420 if ((error = dlerror()) != NULL)
421 #else
422 error = "";
423 if ( !ptr )
424 #endif
425 {
426 out_err(LEVEL_HIGH,"Unable to find function WZD_MODULE_INIT in module %s\n%s\n",filename,error);
427 dlclose(handle);
428 return 1;
429 }
430
431 /*
432 ptr = dlsym(handle,DL_PREFIX "hook_table");
433 if ((error = dlerror()) != NULL) {
434 out_log(LEVEL_HIGH,"Unable to find structure 'hook_table' in module %s\n%s\n",filename,error);
435 dlclose(handle);
436 return 1;
437 }
438
439 {
440 typedef void (*myfct)(void);
441 myfct f;
442 f = (myfct)dlsym(handle,DL_PREFIX "moduletest");
443 out_err(LEVEL_HIGH,"main prog mainConfig: %lx\n",(unsigned long)getlib_mainConfig()->logfile);
444 if (f)
445 f();
446 else
447 out_err(LEVEL_HIGH,"Could not find moduletest\n");
448 }
449 */
450
451 dlclose(handle);
452 return 0;
453 }
454
455 /** add a module to the list */
module_add(wzd_module_t ** module_list,const char * name)456 int module_add(wzd_module_t ** module_list, const char *name)
457 {
458 wzd_module_t * current_module, * new_module;
459
460 new_module = malloc(sizeof(wzd_module_t));
461 if (!new_module) return 1;
462
463 new_module->name = strdup(name);
464 new_module->handle = NULL;
465 new_module->next_module = NULL;
466
467 current_module = *module_list;
468
469 if (!current_module) {
470 *module_list = new_module;
471 return 0;
472 }
473
474 while (current_module->next_module) {
475 current_module = current_module->next_module;
476 }
477
478 current_module->next_module = new_module;
479
480 return 0;
481 }
482
483 /** load a module - module really should have been checked before ! */
module_load(wzd_module_t * module)484 int module_load(wzd_module_t *module)
485 {
486 char path[1024];
487 void * handle;
488 int ret;
489 char * filename;
490 fcn_module_init f_init;
491 int mode = DL_ARG;
492 #ifdef DEBUG
493 char * error;
494 #endif
495
496 filename = module->name;
497
498 #ifndef _MSC_VER
499 if (filename[0] == '/')
500 #else
501 if (filename[0] == '/' || filename[1] == ':')
502 #endif
503 strncpy(path,filename,1023);
504 else
505 { /* relative path */
506 if (strlen(filename) >= 1022) return -1;
507 path[0] = '.';
508 path[1] = '/';
509 strcpy(path+2,filename);
510 }
511
512 #ifdef RTLD_GLOBAL
513 mode |= RTLD_GLOBAL;
514 #else
515 out_log(LEVEL_NORMAL,"WARNING Can't make loaded symbols global on this platform while loading %s\n",path);
516 #endif
517 /* test dlopen */
518 handle = dlopen(path,mode);
519 if (!handle) return -1;
520
521 f_init = (fcn_module_init)dlsym(handle,DL_PREFIX STR_MODULE_INIT);
522 #ifdef DEBUG
523 #ifndef _MSC_VER
524 if ((error = dlerror()) != NULL)
525 #else
526 error = "";
527 if ( !f_init )
528 #endif
529 {
530 out_log(LEVEL_CRITICAL,"Unable to find function WZD_MODULE_INIT in module %s\n%s\n",filename,error);
531 out_log(LEVEL_CRITICAL,"THIS SHOULD HAVE BEEN CHECKED BEFORE !\n");
532 dlclose(handle);
533 return 1;
534 }
535 #endif
536
537 ret = (f_init)();
538 if (ret) {
539 out_log(LEVEL_HIGH,"ERROR could not load module %s\n",module->name);
540 dlclose(handle);
541 return ret;
542 }
543
544 module->handle = handle;
545
546 #ifdef WZD_DBG_MODULES
547 out_log(LEVEL_INFO,"MODULE: loaded '%s' at address %p\n",filename,handle);
548 #endif
549
550 return ret;
551 }
552
553 /** unload module, and remove it from list */
module_unload(wzd_module_t ** module_list,const char * name)554 int module_unload(wzd_module_t **module_list, const char *name)
555 {
556 wzd_module_t * current_module, * previous_module=NULL;
557 fcn_module_close f_close;
558
559 current_module = *module_list;
560 if (!current_module || !name) return 1; /* not found */
561
562 while (current_module) {
563
564 if (strcmp(current_module->name,name)==0)
565 {
566 #ifdef WZD_DBG_MODULES
567 out_log(LEVEL_INFO,"MODULE: unloading '%s' at address %p\n",current_module->name,current_module->handle);
568 #endif
569 f_close = (fcn_module_close)dlsym(current_module->handle,DL_PREFIX STR_MODULE_CLOSE);
570 if (f_close) (*f_close)();
571
572 /** \todo XXX FIXME
573 * dlclose() on a shared lib in a multithread application will likely cause a segfault
574 * when thread exits, because thread will try to free some specific thread-vars
575 * update: it seems behaviour improves with linux 2.6.5
576 */
577 /* dlclose(current_module->handle);*/
578
579 if (previous_module)
580 previous_module->next_module = current_module->next_module;
581 else
582 *module_list = current_module->next_module;
583
584 if (current_module->name)
585 free(current_module->name);
586 #ifdef DEBUG
587 current_module->handle = NULL;
588 current_module->name = NULL;
589 current_module->next_module = NULL;
590 #endif /* DEBUG */
591 free(current_module);
592 return 0;
593 }
594
595 previous_module = current_module;
596 current_module = current_module->next_module;
597 }
598 return 1; /* not found */
599 }
600
601 /** free module list */
module_free(wzd_module_t ** module_list)602 void module_free(wzd_module_t ** module_list)
603 {
604 wzd_module_t * current_module, * next_module;
605 fcn_module_close f_close;
606
607 current_module = *module_list;
608
609 while (current_module) {
610 next_module = current_module->next_module;
611
612 #ifdef WZD_DBG_MODULES
613 out_log(LEVEL_INFO,"MODULE: unloading '%s' at address %p\n",current_module->name,current_module->handle);
614 #endif
615 if (current_module->handle) {
616 f_close = (fcn_module_close)dlsym(current_module->handle,DL_PREFIX STR_MODULE_CLOSE);
617 if (f_close) (*f_close)();
618
619 dlclose(current_module->handle);
620 }
621
622 if (current_module->name)
623 free(current_module->name);
624 #ifdef DEBUG
625 current_module->handle = NULL;
626 current_module->name = NULL;
627 current_module->next_module = NULL;
628 #endif /* DEBUG */
629 free(current_module);
630
631 current_module = next_module;
632 }
633
634 *module_list = NULL;
635 }
636
637 /** \brief Get module name */
module_get_name(wzd_module_t * module)638 const char * module_get_name(wzd_module_t * module)
639 {
640 char ** module_name;
641
642 if (module->handle)
643 module_name = (char**)dlsym(module->handle,DL_PREFIX "module_name");
644 else
645 return NULL;
646
647 return (module_name) ? *module_name : NULL;
648 }
649
650 /** \brief Get module version */
module_get_version(wzd_module_t * module)651 const char * module_get_version(wzd_module_t * module)
652 {
653 char ** module_version;
654
655 if (module->handle)
656 module_version = (char**)dlsym(module->handle,DL_PREFIX "module_version");
657 else
658 return NULL;
659
660 return (module_version) ? *module_version : NULL;
661 }
662
hook_get_current_reply_code(void)663 unsigned int hook_get_current_reply_code(void)
664 {
665 return _reply_code;
666 }
667
668
669