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