1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 2014-2017 The ProFTPD Project team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
20 * and other respective copyright holders give permission to link this program
21 * with OpenSSL, and distribute the resulting executable, without including
22 * the source code for OpenSSL in the source distribution.
23 */
24
25 /* Configuration database implementation. */
26
27 #include "conf.h"
28 #include "privs.h"
29
30 #ifdef HAVE_ARPA_INET_H
31 # include <arpa/inet.h>
32 #endif
33
34 /* From src/pool.c */
35 extern pool *global_config_pool;
36
37 /* Used by find_config_* */
38 static xaset_t *find_config_top = NULL;
39
40 static void config_dumpf(const char *, ...);
41
42 static config_rec *last_param_ptr = NULL;
43
44 static pool *config_tab_pool = NULL;
45 static pr_table_t *config_tab = NULL;
46 static unsigned int config_id = 0;
47
48 static const char *trace_channel = "config";
49
50 /* Adds a config_rec to the specified set */
pr_config_add_set(xaset_t ** set,const char * name,int flags)51 config_rec *pr_config_add_set(xaset_t **set, const char *name, int flags) {
52 pool *conf_pool = NULL, *set_pool = NULL;
53 config_rec *c, *parent = NULL;
54
55 if (set == NULL) {
56 errno = EINVAL;
57 return NULL;
58 }
59
60 if (!*set) {
61
62 /* Allocate a subpool from permanent_pool for the set. */
63 set_pool = make_sub_pool(permanent_pool);
64 pr_pool_tag(set_pool, "config set pool");
65
66 *set = xaset_create(set_pool, NULL);
67 (*set)->pool = set_pool;
68
69 /* Now, make a subpool for the config_rec to be allocated. The default
70 * pool size (PR_TUNABLE_NEW_POOL_SIZE, 512 by default) is a bit large
71 * for config_rec pools; use a smaller size.
72 */
73 conf_pool = pr_pool_create_sz(set_pool, 128);
74
75 } else {
76
77 /* Find the parent set for the config_rec to be allocated. */
78 if ((*set)->xas_list) {
79 parent = ((config_rec *) ((*set)->xas_list))->parent;
80 }
81
82 /* Now, make a subpool for the config_rec to be allocated. The default
83 * pool size (PR_TUNABLE_NEW_POOL_SIZE, 512 by default) is a bit large
84 * for config_rec pools; use a smaller size. Allocate the subpool
85 * from the parent's pool.
86 */
87 conf_pool = pr_pool_create_sz((*set)->pool, 128);
88 }
89
90 pr_pool_tag(conf_pool, "config_rec pool");
91
92 c = (config_rec *) pcalloc(conf_pool, sizeof(config_rec));
93 c->pool = conf_pool;
94 c->set = *set;
95 c->parent = parent;
96
97 if (name) {
98 c->name = pstrdup(conf_pool, name);
99 c->config_id = pr_config_set_id(c->name);
100 }
101
102 if (flags & PR_CONFIG_FL_INSERT_HEAD) {
103 xaset_insert(*set, (xasetmember_t *) c);
104
105 } else {
106 xaset_insert_end(*set, (xasetmember_t *) c);
107 }
108
109 return c;
110 }
111
add_config_set(xaset_t ** set,const char * name)112 config_rec *add_config_set(xaset_t **set, const char *name) {
113 return pr_config_add_set(set, name, 0);
114 }
115
116 /* Adds a config_rec to the given server. If no server is specified, the
117 * config_rec is added to the current "level".
118 */
pr_config_add(server_rec * s,const char * name,int flags)119 config_rec *pr_config_add(server_rec *s, const char *name, int flags) {
120 config_rec *parent = NULL, *c = NULL;
121 pool *p = NULL;
122 xaset_t **set = NULL;
123
124 if (s == NULL) {
125 s = pr_parser_server_ctxt_get();
126 }
127
128 if (s == NULL) {
129 errno = EINVAL;
130 return NULL;
131 }
132
133 c = pr_parser_config_ctxt_get();
134
135 if (c) {
136 parent = c;
137 p = c->pool;
138 set = &c->subset;
139
140 } else {
141 parent = NULL;
142
143 if (s->conf == NULL ||
144 s->conf->xas_list == NULL) {
145
146 p = make_sub_pool(s->pool);
147 pr_pool_tag(p, "pr_config_add() subpool");
148
149 } else {
150 p = ((config_rec *) s->conf->xas_list)->pool;
151 }
152
153 set = &s->conf;
154 }
155
156 if (!*set) {
157 *set = xaset_create(p, NULL);
158 }
159
160 c = pr_config_add_set(set, name, flags);
161 c->parent = parent;
162
163 return c;
164 }
165
add_config(server_rec * s,const char * name)166 config_rec *add_config(server_rec *s, const char *name) {
167 return pr_config_add(s, name, 0);
168 }
169
config_dumpf(const char * fmt,...)170 static void config_dumpf(const char *fmt, ...) {
171 char buf[PR_TUNABLE_BUFFER_SIZE] = {'\0'};
172 va_list msg;
173
174 va_start(msg, fmt);
175 pr_vsnprintf(buf, sizeof(buf), fmt, msg);
176 va_end(msg);
177
178 buf[sizeof(buf)-1] = '\0';
179
180 pr_log_debug(DEBUG5, "%s", buf);
181 }
182
pr_config_dump(void (* dumpf)(const char *,...),xaset_t * s,char * indent)183 void pr_config_dump(void (*dumpf)(const char *, ...), xaset_t *s,
184 char *indent) {
185 config_rec *c = NULL;
186
187 if (dumpf == NULL) {
188 dumpf = config_dumpf;
189 }
190
191 if (s == NULL) {
192 return;
193 }
194
195 if (indent == NULL) {
196 indent = "";
197 }
198
199 for (c = (config_rec *) s->xas_list; c; c = c->next) {
200 pr_signals_handle();
201
202 /* Don't display directives whose name starts with an underscore. */
203 if (c->name != NULL &&
204 *(c->name) != '_') {
205 dumpf("%s%s", indent, c->name);
206 }
207
208 if (c->subset) {
209 pr_config_dump(dumpf, c->subset, pstrcat(c->pool, indent, " ", NULL));
210 }
211 }
212 }
213
config_type_str(int config_type)214 static const char *config_type_str(int config_type) {
215 const char *type = "(unknown)";
216
217 switch (config_type) {
218 case CONF_ROOT:
219 type = "CONF_ROOT";
220 break;
221
222 case CONF_DIR:
223 type = "CONF_DIR";
224 break;
225
226 case CONF_ANON:
227 type = "CONF_ANON";
228 break;
229
230 case CONF_LIMIT:
231 type = "CONF_LIMIT";
232 break;
233
234 case CONF_VIRTUAL:
235 type = "CONF_VIRTUAL";
236 break;
237
238 case CONF_DYNDIR:
239 type = "CONF_DYNDIR";
240 break;
241
242 case CONF_GLOBAL:
243 type = "CONF_GLOBAL";
244 break;
245
246 case CONF_CLASS:
247 type = "CONF_CLASS";
248 break;
249
250 case CONF_NAMED:
251 type = "CONF_NAMED";
252 break;
253
254 case CONF_USERDATA:
255 type = "CONF_USERDATA";
256 break;
257
258 case CONF_PARAM:
259 type = "CONF_PARAM";
260 break;
261 };
262
263 return type;
264 }
265
266 /* Compare two different config_recs to see if they are the same. Note
267 * that "same" here has to be very specific.
268 *
269 * Returns 0 if the two config_recs are the same, and 1 if they differ, and
270 * -1 if there was an error.
271 */
config_cmp(const config_rec * a,const char * a_name,const config_rec * b,const char * b_name)272 static int config_cmp(const config_rec *a, const char *a_name,
273 const config_rec *b, const char *b_name) {
274
275 if (a == NULL ||
276 b == NULL) {
277 errno = EINVAL;
278 return -1;
279 }
280
281 if (a->config_type != b->config_type) {
282 pr_trace_msg(trace_channel, 18,
283 "configs '%s' and '%s' have mismatched config_type (%s != %s)",
284 a_name, b_name, config_type_str(a->config_type),
285 config_type_str(b->config_type));
286 return 1;
287 }
288
289 if (a->flags != b->flags) {
290 pr_trace_msg(trace_channel, 18,
291 "configs '%s' and '%s' have mismatched flags (%ld != %ld)",
292 a_name, b_name, a->flags, b->flags);
293 return 1;
294 }
295
296 if (a->argc != b->argc) {
297 pr_trace_msg(trace_channel, 18,
298 "configs '%s' and '%s' have mismatched argc (%d != %d)",
299 a_name, b_name, a->argc, b->argc);
300 return 1;
301 }
302
303 if (a->argc > 0) {
304 register unsigned int i;
305
306 for (i = 0; i < a->argc; i++) {
307 if (a->argv[i] != b->argv[i]) {
308 pr_trace_msg(trace_channel, 18,
309 "configs '%s' and '%s' have mismatched argv[%u] (%p != %p)",
310 a_name, b_name, i, a->argv[i], b->argv[i]);
311 return 1;
312 }
313 }
314 }
315
316 if (a->config_id != b->config_id) {
317 pr_trace_msg(trace_channel, 18,
318 "configs '%s' and '%s' have mismatched config_id (%d != %d)",
319 a_name, b_name, a->config_id, b->config_id);
320 return 1;
321 }
322
323 /* Save the string comparison for last, to try to save some CPU. */
324 if (strcmp(a->name, b->name) != 0) {
325 pr_trace_msg(trace_channel, 18,
326 "configs '%s' and '%s' have mismatched name ('%s' != '%s')",
327 a_name, b_name, a->name, b->name);
328 return 1;
329 }
330
331 return 0;
332 }
333
copy_config_from(const config_rec * src,config_rec * dst)334 static config_rec *copy_config_from(const config_rec *src, config_rec *dst) {
335 config_rec *c;
336 unsigned int cargc;
337 void **cargv, **sargv;
338
339 if (src == NULL ||
340 dst == NULL) {
341 return NULL;
342 }
343
344 /* If the destination parent config_rec doesn't already have a subset
345 * container, allocate one.
346 */
347 if (dst->subset == NULL) {
348 dst->subset = xaset_create(dst->pool, NULL);
349 }
350
351 c = pr_config_add_set(&dst->subset, src->name, 0);
352 c->config_type = src->config_type;
353 c->flags = src->flags;
354 c->config_id = src->config_id;
355
356 c->argc = src->argc;
357 c->argv = pcalloc(c->pool, (src->argc + 1) * sizeof(void *));
358
359 cargc = c->argc;
360 cargv = c->argv;
361 sargv = src->argv;
362
363 while (cargc--) {
364 pr_signals_handle();
365 *cargv++ = *sargv++;
366 }
367
368 *cargv = NULL;
369 return c;
370 }
371
pr_config_merge_down(xaset_t * s,int dynamic)372 void pr_config_merge_down(xaset_t *s, int dynamic) {
373 config_rec *c, *dst;
374
375 if (s == NULL ||
376 s->xas_list == NULL) {
377 return;
378 }
379
380 for (c = (config_rec *) s->xas_list; c; c = c->next) {
381 pr_signals_handle();
382
383 if ((c->flags & CF_MERGEDOWN) ||
384 (c->flags & CF_MERGEDOWN_MULTI)) {
385
386 for (dst = (config_rec *) s->xas_list; dst; dst = dst->next) {
387 if (dst->config_type == CONF_ANON ||
388 dst->config_type == CONF_DIR) {
389
390 /* If an option of the same name/type is found in the
391 * next level down, it overrides, so we don't merge.
392 */
393 if ((c->flags & CF_MERGEDOWN) &&
394 find_config(dst->subset, c->config_type, c->name, FALSE)) {
395 continue;
396 }
397
398 if (dynamic) {
399 /* If we are doing a dynamic merge (i.e. .ftpaccess files) then
400 * we do not need to re-merge the static configs that are already
401 * there. Otherwise we are creating copies needlessly of any
402 * config_rec marked with the CF_MERGEDOWN_MULTI flag, which
403 * adds to the memory usage/processing time.
404 *
405 * If neither the src or the dst config have the CF_DYNAMIC
406 * flag, it's a static config, and we can skip this merge and move
407 * on. Otherwise, we can merge it.
408 */
409 if (!(c->flags & CF_DYNAMIC) && !(dst->flags & CF_DYNAMIC)) {
410 continue;
411 }
412 }
413
414 /* We want to scan the config_recs contained in dst's subset to see
415 * if we can find another config_rec that duplicates the one we want
416 * to merge into dst.
417 */
418 if (dst->subset != NULL) {
419 config_rec *r = NULL;
420 int merge = TRUE;
421
422 for (r = (config_rec *) dst->subset->xas_list; r; r = r->next) {
423 pr_signals_handle();
424
425 if (config_cmp(r, r->name, c, c->name) == 0) {
426 merge = FALSE;
427
428 pr_trace_msg(trace_channel, 15,
429 "found duplicate '%s' record in '%s', skipping merge",
430 r->name, dst->name);
431 break;
432 }
433 }
434
435 if (merge) {
436 (void) copy_config_from(c, dst);
437 }
438
439 } else {
440 /* No existing subset in dst; we can merge this one in. */
441 (void) copy_config_from(c, dst);
442 }
443 }
444 }
445 }
446 }
447
448 /* Top level merged, recursively merge lower levels */
449 for (c = (config_rec *) s->xas_list; c; c = c->next) {
450 if (c->subset &&
451 (c->config_type == CONF_ANON ||
452 c->config_type == CONF_DIR)) {
453 pr_config_merge_down(c->subset, dynamic);
454 }
455 }
456 }
457
find_config_next2(config_rec * prev,config_rec * c,int type,const char * name,int recurse,unsigned long flags)458 config_rec *find_config_next2(config_rec *prev, config_rec *c, int type,
459 const char *name, int recurse, unsigned long flags) {
460 config_rec *top = c;
461 unsigned int cid = 0;
462 size_t namelen = 0;
463
464 /* We do two searches (if recursing) so that we find the "deepest"
465 * level first.
466 *
467 * The `recurse` argument tells us HOW to perform that search, e.g.
468 * how to do our DFS (depth-first search) approach:
469 *
470 * recurse = 0:
471 * Start at c, search all `next` nodes in list, i.e. all nodes at
472 * the same depth, no recursion.
473 *
474 * recurse = 1:
475 * Start at c, search all `subset` nodes in tree first, then siblings,
476 * then `next` nodes of parent.
477 *
478 * recurse > 1:
479 * Start with child nodes first (`subset`), then c itself (skipping
480 * siblings nodes).
481 */
482
483 if (c == NULL &&
484 prev == NULL) {
485 errno = EINVAL;
486 return NULL;
487 }
488
489 if (prev == NULL) {
490 prev = top;
491 }
492
493 if (name != NULL) {
494 cid = pr_config_get_id(name);
495 namelen = strlen(name);
496 }
497
498 do {
499 if (recurse) {
500 config_rec *res = NULL;
501
502 pr_signals_handle();
503
504 /* Search subsets. */
505 for (c = top; c; c = c->next) {
506 if (c->subset &&
507 c->subset->xas_list) {
508 config_rec *subc = NULL;
509
510 for (subc = (config_rec *) c->subset->xas_list;
511 subc;
512 subc = subc->next) {
513 pr_signals_handle();
514
515 if (subc->config_type == CONF_ANON &&
516 (flags & PR_CONFIG_FIND_FL_SKIP_ANON)) {
517 /* Skip <Anonymous> config_rec */
518 continue;
519 }
520
521 if (subc->config_type == CONF_DIR &&
522 (flags & PR_CONFIG_FIND_FL_SKIP_DIR)) {
523 /* Skip <Directory> config_rec */
524 continue;
525 }
526
527 if (subc->config_type == CONF_LIMIT &&
528 (flags & PR_CONFIG_FIND_FL_SKIP_LIMIT)) {
529 /* Skip <Limit> config_rec */
530 continue;
531 }
532
533 if (subc->config_type == CONF_DYNDIR &&
534 (flags & PR_CONFIG_FIND_FL_SKIP_DYNDIR)) {
535 /* Skip .ftpaccess config_rec */
536 continue;
537 }
538
539 res = find_config_next2(NULL, subc, type, name, recurse + 1, flags);
540 if (res) {
541 return res;
542 }
543 }
544 }
545
546 if (recurse > 1) {
547 /* Sibling subsets are already searched by the caller; no need to
548 * continue here (Bug#4307).
549 */
550 break;
551 }
552 }
553 }
554
555 /* Recurse: If deep recursion yielded no match try the current subset.
556 *
557 * NOTE: the string comparison here is specifically case-sensitive.
558 * The config_rec names are supplied by the modules and intentionally
559 * case sensitive (they shouldn't be verbatim from the config file)
560 * Do NOT change this to strcasecmp(), no matter how tempted you are
561 * to do so, it will break stuff. ;)
562 */
563 for (c = top; c; c = c->next) {
564 pr_signals_handle();
565
566 if (type == -1 ||
567 type == c->config_type) {
568
569 if (name == NULL) {
570 return c;
571 }
572
573 if (cid != 0 &&
574 cid == c->config_id) {
575 return c;
576 }
577
578 if (strncmp(name, c->name, namelen + 1) == 0) {
579 return c;
580 }
581 }
582
583 if (recurse > 1) {
584 /* Sibling subsets are already searched by the caller; no need to
585 * continue here (Bug#4307).
586 */
587 break;
588 }
589 }
590
591 if (recurse == 1) {
592 /* All siblings have been searched; continue the search at the previous
593 * level.
594 */
595 if (prev->parent &&
596 prev->parent->next &&
597 prev->parent->set != find_config_top) {
598 prev = top = prev->parent->next;
599 c = top;
600 continue;
601 }
602 }
603 break;
604
605 } while (TRUE);
606
607 errno = ENOENT;
608 return NULL;
609 }
610
find_config_next(config_rec * prev,config_rec * c,int type,const char * name,int recurse)611 config_rec *find_config_next(config_rec *prev, config_rec *c, int type,
612 const char *name, int recurse) {
613 return find_config_next2(prev, c, type, name, recurse, 0UL);
614 }
615
find_config_set_top(config_rec * c)616 void find_config_set_top(config_rec *c) {
617 if (c &&
618 c->parent) {
619 find_config_top = c->parent->set;
620
621 } else {
622 find_config_top = NULL;
623 }
624 }
625
find_config2(xaset_t * set,int type,const char * name,int recurse,unsigned long flags)626 config_rec *find_config2(xaset_t *set, int type, const char *name,
627 int recurse, unsigned long flags) {
628
629 if (set == NULL ||
630 set->xas_list == NULL) {
631 errno = EINVAL;
632 return NULL;
633 }
634
635 find_config_set_top((config_rec *) set->xas_list);
636
637 return find_config_next2(NULL, (config_rec *) set->xas_list, type, name,
638 recurse, flags);
639 }
640
find_config(xaset_t * set,int type,const char * name,int recurse)641 config_rec *find_config(xaset_t *set, int type, const char *name, int recurse) {
642 return find_config2(set, type, name, recurse, 0UL);
643 }
644
get_param_ptr(xaset_t * set,const char * name,int recurse)645 void *get_param_ptr(xaset_t *set, const char *name, int recurse) {
646 config_rec *c;
647
648 if (set == NULL) {
649 last_param_ptr = NULL;
650 errno = ENOENT;
651 return NULL;
652 }
653
654 c = find_config(set, CONF_PARAM, name, recurse);
655 if (c &&
656 c->argc) {
657 last_param_ptr = c;
658 return c->argv[0];
659 }
660
661 last_param_ptr = NULL;
662 errno = ENOENT;
663 return NULL;
664 }
665
get_param_ptr_next(const char * name,int recurse)666 void *get_param_ptr_next(const char *name, int recurse) {
667 config_rec *c;
668
669 if (!last_param_ptr ||
670 !last_param_ptr->next) {
671 last_param_ptr = NULL;
672 errno = ENOENT;
673 return NULL;
674 }
675
676 c = find_config_next(last_param_ptr, last_param_ptr->next, CONF_PARAM,
677 name, recurse);
678 if (c &&
679 c->argv) {
680 last_param_ptr = c;
681 return c->argv[0];
682 }
683
684 last_param_ptr = NULL;
685 errno = ENOENT;
686 return NULL;
687 }
688
pr_config_remove(xaset_t * set,const char * name,int flags,int recurse)689 int pr_config_remove(xaset_t *set, const char *name, int flags, int recurse) {
690 server_rec *s;
691 config_rec *c;
692 int found = 0;
693
694 s = pr_parser_server_ctxt_get();
695 if (s == NULL) {
696 s = main_server;
697 }
698
699 while ((c = find_config(set, -1, name, recurse)) != NULL) {
700 xaset_t *found_set;
701
702 pr_signals_handle();
703
704 found++;
705
706 found_set = c->set;
707 xaset_remove(found_set, (xasetmember_t *) c);
708
709 /* If the set is empty, and has no more contained members in the xas_list,
710 * destroy the set.
711 */
712 if (!found_set->xas_list) {
713
714 /* First, set any pointers to the container of the set to NULL. */
715 if (c->parent &&
716 c->parent->subset == found_set) {
717 c->parent->subset = NULL;
718
719 } else if (s && s->conf == found_set) {
720 s->conf = NULL;
721 }
722
723 if (!(flags & PR_CONFIG_FL_PRESERVE_ENTRY)) {
724 /* Next, destroy the set's pool, which destroys the set as well. */
725 destroy_pool(found_set->pool);
726 }
727
728 } else {
729 if (!(flags & PR_CONFIG_FL_PRESERVE_ENTRY)) {
730 /* If the set was not empty, destroy only the requested config_rec. */
731 destroy_pool(c->pool);
732 }
733 }
734 }
735
736 return found;
737 }
738
remove_config(xaset_t * set,const char * name,int recurse)739 int remove_config(xaset_t *set, const char *name, int recurse) {
740 return pr_config_remove(set, name, 0, recurse);
741 }
742
add_config_param_set(xaset_t ** set,const char * name,unsigned int num,...)743 config_rec *add_config_param_set(xaset_t **set, const char *name,
744 unsigned int num, ...) {
745 config_rec *c;
746 void **argv;
747 va_list ap;
748
749 c = pr_config_add_set(set, name, 0);
750 if (c == NULL) {
751 return NULL;
752 }
753
754 c->config_type = CONF_PARAM;
755 c->argc = num;
756 c->argv = pcalloc(c->pool, (num+1) * sizeof(void *));
757
758 argv = c->argv;
759 va_start(ap,num);
760
761 while (num-- > 0) {
762 *argv++ = va_arg(ap, void *);
763 }
764
765 va_end(ap);
766
767 return c;
768 }
769
add_config_param_str(const char * name,unsigned int num,...)770 config_rec *add_config_param_str(const char *name, unsigned int num, ...) {
771 config_rec *c;
772 char *arg = NULL;
773 void **argv = NULL;
774 va_list ap;
775
776 c = pr_config_add(NULL, name, 0);
777 if (c != NULL) {
778 c->config_type = CONF_PARAM;
779 c->argc = num;
780 c->argv = pcalloc(c->pool, (num+1) * sizeof(char *));
781
782 argv = c->argv;
783 va_start(ap, num);
784
785 while (num-- > 0) {
786 arg = va_arg(ap, char *);
787 if (arg) {
788 *argv++ = pstrdup(c->pool, arg);
789
790 } else {
791 *argv++ = NULL;
792 }
793 }
794
795 va_end(ap);
796 }
797
798 return c;
799 }
800
pr_conf_add_server_config_param_str(server_rec * s,const char * name,unsigned int num,...)801 config_rec *pr_conf_add_server_config_param_str(server_rec *s, const char *name,
802 unsigned int num, ...) {
803 config_rec *c;
804 char *arg = NULL;
805 void **argv = NULL;
806 va_list ap;
807
808 c = pr_config_add(s, name, 0);
809 if (c == NULL) {
810 return NULL;
811 }
812
813 c->config_type = CONF_PARAM;
814 c->argc = num;
815 c->argv = pcalloc(c->pool, (num+1) * sizeof(char *));
816
817 argv = c->argv;
818 va_start(ap, num);
819
820 while (num-- > 0) {
821 arg = va_arg(ap, char *);
822 if (arg) {
823 *argv++ = pstrdup(c->pool, arg);
824
825 } else {
826 *argv++ = NULL;
827 }
828 }
829
830 va_end(ap);
831 return c;
832 }
833
add_config_param(const char * name,unsigned int num,...)834 config_rec *add_config_param(const char *name, unsigned int num, ...) {
835 config_rec *c;
836 void **argv;
837 va_list ap;
838
839 if (name == NULL) {
840 errno = EINVAL;
841 return NULL;
842 }
843
844 c = pr_config_add(NULL, name, 0);
845 if (c) {
846 c->config_type = CONF_PARAM;
847 c->argc = num;
848 c->argv = pcalloc(c->pool, (num+1) * sizeof(void*));
849
850 argv = c->argv;
851 va_start(ap, num);
852
853 while (num-- > 0) {
854 *argv++ = va_arg(ap, void *);
855 }
856
857 va_end(ap);
858 }
859
860 return c;
861 }
862
pr_config_get_id(const char * name)863 unsigned int pr_config_get_id(const char *name) {
864 const void *ptr = NULL;
865 unsigned int id = 0;
866
867 if (name == NULL) {
868 errno = EINVAL;
869 return 0;
870 }
871
872 if (config_tab == NULL) {
873 errno = EPERM;
874 return 0;
875 }
876
877 ptr = pr_table_get(config_tab, name, NULL);
878 if (ptr == NULL) {
879 errno = ENOENT;
880 return 0;
881 }
882
883 id = *((unsigned int *) ptr);
884 return id;
885 }
886
pr_config_set_id(const char * name)887 unsigned int pr_config_set_id(const char *name) {
888 unsigned int *ptr = NULL;
889 unsigned int id;
890
891 if (!name) {
892 errno = EINVAL;
893 return 0;
894 }
895
896 if (!config_tab) {
897 errno = EPERM;
898 return 0;
899 }
900
901 ptr = pr_table_pcalloc(config_tab, sizeof(unsigned int));
902 *ptr = ++config_id;
903
904 if (pr_table_add(config_tab, name, ptr, sizeof(unsigned int *)) < 0) {
905 if (errno == EEXIST) {
906 id = pr_config_get_id(name);
907
908 } else {
909 if (errno == ENOSPC) {
910 pr_log_debug(DEBUG9,
911 "error adding '%s' to config ID table: table is full", name);
912
913 } else {
914 pr_log_debug(DEBUG9, "error adding '%s' to config ID table: %s",
915 name, strerror(errno));
916 }
917
918 return 0;
919 }
920
921 } else {
922 id = *ptr;
923 }
924
925 return id;
926 }
927
init_config(void)928 void init_config(void) {
929 unsigned int maxents;
930
931 /* Make sure global_config_pool is destroyed */
932 if (global_config_pool) {
933 destroy_pool(global_config_pool);
934 global_config_pool = NULL;
935 }
936
937 if (config_tab) {
938 /* Clear the existing config ID table. This needs to happen when proftpd
939 * is restarting.
940 */
941 if (pr_table_empty(config_tab) < 0) {
942 pr_log_debug(DEBUG0, "error emptying config ID table: %s",
943 strerror(errno));
944 }
945
946 if (pr_table_free(config_tab) < 0) {
947 pr_log_debug(DEBUG0, "error destroying config ID table: %s",
948 strerror(errno));
949 }
950
951 config_tab = pr_table_alloc(config_tab_pool, 0);
952
953 /* Reset the ID counter as well. Otherwise, an exceedingly long-lived
954 * proftpd, restarted many times, has the possibility of overflowing
955 * the counter data type.
956 */
957 config_id = 0;
958
959 } else {
960
961 config_tab_pool = make_sub_pool(permanent_pool);
962 pr_pool_tag(config_tab_pool, "Config Table Pool");
963 config_tab = pr_table_alloc(config_tab_pool, 0);
964 }
965
966 /* Increase the max "size" of the table; some configurations can lead
967 * to a large number of configuration directives.
968 */
969 maxents = 32768;
970
971 if (pr_table_ctl(config_tab, PR_TABLE_CTL_SET_MAX_ENTS, &maxents) < 0) {
972 pr_log_debug(DEBUG2, "error setting config ID table max size to %u: %s",
973 maxents, strerror(errno));
974 }
975
976 return;
977 }
978