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