1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 1997, 1998 Public Flood Software
4 * Copyright (c) 2001-2017 The ProFTPD Project team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
19 *
20 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
21 * and other respective copyright holders give permission to link this program
22 * with OpenSSL, and distribute the resulting executable, without including
23 * the source code for OpenSSL in the source distribution.
24 */
25
26 /* Module handling routines */
27
28 #include "conf.h"
29
30 extern module *static_modules[];
31 extern module *loaded_modules;
32
33 /* Currently running module */
34 module *curr_module = NULL;
35
36 /* Used to track the priority for loaded modules. */
37 static unsigned int curr_module_pri = 0;
38
39 static const char *trace_channel = "module";
40
pr_module_call(module * m,modret_t * (* func)(cmd_rec *),cmd_rec * cmd)41 modret_t *pr_module_call(module *m, modret_t *(*func)(cmd_rec *),
42 cmd_rec *cmd) {
43 modret_t *res;
44 module *prev_module = curr_module;
45
46 if (m == NULL ||
47 func == NULL ||
48 cmd == NULL) {
49 errno = EINVAL;
50 return NULL;
51 }
52
53 if (cmd->tmp_pool == NULL) {
54 cmd->tmp_pool = make_sub_pool(cmd->pool);
55 pr_pool_tag(cmd->tmp_pool, "Module call tmp_pool");
56 }
57
58 curr_module = m;
59 res = func(cmd);
60 curr_module = prev_module;
61
62 /* Note that we don't clear the pool here because the function may
63 * return data which resides in this pool.
64 */
65 return res;
66 }
67
mod_create_data(cmd_rec * cmd,void * d)68 modret_t *mod_create_data(cmd_rec *cmd, void *d) {
69 modret_t *res;
70
71 if (cmd == NULL) {
72 errno = EINVAL;
73 return NULL;
74 }
75
76 res = pcalloc(cmd->tmp_pool, sizeof(modret_t));
77 res->data = d;
78
79 return res;
80 }
81
mod_create_ret(cmd_rec * cmd,unsigned char err,const char * n,const char * m)82 modret_t *mod_create_ret(cmd_rec *cmd, unsigned char err, const char *n,
83 const char *m) {
84 modret_t *res;
85
86 if (cmd == NULL) {
87 errno = EINVAL;
88 return NULL;
89 }
90
91 res = pcalloc(cmd->tmp_pool, sizeof(modret_t));
92 res->mr_handler_module = curr_module;
93 res->mr_error = err;
94
95 if (n != NULL) {
96 res->mr_numeric = pstrdup(cmd->tmp_pool, n);
97 }
98
99 if (m != NULL) {
100 res->mr_message = pstrdup(cmd->tmp_pool, m);
101 }
102
103 return res;
104 }
105
mod_create_error(cmd_rec * cmd,int mr_errno)106 modret_t *mod_create_error(cmd_rec *cmd, int mr_errno) {
107 modret_t *res;
108
109 if (cmd == NULL) {
110 errno = EINVAL;
111 return NULL;
112 }
113
114 res = pcalloc(cmd->tmp_pool, sizeof(modret_t));
115 res->mr_handler_module = curr_module;
116 res->mr_error = mr_errno;
117
118 return res;
119 }
120
121 /* Called after forking in order to inform/initialize modules
122 * need to know we are a child and have a connection.
123 */
modules_session_init(void)124 int modules_session_init(void) {
125 module *prev_module = curr_module, *m;
126
127 for (m = loaded_modules; m; m = m->next) {
128 if (m->sess_init) {
129 curr_module = m;
130
131 pr_trace_msg(trace_channel, 12,
132 "invoking sess_init callback on mod_%s.c", m->name);
133 if (m->sess_init() < 0) {
134 int xerrno = errno;
135
136 pr_log_pri(PR_LOG_WARNING, "mod_%s.c: error initializing session: %s",
137 m->name, strerror(xerrno));
138
139 errno = xerrno;
140 return -1;
141 }
142 }
143 }
144
145 curr_module = prev_module;
146 return 0;
147 }
148
command_exists(const char * name)149 unsigned char command_exists(const char *name) {
150 int idx = -1;
151 unsigned int hash = 0;
152 cmdtable *cmdtab;
153
154 cmdtab = pr_stash_get_symbol2(PR_SYM_CMD, name, NULL, &idx, &hash);
155 while (cmdtab && cmdtab->cmd_type != CMD) {
156 pr_signals_handle();
157 cmdtab = pr_stash_get_symbol2(PR_SYM_CMD, name, cmdtab, &idx, &hash);
158 }
159
160 return (cmdtab ? TRUE : FALSE);
161 }
162
pr_module_exists(const char * name)163 unsigned char pr_module_exists(const char *name) {
164 return pr_module_get(name) != NULL ? TRUE : FALSE;
165 }
166
pr_module_get(const char * name)167 module *pr_module_get(const char *name) {
168 char buf[80] = {'\0'};
169 module *m;
170
171 if (name == NULL) {
172 errno = EINVAL;
173 return NULL;
174 }
175
176 /* Check the list of compiled-in modules. */
177 for (m = loaded_modules; m; m = m->next) {
178 memset(buf, '\0', sizeof(buf));
179 pr_snprintf(buf, sizeof(buf), "mod_%s.c", m->name);
180 buf[sizeof(buf)-1] = '\0';
181
182 if (strcmp(buf, name) == 0) {
183 return m;
184 }
185 }
186
187 errno = ENOENT;
188 return NULL;
189 }
190
modules_list2(int (* listf)(const char *,...),int flags)191 void modules_list2(int (*listf)(const char *, ...), int flags) {
192 if (listf == NULL) {
193 listf = printf;
194 }
195
196 if (flags & PR_MODULES_LIST_FL_SHOW_STATIC) {
197 register unsigned int i = 0;
198
199 listf("Compiled-in modules:\n");
200 for (i = 0; static_modules[i]; i++) {
201 module *m = static_modules[i];
202
203 if (flags & PR_MODULES_LIST_FL_SHOW_VERSION) {
204 const char *version;
205
206 version = m->module_version;
207 if (version != NULL) {
208 listf(" %s\n", version);
209
210 } else {
211 listf(" mod_%s.c\n", m->name);
212 }
213
214 } else {
215 listf(" mod_%s.c\n", m->name);
216 }
217 }
218
219 } else {
220 module *m;
221
222 listf("Loaded modules:\n");
223 for (m = loaded_modules; m; m = m->next) {
224
225 if (flags & PR_MODULES_LIST_FL_SHOW_VERSION) {
226 const char *version;
227
228 version = m->module_version;
229 if (version != NULL) {
230 listf(" %s\n", version);
231
232 } else {
233 listf(" mod_%s.c\n", m->name);
234 }
235
236 } else {
237 listf(" mod_%s.c\n", m->name);
238 }
239 }
240 }
241 }
242
modules_list(int flags)243 void modules_list(int flags) {
244 modules_list2(NULL, flags);
245 }
246
pr_module_load_authtab(module * m)247 int pr_module_load_authtab(module *m) {
248 if (m == NULL ||
249 m->name == NULL) {
250 errno = EINVAL;
251 return -1;
252 }
253
254 if (m->authtable) {
255 authtable *authtab;
256
257 for (authtab = m->authtable; authtab->name; authtab++) {
258 authtab->m = m;
259
260 if (pr_stash_add_symbol(PR_SYM_AUTH, authtab) < 0) {
261 return -1;
262 }
263 }
264 }
265
266 return 0;
267 }
268
pr_module_load_cmdtab(module * m)269 int pr_module_load_cmdtab(module *m) {
270 if (m == NULL ||
271 m->name == NULL) {
272 errno = EINVAL;
273 return -1;
274 }
275
276 if (m->cmdtable) {
277 cmdtable *cmdtab;
278
279 for (cmdtab = m->cmdtable; cmdtab->command; cmdtab++) {
280 cmdtab->m = m;
281
282 if (cmdtab->cmd_type == HOOK) {
283 if (pr_stash_add_symbol(PR_SYM_HOOK, cmdtab) < 0) {
284 return -1;
285 }
286
287 } else {
288 /* All other cmd_types are for CMDs: PRE_CMD, CMD, POST_CMD, etc. */
289 if (pr_stash_add_symbol(PR_SYM_CMD, cmdtab) < 0) {
290 return -1;
291 }
292 }
293 }
294 }
295
296 return 0;
297 }
298
pr_module_load_conftab(module * m)299 int pr_module_load_conftab(module *m) {
300 if (m == NULL ||
301 m->name == NULL) {
302 errno = EINVAL;
303 return -1;
304 }
305
306 if (m->conftable) {
307 conftable *conftab;
308
309 for (conftab = m->conftable; conftab->directive; conftab++) {
310 conftab->m = m;
311
312 if (pr_stash_add_symbol(PR_SYM_CONF, conftab) < 0) {
313 return -1;
314 }
315 }
316 }
317
318 return 0;
319 }
320
pr_module_load(module * m)321 int pr_module_load(module *m) {
322 char buf[256];
323
324 if (m == NULL ||
325 m->name == NULL) {
326 errno = EINVAL;
327 return -1;
328 }
329
330 /* Check the API version the module wants to use. */
331 if (m->api_version < PR_MODULE_API_VERSION) {
332 errno = EACCES;
333 return -1;
334 }
335
336 /* Do not allow multiple modules with the same name. */
337 memset(buf, '\0', sizeof(buf));
338 pr_snprintf(buf, sizeof(buf), "mod_%s.c", m->name);
339 buf[sizeof(buf)-1] = '\0';
340
341 if (pr_module_get(buf) != NULL) {
342 errno = EEXIST;
343 return -1;
344 }
345
346 /* Invoke the module's initialization routine. */
347 if (!m->init ||
348 m->init() >= 0) {
349
350 /* Assign a priority to this module. */
351 m->priority = curr_module_pri++;
352
353 /* Add the module's config, cmd, and auth tables. */
354 if (pr_module_load_conftab(m) < 0) {
355 return -1;
356 }
357
358 if (pr_module_load_cmdtab(m) < 0) {
359 return -1;
360 }
361
362 if (pr_module_load_authtab(m) < 0) {
363 return -1;
364 }
365
366 /* Add the module to the loaded_modules list. */
367 if (loaded_modules) {
368 m->next = loaded_modules;
369 loaded_modules->prev = m;
370 }
371
372 loaded_modules = m;
373
374 /* Generate an event. */
375 pr_event_generate("core.module-load", buf);
376 return 0;
377 }
378
379 errno = EPERM;
380 return -1;
381 }
382
pr_module_unload(module * m)383 int pr_module_unload(module *m) {
384 char buf[256];
385
386 if (m == NULL ||
387 m->name == NULL) {
388 errno = EINVAL;
389 return -1;
390 }
391
392 /* Make sure this module has been loaded. We can't unload a module that
393 * has not been loaded, now can we?
394 */
395
396 memset(buf, '\0', sizeof(buf));
397 pr_snprintf(buf, sizeof(buf), "mod_%s.c", m->name);
398 buf[sizeof(buf)-1] = '\0';
399
400 if (pr_module_get(buf) == NULL) {
401 errno = ENOENT;
402 return -1;
403 }
404
405 /* Generate an event. */
406 pr_event_generate("core.module-unload", buf);
407
408 /* Remove the module from the loaded_modules list. */
409 if (m->prev) {
410 m->prev->next = m->next;
411
412 } else {
413 /* This module is the start of the loaded_modules list (prev is NULL),
414 * so we need to update that pointer, too.
415 */
416 loaded_modules = m->next;
417 }
418
419 if (m->next)
420 m->next->prev = m->prev;
421
422 m->prev = m->next = NULL;
423
424 /* Remove the module's config, cmd, and auth tables. */
425 if (m->conftable) {
426 conftable *conftab;
427
428 for (conftab = m->conftable; conftab->directive; conftab++) {
429 pr_stash_remove_symbol(PR_SYM_CONF, conftab->directive, conftab->m);
430 }
431 }
432
433 if (m->cmdtable) {
434 cmdtable *cmdtab;
435
436 for (cmdtab = m->cmdtable; cmdtab->command; cmdtab++) {
437 if (cmdtab->cmd_type == HOOK) {
438 pr_stash_remove_symbol(PR_SYM_HOOK, cmdtab->command, cmdtab->m);
439
440 } else {
441 /* All other cmd_types are for CMDs: PRE_CMD, CMD, POST_CMD, etc. */
442 pr_stash_remove_symbol(PR_SYM_CMD, cmdtab->command, cmdtab->m);
443 }
444 }
445 }
446
447 if (m->authtable) {
448 authtable *authtab;
449
450 for (authtab = m->authtable; authtab->name; authtab++) {
451 pr_stash_remove_symbol(PR_SYM_AUTH, authtab->name, authtab->m);
452 }
453 }
454
455 /* Remove any callbacks that the module may have registered, i.e.:
456 *
457 * ctrls
458 * events
459 * timers
460 *
461 * Ideally we would also automatically unregister other callbacks that
462 * the module may have registered, such as FSIO, NetIO, variables, and
463 * response handlers. However, these APIs do not yet allow for
464 * removal of all callbacks for a given module.
465 */
466
467 #ifdef PR_USE_CTRLS
468 pr_ctrls_unregister(m, NULL);
469 #endif /* PR_USE_CTRLS */
470 pr_event_unregister(m, NULL, NULL);
471 pr_timer_remove(-1, m);
472
473 return 0;
474 }
475
modules_init(void)476 int modules_init(void) {
477 register unsigned int i = 0;
478
479 for (i = 0; static_modules[i]; i++) {
480 module *m = static_modules[i];
481
482 if (pr_module_load(m) < 0) {
483 pr_log_pri(PR_LOG_WARNING, "fatal: unable to load module 'mod_%s.c': %s",
484 m->name, strerror(errno));
485 exit(1);
486 }
487 }
488
489 return 0;
490 }
491