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