Coverage Report

Created: 2024-09-30 06:24

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