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 
35 #include <libwzd-base/hash.h>
36 
37 #include "wzd_structs.h"
38 #include "wzd_log.h"
39 #include "wzd_misc.h"
40 #include "wzd_perm.h"
41 #include "wzd_site.h"
42 #include "wzd_site_group.h"
43 #include "wzd_site_user.h"
44 #include "wzd_ClientThread.h"
45 
46 #include "wzd_debug.h"
47 
48 #endif /* WZD_USE_PCH */
49 
_command_free(wzd_command_t * command)50 static void _command_free(wzd_command_t *command)
51 {
52   if (!command) return;
53   free(command->name);
54   str_deallocate(command->external_command);
55   perm_free_recursive(command->perms);
56   free(command);
57 }
58 
59 /** \brief Initialize storage for server commands
60  *
61  * \param[out] _ctable Pointer to the allocated hash table
62  *
63  * \return 0 if ok
64  */
commands_init(CHTBL ** _ctable)65 int commands_init(CHTBL ** _ctable)
66 {
67   if (*_ctable) {
68     commands_fini(*_ctable);
69   }
70 
71   *_ctable = malloc(sizeof(CHTBL));
72 
73   if (chtbl_init(*_ctable, 128, (hash_function)hash_str, (cmp_function)strcmp, (void (*)(void*))_command_free)) {
74     free(*_ctable);
75     *_ctable = NULL;
76     return -1;
77   }
78 
79   return 0;
80 }
81 
82 /** \brief Destroy stored commands, and free memory used for commands
83  *
84  * \param[in] _ctable Hash table containing commands
85  */
commands_fini(CHTBL * _ctable)86 void commands_fini(CHTBL * _ctable)
87 {
88   if (!_ctable) return;
89 
90   chtbl_destroy(_ctable);
91   free(_ctable);
92   _ctable = NULL;
93 }
94 
95 /** \brief Add a new FTP command, linked to a C function
96  *
97  * \param[in] _ctable Hash table containing commands
98  * \param[in] name The FTP command (for ex, XCRC). For a site command, append command
99  * name with a space: SITE_HELP
100  * \param[in] command The function which will be executed when receiving the FTP command
101  * \param[in] help A pointer to a help function (not used at the moment)
102  * \param[in] id A unique identifier (32 bits unsigned integer) for the command (see \ref wzd_token_t)
103  *
104  * \note Command names are case insensitive, and must be valid ASCII
105  */
commands_add(CHTBL * _ctable,const char * name,wzd_function_command_t command,wzd_function_command_t help,u32_t id)106 int commands_add(CHTBL * _ctable,
107     const char *name,
108     wzd_function_command_t command,
109     wzd_function_command_t help,
110     u32_t id)
111 {
112   wzd_command_t * com;
113 
114   if (!_ctable) return -1;
115   if (!name || !command || !id) return -1;
116 
117   if (chtbl_lookup(_ctable, name, (void**)&com))
118   {
119     /* new entry */
120     com = malloc(sizeof(wzd_command_t));
121     com->name = strdup(name);
122     ascii_lower(com->name,strlen(com->name));
123     com->id = id;
124     com->command = command;
125     com->help_function = help;
126     com->external_command = NULL;
127 
128     com->perms = NULL;
129 
130     if ((chtbl_insert(_ctable, com->name, com, NULL, NULL, (void(*)(void*))_command_free))==0)
131     {
132       return 0;
133     }
134 
135     free(com->name);
136     free(com);
137     return -1;
138   }
139 
140   return 0;
141 }
142 
143 /** \brief Add a new FTP command, linked to an external program (for ex, a perl module)
144  *
145  * \param[in] _ctable Hash table containing commands
146  * \param[in] name The FTP command (for ex, XCRC). For a site command, append command
147  * name with a space: SITE_HELP
148  * \param[in] external_command The application which will be executed when receiving the FTP command.
149  * The application can use protocols (see \ref hook_add_protocol)
150  *
151  * \note Command names are case insensitive, and must be valid ASCII
152  */
commands_add_external(CHTBL * _ctable,const char * name,const wzd_string_t * external_command)153 int commands_add_external(CHTBL * _ctable,
154     const char *name,
155     const wzd_string_t *external_command)
156 {
157   wzd_command_t * com;
158 
159   if (!_ctable) return -1;
160   if (!name || !external_command) return -1;
161 
162   /** \todo this should be done in an atomic way */
163   if (chtbl_lookup(_ctable, name, (void**)&com) == 0) { /* already found, replace command */
164     if ((chtbl_remove(_ctable, com->name)!=0))
165     {
166       out_err(LEVEL_CRITICAL,"ERROR Could not remove a previous command for %s\n",name);
167       return -1;
168     }
169   }
170 
171   /* new entry */
172   com = malloc(sizeof(wzd_command_t));
173   com->name = strdup(name);
174   ascii_lower(com->name,strlen(com->name));
175   com->id = TOK_CUSTOM;
176   com->external_command = str_dup(external_command);
177 
178   com->command = NULL;
179   com->help_function = NULL;
180 
181   com->perms = NULL;
182 
183   if ((chtbl_insert(_ctable, com->name, com, NULL, NULL, (void(*)(void*))_command_free))==0)
184     return 0;
185 
186   str_deallocate(com->external_command);
187   free(com->name);
188   free(com);
189   return -1;
190 }
191 
192 /** \brief Add default FTP commands to hash table
193  *
194  * \param[in] _ctable Hash table containing commands
195  *
196  * \return 0 if ok
197  */
commands_add_defaults(CHTBL * _ctable)198 int commands_add_defaults(CHTBL * _ctable)
199 {
200   if (!_ctable) return -1;
201 
202   if (commands_add(_ctable,"site",do_site,NULL,TOK_SITE)) return -1;
203 
204   if (commands_add(_ctable,"type",do_type,NULL,TOK_TYPE)) return -1;
205   if (commands_add(_ctable,"port",do_port,NULL,TOK_PORT)) return -1;
206   if (commands_add(_ctable,"pasv",do_pasv,NULL,TOK_PASV)) return -1;
207   if (commands_add(_ctable,"eprt",do_eprt,NULL,TOK_EPRT)) return -1;
208   if (commands_add(_ctable,"epsv",do_epsv,NULL,TOK_EPSV)) return -1;
209   if (commands_add(_ctable,"abor",do_abor,NULL,TOK_ABOR)) return -1;
210   if (commands_add(_ctable,"pwd",do_print_message,NULL,TOK_PWD)) return -1;
211   if (commands_add(_ctable,"allo",do_print_message,NULL,TOK_ALLO)) return -1;
212   if (commands_add(_ctable,"feat",do_print_message,NULL,TOK_FEAT)) return -1;
213   if (commands_add(_ctable,"noop",do_print_message,NULL,TOK_NOOP)) return -1;
214   if (commands_add(_ctable,"syst",do_print_message,NULL,TOK_SYST)) return -1;
215   if (commands_add(_ctable,"rnfr",do_rnfr,NULL,TOK_RNFR)) return -1;
216   if (commands_add(_ctable,"rnto",do_rnto,NULL,TOK_RNTO)) return -1;
217   if (commands_add(_ctable,"cdup",do_cwd,NULL,TOK_CDUP)) return -1;
218   if (commands_add(_ctable,"cwd",do_cwd,NULL,TOK_CWD)) return -1;
219   if (commands_add(_ctable,"list",do_list,NULL,TOK_LIST)) return -1;
220   if (commands_add(_ctable,"nlst",do_list,NULL,TOK_NLST)) return -1;
221   if (commands_add(_ctable,"mlst",do_mlst,NULL,TOK_MLST)) return -1;
222   if (commands_add(_ctable,"mlsd",do_mlsd,NULL,TOK_MLSD)) return -1;
223   if (commands_add(_ctable,"stat",do_stat,NULL,TOK_STAT)) return -1;
224   if (commands_add(_ctable,"mkd",do_mkdir,NULL,TOK_MKD)) return -1;
225   if (commands_add(_ctable,"rmd",do_rmdir,NULL,TOK_RMD)) return -1;
226   if (commands_add(_ctable,"retr",do_retr,NULL,TOK_RETR)) return -1;
227   if (commands_add(_ctable,"stor",do_stor,NULL,TOK_STOR)) return -1;
228   if (commands_add(_ctable,"appe",do_stor,NULL,TOK_APPE)) return -1;
229   if (commands_add(_ctable,"rest",do_rest,NULL,TOK_REST)) return -1;
230   if (commands_add(_ctable,"mdtm",do_mdtm,NULL,TOK_MDTM)) return -1;
231   if (commands_add(_ctable,"size",do_size,NULL,TOK_SIZE)) return -1;
232   if (commands_add(_ctable,"dele",do_dele,NULL,TOK_DELE)) return -1;
233   if (commands_add(_ctable,"delete",do_dele,NULL,TOK_DELE)) return -1;
234   if (commands_add(_ctable,"pret",do_pret,NULL,TOK_PRET)) return -1;
235   if (commands_add(_ctable,"xcrc",do_xcrc,NULL,TOK_XCRC)) return -1;
236   if (commands_add(_ctable,"xmd5",do_xmd5,NULL,TOK_XMD5)) return -1;
237   if (commands_add(_ctable,"opts",do_opts,NULL,TOK_OPTS)) return -1;
238   if (commands_add(_ctable,"help",do_help,NULL,TOK_HELP)) return -1;
239   if (commands_add(_ctable,"quit",do_quit,NULL,TOK_QUIT)) return -1;
240 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
241   if (commands_add(_ctable,"pbsz",do_pbsz,NULL,TOK_PBSZ)) return -1;
242   if (commands_add(_ctable,"prot",do_prot,NULL,TOK_PROT)) return -1;
243   if (commands_add(_ctable,"cpsv",do_pasv,NULL,TOK_CPSV)) return -1;
244   if (commands_add(_ctable,"sscn",do_sscn,NULL,TOK_SSCN)) return -1;
245 #endif
246   if (commands_add(_ctable,"moda",do_moda,NULL,TOK_MODA)) return -1;
247 
248 #if defined(HAVE_KRB5)
249   if (commands_add(_ctable,"mic",do_mic,NULL,TOK_MIC)) return -1;
250 #endif
251 
252   if (commands_add(_ctable,"site_addip",do_site_addip,do_site_help_addip,TOK_SITE_ADDIP)) return -1;
253   if (commands_add(_ctable,"site_adduser",do_site_adduser,do_site_help_adduser,TOK_SITE_ADDUSER)) return -1;
254   if (commands_add(_ctable,"site_backend",do_site_backend,NULL,TOK_SITE_BACKEND)) return -1;
255   if (commands_add(_ctable,"site_chacl",do_site_chacl,NULL,TOK_SITE_CHACL)) return -1;
256   if (commands_add(_ctable,"site_change",do_site_change,do_site_help_change,TOK_SITE_CHANGE)) return -1;
257   if (commands_add(_ctable,"site_changegrp",do_site_changegrp,do_site_help_changegrp,TOK_SITE_CHANGEGRP)) return -1;
258   if (commands_add(_ctable,"site_checkperm",do_site_checkperm,NULL,TOK_SITE_CHECKPERM)) return -1;
259   if (commands_add(_ctable,"site_chgrp",do_site_chgrp,NULL,TOK_SITE_CHGRP)) return -1;
260   if (commands_add(_ctable,"site_chmod",do_site_chmod,NULL,TOK_SITE_CHMOD)) return -1;
261   if (commands_add(_ctable,"site_chown",do_site_chown,NULL,TOK_SITE_CHOWN)) return -1;
262   if (commands_add(_ctable,"site_chpass",do_site_chpass,NULL,TOK_SITE_CHPASS)) return -1;
263   if (commands_add(_ctable,"site_chratio",do_site_chratio,do_site_help_chratio,TOK_SITE_CHRATIO)) return -1;
264   if (commands_add(_ctable,"site_close",do_site,NULL,TOK_SITE_CLOSE)) return -1;
265   if (commands_add(_ctable,"site_color",do_site_color,NULL,TOK_SITE_COLOR)) return -1;
266   if (commands_add(_ctable,"site_delip",do_site_delip,do_site_help_delip,TOK_SITE_DELIP)) return -1;
267   if (commands_add(_ctable,"site_deluser",do_site_deluser,do_site_help_deluser,TOK_SITE_DELUSER)) return -1;
268   if (commands_add(_ctable,"site_flags",do_site_flags,NULL,TOK_SITE_FLAGS)) return -1;
269   if (commands_add(_ctable,"site_free",do_site_free,NULL,TOK_SITE_FREE)) return -1;
270   if (commands_add(_ctable,"site_ginfo",do_site_ginfo,NULL,TOK_SITE_GINFO)) return -1;
271   if (commands_add(_ctable,"site_give",do_site_give,do_site_help_give,TOK_SITE_GIVE)) return -1;
272   if (commands_add(_ctable,"site_group",do_site_group,do_site_help_group,TOK_SITE_GROUP)) return -1;
273   if (commands_add(_ctable,"site_grpadd",do_site_grpadd,do_site_help_grpadd,TOK_SITE_GRPADD)) return -1;
274   if (commands_add(_ctable,"site_grpaddip",do_site_grpaddip,do_site_help_grpaddip,TOK_SITE_GRPADDIP)) return -1;
275   if (commands_add(_ctable,"site_grpchange",do_site_grpchange,do_site_help_grpchange,TOK_SITE_GRPCHANGE)) return -1;
276   if (commands_add(_ctable,"site_grpdel",do_site_grpdel,do_site_help_grpdel,TOK_SITE_GRPDEL)) return -1;
277   if (commands_add(_ctable,"site_grpdelip",do_site_grpdelip,do_site_help_grpdelip,TOK_SITE_GRPDELIP)) return -1;
278   if (commands_add(_ctable,"site_grpkill",do_site_grpkill,NULL,TOK_SITE_GRPKILL)) return -1;
279   if (commands_add(_ctable,"site_grpratio",do_site_grpratio,do_site_help_grpratio,TOK_SITE_GRPRATIO)) return -1;
280   if (commands_add(_ctable,"site_grpren",do_site_grpren,do_site_help_grpren,TOK_SITE_GRPREN)) return -1;
281   if (commands_add(_ctable,"site_gsinfo",do_site_gsinfo,NULL,TOK_SITE_GSINFO)) return -1;
282   if (commands_add(_ctable,"site_help",do_site_help_command,NULL,TOK_SITE_HELP)) return -1;
283   if (commands_add(_ctable,"site_idle",do_site_idle,NULL,TOK_SITE_IDLE)) return -1;
284   if (commands_add(_ctable,"site_invite",do_site_invite,NULL,TOK_SITE_INVITE)) return -1;
285   if (commands_add(_ctable,"site_kick",do_site_kick,NULL,TOK_SITE_KICK)) return -1;
286   if (commands_add(_ctable,"site_kill",do_site_kill,NULL,TOK_SITE_KILL)) return -1;
287   if (commands_add(_ctable,"site_killpath",do_site_killpath,NULL,TOK_SITE_KILLPATH)) return -1;
288   if (commands_add(_ctable,"site_link",do_site_link,NULL,TOK_SITE_LINK)) return -1;
289   if (commands_add(_ctable,"site_msg",do_site_msg,NULL,TOK_SITE_MSG)) return -1;
290   if (commands_add(_ctable,"site_perm",do_site_perm,NULL,TOK_SITE_PERM)) return -1;
291   if (commands_add(_ctable,"site_purge",do_site_purgeuser,NULL,TOK_SITE_PURGE)) return -1;
292   if (commands_add(_ctable,"site_readd",do_site_readduser,do_site_help_readduser,TOK_SITE_READD)) return -1;
293   if (commands_add(_ctable,"site_reload",do_site_reload,NULL,TOK_SITE_RELOAD)) return -1;
294   if (commands_add(_ctable,"site_reopen",do_site,NULL,TOK_SITE_REOPEN)) return -1;
295   if (commands_add(_ctable,"site_rusage",do_site_rusage,NULL,TOK_SITE_RUSAGE)) return -1;
296   if (commands_add(_ctable,"site_savecfg",do_site_savecfg,NULL,TOK_SITE_SAVECFG)) return -1;
297   if (commands_add(_ctable,"site_sections",do_site_sections,NULL,TOK_SITE_SECTIONS)) return -1;
298   if (commands_add(_ctable,"site_showlog",do_site_showlog,NULL,TOK_SITE_SHOWLOG)) return -1;
299   if (commands_add(_ctable,"site_shutdown",do_site,NULL,TOK_SITE_SHUTDOWN)) return -1;
300   if (commands_add(_ctable,"site_su",do_site_su,do_site_help_su,TOK_SITE_SU)) return -1;
301   if (commands_add(_ctable,"site_tagline",do_site_tagline,NULL,TOK_SITE_TAGLINE)) return -1;
302   if (commands_add(_ctable,"site_take",do_site_take,do_site_help_take,TOK_SITE_TAKE)) return -1;
303 #ifdef DEBUG
304   if (commands_add(_ctable,"site_test",do_site_test,NULL,TOK_SITE_TEST)) return -1;
305 #endif
306   if (commands_add(_ctable,"site_unlock",do_site_unlock,NULL,TOK_SITE_UNLOCK)) return -1;
307   if (commands_add(_ctable,"site_uptime",do_site,NULL,TOK_SITE_UPTIME)) return -1;
308   if (commands_add(_ctable,"site_user",do_site_user,NULL,TOK_SITE_USER)) return -1;
309   if (commands_add(_ctable,"site_utime",do_site_utime,NULL,TOK_SITE_UTIME)) return -1;
310   if (commands_add(_ctable,"site_vars",do_site_vars,NULL,TOK_SITE_VARS)) return -1;
311   if (commands_add(_ctable,"site_vars_group",do_site_vars_group,NULL,TOK_SITE_VARS_GROUP)) return -1;
312   if (commands_add(_ctable,"site_vars_user",do_site_vars_user,NULL,TOK_SITE_VARS_USER)) return -1;
313   if (commands_add(_ctable,"site_version",do_site_version,NULL,TOK_SITE_VERSION)) return -1;
314   if (commands_add(_ctable,"site_vfsadd",do_site_vfsadd,NULL,TOK_SITE_VFSADD)) return -1;
315   if (commands_add(_ctable,"site_vfsdel",do_site_vfsdel,NULL,TOK_SITE_VFSDEL)) return -1;
316   if (commands_add(_ctable,"site_who",do_site,NULL,TOK_SITE_WHO)) return -1;
317   if (commands_add(_ctable,"site_wipe",do_site_wipe,NULL,TOK_SITE_WIPE)) return -1;
318 
319   return 0;
320 }
321 
322 /** \brief Search for command in registered commands
323  *
324  * \param[in] _ctable Hash table containing commands
325  * \param[in] str Command name to find
326  *
327  * \return
328  * - a wzd_command_t structure if the command has been found
329  * - NULL if not found
330  */
commands_find(CHTBL * _ctable,wzd_string_t * str)331 wzd_command_t * commands_find(CHTBL * _ctable, wzd_string_t *str)
332 {
333   wzd_command_t * command = NULL;
334 
335   if (!_ctable || !str) return NULL;
336 
337   str_tolower(str);
338 
339   chtbl_lookup(_ctable, str_tochar(str), (void**)&command);
340 
341   return command;
342 }
343 
344 /** \brief Set permissions associated to a command
345  *
346  * Replace permissions for the specified command.
347  * The command must exist.
348  *
349  * \param[in] _ctable Hash table containing commands
350  * \param[in] permname The permission name
351  * \param[in] permline A string describing permissions
352  * \return 0 if command is ok
353  */
commands_set_permission(CHTBL * _ctable,const char * permname,const char * permline)354 int commands_set_permission(CHTBL * _ctable, const char * permname, const char * permline)
355 {
356   wzd_command_t * command;
357   wzd_command_perm_t * old_perms;
358   wzd_string_t * str = STR(permname);
359 
360   command = commands_find(_ctable,str);
361   str_deallocate(str);
362   if (!command) return -1;
363 
364   old_perms = command->perms;
365   command->perms = NULL;
366 
367   if ( ! perm_add_perm(permname, permline, &command->perms) ) {
368     perm_free_recursive(old_perms);
369     return 0;
370   } else {
371     perm_free_recursive(command->perms);
372     command->perms = old_perms;
373     return 1;
374   }
375 }
376 
377 /** \brief Add permissions to a command
378  *
379  * Add permissions for the specified command.
380  * The command must exist.
381  *
382  * \param[in] _ctable Hash table containing commands
383  * \param[in] permname The permission name
384  * \param[in] permline A string describing permissions, to be appended
385  * \return 0 if command is ok
386  */
commands_add_permission(CHTBL * _ctable,const char * permname,const char * permline)387 int commands_add_permission(CHTBL * _ctable, const char * permname, const char * permline)
388 {
389   wzd_command_t * command;
390   wzd_string_t * str = STR(permname);
391 
392   command = commands_find(_ctable,str);
393   str_deallocate(str);
394   if (!command) return -1;
395 
396   return perm_add_perm(permname, permline, &command->perms);
397 }
398 
399 /** \brief Check if user is authorized to run specified command
400  *
401  * Check if the user in the specific context is allowed to run the command.
402  *
403  * \param[in] command The command name
404  * \param[in] context The client context
405  * \return 0 if ok
406  */
commands_check_permission(wzd_command_t * command,wzd_context_t * context)407 int commands_check_permission(wzd_command_t * command, wzd_context_t * context)
408 {
409   if (!command) return 0;
410 
411   return perm_check_perm(command->perms, context);
412 }
413 
414 /** \brief Delete permissions associated to a command
415  *
416  * Delete permissions associated to the command.
417  *
418  * \param[in] _ctable Hash table containing commands
419  * \param[in] str The command name
420  * \return 0 if ok
421  */
commands_delete_permission(CHTBL * _ctable,wzd_string_t * str)422 int commands_delete_permission(CHTBL * _ctable, wzd_string_t * str)
423 {
424   wzd_command_t * command;
425 
426   command = commands_find(_ctable,str);
427   if (!command) return 1;
428 
429   perm_free_recursive(command->perms);
430   command->perms = NULL;
431 
432   return 0;
433 }
434 
435