Coverage Report

Created: 2026-09-28 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/server/cf_parse.c
Line
Count
Source
1
/*
2
 *   This program is free software; you can redistribute it and/or modify
3
 *   it under the terms of the GNU General Public License as published by
4
 *   the Free Software Foundation; either version 2 of the License, or
5
 *   (at your option) any later version.
6
 *
7
 *   This program is distributed in the hope that it will be useful,
8
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 *   GNU General Public License for more details.
11
 *
12
 *   You should have received a copy of the GNU General Public License
13
 *   along with this program; if not, write to the Free Software
14
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15
 */
16
17
/**
18
 * $Id: b6074b082006b065d7dab77f76438e54fdaade1e $
19
 * @file cf_parse.c
20
 * @brief Convert internal format configuration values into native C types.
21
 *
22
 * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23
 * @copyright 2000,2006 The FreeRADIUS server project
24
 * @copyright 2000 Miquel van Smoorenburg (miquels@cistron.nl)
25
 * @copyright 2000 Alan DeKok (aland@freeradius.org)
26
 */
27
RCSID("$Id: b6074b082006b065d7dab77f76438e54fdaade1e $")
28
29
#include <string.h>
30
#include <sys/errno.h>
31
#include <sys/fcntl.h>
32
33
#include <freeradius-devel/server/cf_file.h>
34
#include <freeradius-devel/server/cf_parse.h>
35
#include <freeradius-devel/server/cf_priv.h>
36
#include <freeradius-devel/server/log.h>
37
#include <freeradius-devel/server/tmpl.h>
38
#include <freeradius-devel/server/virtual_servers.h>
39
#include <freeradius-devel/server/main_config.h>
40
#include <freeradius-devel/util/debug.h>
41
#include <freeradius-devel/util/inet.h>
42
#include <freeradius-devel/util/misc.h>
43
#include <freeradius-devel/util/perm.h>
44
45
static conf_parser_t conf_term = CONF_PARSER_TERMINATOR;
46
static char const parse_spaces[] = "                                                                                                                                                                                                                                              ";
47
48
0
#define PAIR_SPACE(_cs) ((_cs->depth + 1) * 2)
49
0
#define SECTION_SPACE(_cs) (_cs->depth * 2)
50
51
void cf_pair_debug_log(CONF_SECTION const *cs, CONF_PAIR *cp, conf_parser_t const *rule)
52
0
{
53
0
  char const  *value;
54
0
  char    *tmp = NULL;
55
0
  char const  *quote = "";
56
0
  bool    secret = (rule && (rule->flags & CONF_FLAG_SECRET));
57
0
  fr_type_t type;
58
59
0
  if (cp->printed) return;
60
61
  /*
62
   *  tmpls are special, they just need to get printed as string
63
   */
64
0
  if (!rule || (rule->flags & CONF_FLAG_TMPL)) {
65
0
    type = FR_TYPE_STRING;
66
0
  } else {
67
0
    type = rule->type;
68
0
  }
69
70
0
  if (secret && (fr_debug_lvl < L_DBG_LVL_3)) {
71
0
    cf_log_debug(cs, "%.*s%s = <<< secret >>>", PAIR_SPACE(cs), parse_spaces, cp->attr);
72
0
    return;
73
0
  }
74
75
  /*
76
   *  Print the strings with the correct quotation character and escaping.
77
   */
78
0
  if (fr_type_is_string(type)) {
79
0
    value = tmp = fr_asprint(NULL, cp->value, talloc_strlen(cp->value), fr_token_quote[cp->rhs_quote]);
80
81
0
  } else {
82
0
    value = cf_pair_value(cp);
83
0
  }
84
85
0
  if (fr_type_is_quoted(type)) {
86
0
    switch (cf_pair_value_quote(cp)) {
87
0
    default:
88
0
      break;
89
90
0
    case T_DOUBLE_QUOTED_STRING:
91
0
      quote = "\"";
92
0
      break;
93
94
0
    case T_SINGLE_QUOTED_STRING:
95
0
      quote = "'";
96
0
      break;
97
98
0
    case T_BACK_QUOTED_STRING:
99
0
      quote = "`";
100
0
      break;
101
102
0
    case T_SOLIDUS_QUOTED_STRING:
103
0
      quote = "/";
104
0
      break;
105
0
    }
106
0
  }
107
108
0
  cf_log_debug(cs, "%.*s%s = %s%s%s", PAIR_SPACE(cs), parse_spaces, cp->attr, quote, value, quote);
109
110
0
  talloc_free(tmp);
111
112
0
  cp->printed = true;
113
0
}
114
115
/** Parses a #CONF_PAIR into a boxed value
116
 *
117
 * @copybrief cf_pair_value
118
 * @see cf_pair_value
119
 *
120
 * @param[in] ctx to allocate any dynamic buffers in.
121
 * @param[out] out  Where to write the parsed value.
122
 * @param[in] cp  to parse.
123
 * @param[in] rule  to parse to.  May contain flags.
124
 * @return
125
 *  - 0 on success.
126
 *  - -1 on failure.
127
 */
128
int cf_pair_to_value_box(TALLOC_CTX *ctx, fr_value_box_t *out, CONF_PAIR *cp, conf_parser_t const *rule)
129
0
{
130
0
  if (fr_value_box_from_str(ctx, out, rule->type, NULL, cp->value, talloc_strlen(cp->value), NULL) < 0) {
131
0
    cf_log_perr(cp, "Invalid value \"%s\" for config item %s (data type %s)",
132
0
          cp->value, cp->attr, fr_type_to_str(rule->type));
133
134
0
    return -1;
135
0
  }
136
137
  /*
138
   *  Strings can be file paths...
139
   */
140
0
  if (fr_type_is_string(rule->type)) {
141
0
    if (fr_rule_file_socket(rule)) {
142
      /*
143
       *  Attempt to actually connect to the socket.
144
       *  There's no real standard behaviour across
145
       *  operating systems for this.
146
       *
147
       *  This also implies fr_rule_file_exists.
148
       */
149
0
      if (fr_rule_file_readable(rule) || fr_rule_file_writable(rule)) {
150
0
        if (cf_file_check_effective(cf_pair_value(cp), cf_file_check_unix_connect, NULL) != 0) {
151
0
          cf_log_perr(cp, "File check failed");
152
0
          return -1;
153
0
        }
154
      /*
155
       *  Otherwise just passively check if the socket
156
       *  exists.
157
       */
158
0
      } else if (fr_rule_file_exists(rule)) {
159
0
        if (cf_file_check_effective(cf_pair_value(cp), cf_file_check_unix_perm, NULL) != 0) {
160
0
          cf_log_perr(cp, "File check failed");
161
0
          return -1;
162
0
        }
163
      /*
164
       *  ...and if there's no existence requirement
165
       *  just check that it's a unix socket.
166
       */
167
0
      } else {
168
0
        switch (cf_file_check_effective(cf_pair_value(cp), cf_file_check_unix_perm, NULL)) {
169
0
        default:
170
          /* ok */
171
0
          break;
172
173
0
        case CF_FILE_NO_UNIX_SOCKET:
174
0
          cf_log_perr(cp, "File check failed");
175
0
          return -1;
176
0
        }
177
0
      }
178
0
    }
179
    /*
180
     *  If there's out AND it's an input file, check
181
     *  that we can read it.  This check allows errors
182
     *  to be caught as early as possible, during
183
     *  server startup.
184
     */
185
0
    else if (fr_rule_file_readable(rule) && (cf_file_check(cp, true) < 0)) {
186
0
    error:
187
0
      fr_value_box_clear(out);
188
0
      return -1;
189
0
    }
190
0
    else if (fr_rule_file_exists(rule) && (cf_file_check(cp, false) < 0)) goto error;
191
0
  }
192
193
0
  fr_value_box_mark_safe_for(out, FR_VALUE_BOX_SAFE_FOR_ANY);
194
195
0
  return 0;
196
0
}
197
198
/** Parses a #CONF_PAIR into a C data type
199
 *
200
 * @copybrief cf_pair_value
201
 * @see cf_pair_value
202
 *
203
 * @param[in] ctx to allocate any dynamic buffers in.
204
 * @param[out] out  Where to write the parsed value.
205
 * @param[in] base  address of the structure out points into.
206
 *      May be NULL in the case of manual parsing.
207
 * @param[in] ci  to parse.
208
 * @param[in] rule  to parse to.  May contain flags.
209
 * @return
210
 *  - 0 on success.
211
 *  - -1 on failure.
212
 */
213
int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM *ci, conf_parser_t const *rule)
214
0
{
215
0
  int   ret = 0;
216
0
  bool    cant_be_empty, tmpl;
217
218
0
  fr_slen_t slen;
219
220
0
  CONF_PAIR *cp = cf_item_to_pair(ci);
221
222
0
  cant_be_empty = fr_rule_not_empty(rule);
223
0
  tmpl = fr_rule_is_tmpl(rule);
224
225
0
  fr_assert(cp);
226
0
  fr_assert(!fr_rule_is_attribute(rule) || tmpl);    /* Attribute flag only valid for templates */
227
228
0
  if (fr_rule_required(rule)) cant_be_empty = true; /* May want to review this in the future... */
229
230
  /*
231
   *  Everything except templates must have a base type.
232
   */
233
0
  if (!rule->type && !tmpl) {
234
0
    cf_log_err(cp, "Configuration pair \"%s\" must have a data type", cp->attr);
235
0
    return -1;
236
0
  }
237
238
  /*
239
   *  Catch crazy errors.
240
   */
241
0
  if (!cp->value) {
242
0
    cf_log_err(cp, "Configuration pair \"%s\" must have a value", cp->attr);
243
0
    return -1;
244
0
  }
245
246
  /*
247
   *  Check for zero length strings
248
   */
249
0
  if ((cp->value[0] == '\0') && cant_be_empty) {
250
0
    cf_log_err(cp, "Configuration pair \"%s\" must not be empty (zero length)", cp->attr);
251
0
    if (!fr_rule_required(rule)) cf_log_err(cp, "Comment item to silence this message");
252
0
  error:
253
0
    ret = -1;
254
0
    return ret;
255
0
  }
256
257
0
  if (tmpl) {
258
0
    tmpl_t      *vpt;
259
0
    static tmpl_rules_t rules = {
260
0
            .attr = {
261
0
              .allow_unknown = true,
262
0
              .allow_unresolved = true,
263
0
              .allow_foreign = true,
264
0
            },
265
0
            .literals_safe_for = FR_VALUE_BOX_SAFE_FOR_ANY,
266
0
          };
267
0
    fr_sbuff_t    sbuff = FR_SBUFF_IN(cp->value, strlen(cp->value));
268
269
0
    rules.attr.list_def = request_attr_request;
270
271
    /*
272
     *  Bare words are magical sometimes.
273
     */
274
0
    if (cp->rhs_quote == T_BARE_WORD) {
275
      /*
276
       *  Attributes are parsed as attributes.
277
       */
278
0
      if (fr_rule_is_attribute(rule)) {
279
0
        slen = tmpl_afrom_attr_substr(cp, NULL, &vpt, &sbuff, NULL, &rules);
280
0
        if (slen < 0) goto tmpl_error;
281
282
0
        fr_assert(vpt);
283
284
0
        *(tmpl_t **)out = vpt;
285
0
        goto finish;
286
0
      }
287
288
      /*
289
       *  @todo - otherwise bare words are NOT parsed as attributes, they're parsed as
290
       *  bare words, ala v3.
291
       */
292
293
0
    } else if (fr_rule_is_attribute(rule)) {
294
0
      cf_log_err(cp, "Unexpected quoted string.  An attribute name is required here.");
295
0
      goto error;
296
0
    }
297
298
0
    slen = tmpl_afrom_substr(cp, &vpt, &sbuff, cp->rhs_quote,
299
0
           value_parse_rules_unquoted[cp->rhs_quote],
300
0
           &rules);
301
0
    if (slen < 0) {
302
0
    tmpl_error:
303
0
      cf_canonicalize_error(cp, slen, fr_strerror(), cp->value);
304
0
      goto error;
305
0
    }
306
0
    fr_assert(vpt);
307
308
    /*
309
     *  The caller told us what data type was expected.  If we do have data, then try to cast
310
     *  it to the requested type.
311
     */
312
0
    if ((rule->type != FR_TYPE_VOID) && tmpl_contains_data(vpt)) {
313
0
      slen = 0;         // for errors
314
315
0
      if (tmpl_is_data_unresolved(vpt)) {
316
0
        tmpl_cast_set(vpt, rule->type);
317
318
0
        if (tmpl_resolve(vpt, NULL) < 0) goto tmpl_error;
319
320
0
      } else if (rule->type != tmpl_value_type(vpt)) {
321
0
        fr_assert(tmpl_is_data(vpt));
322
323
0
        if (tmpl_cast_in_place(vpt, rule->type, NULL) < 0) goto tmpl_error;
324
0
      }
325
0
    }
326
327
0
    *(tmpl_t **)out = vpt;
328
0
    goto finish;
329
0
  }
330
331
  /*
332
   *  Parse as a boxed value out of sheer laziness...
333
   *
334
   *  Then we get all the internal types for free, and only need to add
335
   *  one set of printing and parsing functions for new types...
336
   */
337
0
  {
338
0
    fr_value_box_t  vb;
339
340
0
    if (cf_pair_to_value_box(ctx, &vb, cf_item_to_pair(ci), rule) < 0) goto error;
341
342
0
    if (fr_value_box_memcpy_out(out, &vb) < 0) {
343
0
      cf_log_perr(cp, "Failed unboxing parsed configuration item value");
344
0
      fr_value_box_clear_value(&vb);
345
0
      goto error;
346
0
    }
347
0
  }
348
349
0
finish:
350
351
0
  return ret;
352
0
}
353
354
/** Allocate a pair using the dflt value and quotation
355
 *
356
 * The pair created by this function should fed to #cf_pair_parse for parsing.
357
 *
358
 * @param[out] out  Where to write the CONF_PAIR we created with the default value.
359
 * @param[in] parent  being populated.
360
 * @param[in] cs  to parent the CONF_PAIR from.
361
 * @param[in] rule  to use to create the default.
362
 * @return
363
 *  - 0 on success.
364
 *  - -1 on failure.
365
 */
366
static int cf_pair_default(CONF_PAIR **out, void *parent, CONF_SECTION *cs, conf_parser_t const *rule)
367
368
0
{
369
0
  int   lineno = 0;
370
0
  char const  *expanded;
371
0
  CONF_PAIR *cp;
372
0
  char    buffer[8192];
373
0
  fr_token_t  dflt_quote = rule->quote;
374
375
0
  fr_assert(rule->dflt || rule->dflt_func);
376
377
0
  if (fr_rule_required(rule)) {
378
0
    cf_log_err(cs, "Configuration pair \"%s\" must have a value", rule->name1);
379
0
    return -1;
380
0
  }
381
382
  /*
383
   *  If no default quote was set, determine it from the type
384
   */
385
0
  if (dflt_quote == T_INVALID) {
386
0
    if (fr_type_is_quoted(rule->type)) {
387
0
      dflt_quote = T_DOUBLE_QUOTED_STRING;
388
0
    } else {
389
0
      dflt_quote = T_BARE_WORD;
390
0
    }
391
0
  }
392
393
  /*
394
   *  Use the dynamic default function if set
395
   */
396
0
  if (rule->dflt_func) {
397
0
    if (rule->dflt_func(out, parent, cs, dflt_quote, rule) < 0) {
398
0
      cf_log_perr(cs, "Failed producing default for \"%s\"", rule->name1);
399
0
      return -1;
400
0
    }
401
402
0
    return 0;
403
0
  }
404
405
0
  expanded = cf_expand_variables("<internal>", lineno, cs, buffer, sizeof(buffer), rule->dflt, -1, NULL,
406
0
               (dflt_quote != T_BARE_WORD));
407
0
  if (!expanded) {
408
0
    cf_log_err(cs, "Failed expanding variable %s", rule->name1);
409
0
    return -1;
410
0
  }
411
412
0
  cp = cf_pair_alloc(cs, rule->name1, expanded, T_OP_EQ, T_BARE_WORD, dflt_quote);
413
0
  if (!cp) return -1;
414
415
  /*
416
   *  Set the ret to indicate we used a default value
417
   */
418
0
  *out = cp;
419
420
0
  return 1;
421
0
}
422
423
static int cf_pair_unescape(CONF_PAIR *cp, conf_parser_t const *rule)
424
0
{
425
0
  char const *p;
426
0
  char *str, *unescaped, *q;
427
428
0
  if (!cp->value) return 0;
429
430
0
  if (cp->rhs_quote != T_DOUBLE_QUOTED_STRING) return 0;
431
432
0
  if (!(rule->flags & CONF_FLAG_TMPL)) {
433
0
    if (rule->type != FR_TYPE_STRING) return 0;
434
0
  }
435
436
0
  if (strchr(cp->value, '\\') == NULL) return 0;
437
438
0
  str = talloc_strdup(cp, cp->value);
439
0
  if (!str) return -1;
440
441
0
  p = cp->value;
442
0
  q = str;
443
0
  while (*p) {
444
0
    if (*p != '\\') {
445
0
      *(q++) = *(p++);
446
0
      continue;
447
0
    }
448
449
0
    p++;
450
0
    if (*p == '\0') {
451
0
      *q++ = '\\';
452
0
      continue;
453
0
    }
454
0
    switch (*p) {
455
0
    case 'r':
456
0
      *q++ = '\r';
457
0
      break;
458
0
    case 'n':
459
0
      *q++ = '\n';
460
0
      break;
461
0
    case 't':
462
0
      *q++ = '\t';
463
0
      break;
464
465
0
    default:
466
0
      if ((*p >= '0') && (*p <= '7')) {
467
0
        unsigned long oct;
468
0
        char *end;
469
470
0
        oct = strtoul(p, &end, 8);
471
0
        if (oct == ULONG_MAX) {
472
0
          cf_log_err(cp, "Failed parsing octal string");
473
0
        error:
474
0
          talloc_free(str);
475
0
          return -1;
476
0
        }
477
478
0
        if (!oct) {
479
0
          cf_log_err(cp, "Cannot have embedded zeros in value at %s", p);
480
0
          goto error;
481
0
        }
482
483
0
        if (oct > UINT8_MAX) {
484
0
          cf_log_err(cp, "Invalid octal number in value at %s", p);
485
0
          goto error;
486
0
        }
487
488
0
        *q++ = oct;
489
0
        p = end;
490
0
        continue;
491
0
      } else {
492
0
        *q++ = *p;
493
0
      }
494
0
      break;
495
0
    }
496
0
    p++;
497
0
  }
498
0
  *q = '\0';
499
500
0
  unescaped = talloc_strdup(cp, str); /* no embedded NUL */
501
0
  talloc_free(str);
502
0
  if (!unescaped) return -1;
503
504
  /*
505
   *  Replace the old value with the new one.
506
   */
507
0
  talloc_const_free(cp->value);
508
0
  cp->value = unescaped;
509
510
0
  return 0;
511
0
}
512
513
/** Parses a #CONF_PAIR into a C data type, with a default value.
514
 *
515
 * @param[in] ctx To allocate arrays and values in.
516
 * @param[out] out  Where to write the result.
517
 *      Must not be NULL unless rule->runc is provided.
518
 * @param[in] base  address of the structure out points into.
519
 *      May be NULL in the case of manual parsing.
520
 * @param[in] cs  to search for matching #CONF_PAIR in.
521
 * @param[in] rule  to parse #CONF_PAIR with.
522
 * @return
523
 *  - 1 if default value was used, or if there was no CONF_PAIR or dflt.
524
 *  - 0 on success.
525
 *  - -1 on error.
526
 *  - -2 if deprecated.
527
 */
528
static int CC_HINT(nonnull(4,5)) cf_pair_parse_internal(TALLOC_CTX *ctx, void *out, void *base,
529
                    CONF_SECTION *cs, conf_parser_t const *rule)
530
0
{
531
0
  bool    required, deprecated, was_dflt = false;
532
0
  size_t    count = 0;
533
0
  CONF_PAIR *cp = NULL, *dflt_cp = NULL;
534
535
0
#ifndef NDEBUG
536
0
  char const  *dflt = rule->dflt;
537
0
  fr_token_t  dflt_quote = rule->quote;
538
0
#endif
539
0
  cf_parse_t  func = rule->func ? rule->func : cf_pair_parse_value;
540
541
0
  fr_assert(!fr_rule_is_tmpl(rule) || !dflt || (dflt_quote != T_INVALID)); /* We ALWAYS need a quoting type for templates */
542
543
  /*
544
   *  Functions don't necessarily *need* to write
545
   *  anywhere, so their data pointer can be NULL.
546
   */
547
0
  if (!out) {
548
0
    if (!rule->func) {
549
0
      cf_log_err(cs, "Rule doesn't specify output destination");
550
0
      return -1;
551
0
    }
552
0
  }
553
554
0
  required = fr_rule_required(rule);
555
0
  deprecated = fr_rule_deprecated(rule);
556
557
0
  cp = cf_pair_find(cs, rule->name1);
558
0
  if (cp && cp->item.parsed) {
559
0
    return 0;
560
0
  }
561
0
  cp = NULL;
562
563
  /*
564
   *  If the item is multi-valued we allocate an array
565
   *  to hold the multiple values.
566
   */
567
0
  if (fr_rule_multi(rule)) {
568
0
    void    **array;
569
0
    size_t    i = 0;
570
571
    /*
572
     *  Easier than re-allocing
573
     */
574
0
    count = cf_pair_count(cs, rule->name1);
575
576
    /*
577
     *  Multivalued, but there's no value, create a
578
     *  default pair.
579
     */
580
0
    if (!count) {
581
0
      if (deprecated) return 0;
582
583
0
      if (!fr_rule_dflt(rule)) {
584
0
        if (required) {
585
0
      need_value:
586
0
          cf_log_err(cs, "Configuration item \"%s\" must have a value", rule->name1);
587
0
          return -1;
588
0
        }
589
0
        return 1;
590
0
      }
591
592
0
      if (cf_pair_default(&dflt_cp, base, cs, rule) < 0) return -1;
593
0
      count = cf_pair_count(cs, rule->name1); /* Dynamic functions can add multiple defaults */
594
0
      if (!count) {
595
0
        if (fr_rule_not_empty(rule)) {
596
0
          cf_log_err(cs, "Configuration item \"%s\" cannot be empty", rule->name1);
597
0
          return -1;
598
0
        }
599
0
        return 0;
600
0
      }
601
0
    }
602
603
0
    if (deprecated) {
604
      /*
605
       *  Emit the deprecated warning in the
606
       *  context of the first pair.
607
       */
608
0
      cp = cf_pair_find(cs, rule->name1);
609
0
      fr_assert(cp);
610
611
0
    deprecated:
612
0
      cf_log_err(cp, "Configuration pair \"%s\" is deprecated", cp->attr);
613
0
      return -2;
614
0
    }
615
616
    /*
617
     *  No output, so don't bother allocing the array
618
     */
619
0
    if (!out) {
620
0
      array = NULL;
621
622
    /*
623
     *  Tmpl is outside normal range
624
     */
625
0
    } else if (fr_rule_is_tmpl(rule)) {
626
0
      MEM(array = (void **)talloc_zero_array(ctx, tmpl_t *, count));
627
628
    /*
629
     *  Allocate an array of values.
630
     *
631
     *  We don't NULL terminate.  Consumer must use
632
     *  talloc_array_length().
633
     */
634
0
    } else {
635
0
      array = fr_type_array_alloc(ctx, rule->type, count);
636
0
      if (unlikely(array == NULL)) {
637
0
        cf_log_perr(cp, "Failed allocating value array");
638
0
        return -1;
639
0
      }
640
0
    }
641
642
0
    while ((cp = cf_pair_find_next(cs, cp, rule->name1))) {
643
0
      int   ret;
644
0
      void    *entry;
645
0
      TALLOC_CTX  *value_ctx = array;
646
647
      /*
648
       *  Figure out where to write the output
649
       */
650
0
      if (!array) {
651
0
        entry = NULL;
652
0
      } else if ((rule->type == FR_TYPE_VOID) || (rule->flags & CONF_FLAG_TMPL)) {
653
0
        entry = &array[i++];
654
0
      } else {
655
0
        entry = ((uint8_t *) array) + (i++ * fr_value_box_field_sizes[rule->type]);
656
0
      }
657
658
0
      if (cf_pair_unescape(cp, rule) < 0) return -1;
659
660
      /*
661
       *  Switch between custom parsing function
662
       *  and the standard value parsing function.
663
       */
664
0
      cf_pair_debug_log(cs, cp, rule);
665
666
0
      if (cp->item.parsed) continue;
667
0
      ret = func(value_ctx, entry, base, cf_pair_to_item(cp), rule);
668
0
      if (ret < 0) {
669
0
        talloc_free(array);
670
0
        return -1;
671
0
      }
672
0
      cp->item.parsed = true;
673
0
    }
674
0
    if (array) *(void **)out = array;
675
  /*
676
   *  Single valued config item gets written to
677
   *  the data pointer directly.
678
   */
679
0
  } else {
680
0
    CONF_PAIR *next;
681
0
    int   ret;
682
683
0
    cp = cf_pair_find(cs, rule->name1);
684
0
    if (!cp) {
685
0
      if (deprecated) return 0;
686
687
0
      if (!fr_rule_dflt(rule)) {
688
0
        if (required) goto need_value;
689
0
        return 1;
690
0
      }
691
692
0
      if (cf_pair_default(&dflt_cp, base, cs, rule) < 0) return -1;
693
0
      cp = dflt_cp;
694
0
      if (!cp) {
695
0
        if (fr_rule_not_empty(rule)) {
696
0
          cf_log_err(cs, "Configuration item \"%s\" cannot be empty", rule->name1);
697
0
          return -1;
698
0
        }
699
700
0
        return 0;
701
0
      }
702
0
      was_dflt = true;
703
0
    } else {
704
0
      if (cf_pair_unescape(cp, rule) < 0) return -1;
705
0
    }
706
707
0
    next = cf_pair_find_next(cs, cp, rule->name1);
708
0
    if (next) {
709
0
      cf_log_err(cf_pair_to_item(next), "Invalid duplicate configuration item '%s'", rule->name1);
710
0
      return -1;
711
0
    }
712
0
    if (deprecated) goto deprecated;
713
714
0
    cf_pair_debug_log(cs, cp, rule);
715
716
0
    if (cp->item.parsed) return 0;
717
0
    ret = func(ctx, out, base, cf_pair_to_item(cp), rule);
718
0
    if (ret < 0) return -1;
719
0
    cp->item.parsed = true;
720
0
  }
721
722
0
  return was_dflt ? 1 : 0;
723
0
}
724
725
/** Parses a #CONF_PAIR into a C data type, with a default value.
726
 *
727
 * Takes fields from a #conf_parser_t struct and uses them to parse the string value
728
 * of a #CONF_PAIR into a C data type matching the type argument.
729
 *
730
 * The format of the types are the same as #fr_value_box_t types.
731
 *
732
 * @note The dflt value will only be used if no matching #CONF_PAIR is found. Empty strings will not
733
 *   result in the dflt value being used.
734
 *
735
 * **fr_type_t to data type mappings**
736
 * | fr_type_t               | Data type          | Dynamically allocated  |
737
 * | ----------------------- | ------------------ | ---------------------- |
738
 * | FR_TYPE_BOOL            | ``bool``           | No                     |
739
 * | FR_TYPE_UINT32          | ``uint32_t``       | No                     |
740
 * | FR_TYPE_UINT16          | ``uint16_t``       | No                     |
741
 * | FR_TYPE_UINT64          | ``uint64_t``       | No                     |
742
 * | FR_TYPE_INT32           | ``int32_t``        | No                     |
743
 * | FR_TYPE_STRING          | ``char const *``   | Yes                    |
744
 * | FR_TYPE_IPV4_ADDR       | ``fr_ipaddr_t``    | No                     |
745
 * | FR_TYPE_IPV4_PREFIX     | ``fr_ipaddr_t``    | No                     |
746
 * | FR_TYPE_IPV6_ADDR       | ``fr_ipaddr_t``    | No                     |
747
 * | FR_TYPE_IPV6_PREFIX     | ``fr_ipaddr_t``    | No                     |
748
 * | FR_TYPE_COMBO_IP_ADDR   | ``fr_ipaddr_t``    | No                     |
749
 * | FR_TYPE_COMBO_IP_PREFIX | ``fr_ipaddr_t``    | No                     |
750
 * | FR_TYPE_TIME_DELTA      | ``fr_time_delta_t``| No                     |
751
 *
752
 * @param[in] ctx To allocate arrays and values in.
753
 * @param[in] cs  to search for matching #CONF_PAIR in.
754
 * @param[in] name  of #CONF_PAIR to search for.
755
 * @param[in] type  Data type to parse #CONF_PAIR value as.
756
 *      Should be one of the following ``data`` types,
757
 *      and one or more of the following ``flag`` types or'd together:
758
759
 *  - ``data`` #FR_TYPE_BOOL    - @copybrief FR_TYPE_BOOL
760
 *  - ``data`` #FR_TYPE_UINT32    - @copybrief FR_TYPE_UINT32
761
 *  - ``data`` #FR_TYPE_UINT16    - @copybrief FR_TYPE_UINT16
762
 *  - ``data`` #FR_TYPE_UINT64    - @copybrief FR_TYPE_UINT64
763
 *  - ``data`` #FR_TYPE_INT32   - @copybrief FR_TYPE_INT32
764
 *  - ``data`` #FR_TYPE_STRING    - @copybrief FR_TYPE_STRING
765
 *  - ``data`` #FR_TYPE_IPV4_ADDR   - @copybrief FR_TYPE_IPV4_ADDR (IPv4 address with prefix 32).
766
 *  - ``data`` #FR_TYPE_IPV4_PREFIX   - @copybrief FR_TYPE_IPV4_PREFIX (IPv4 address with variable prefix).
767
 *  - ``data`` #FR_TYPE_IPV6_ADDR   - @copybrief FR_TYPE_IPV6_ADDR (IPv6 address with prefix 128).
768
 *  - ``data`` #FR_TYPE_IPV6_PREFIX   - @copybrief FR_TYPE_IPV6_PREFIX (IPv6 address with variable prefix).
769
 *  - ``data`` #FR_TYPE_COMBO_IP_ADDR   - @copybrief FR_TYPE_COMBO_IP_ADDR (IPv4/IPv6 address with
770
 *              prefix 32/128).
771
 *  - ``data`` #FR_TYPE_COMBO_IP_PREFIX - @copybrief FR_TYPE_COMBO_IP_PREFIX (IPv4/IPv6 address with
772
 *              variable prefix).
773
 *  - ``data`` #FR_TYPE_TIME_DELTA    - @copybrief FR_TYPE_TIME_DELTA
774
 *  - ``flag`` #CONF_FLAG_TMPL    - @copybrief CONF_FLAG_TMPL
775
 *                Feeds the value into #tmpl_afrom_substr. Value can be
776
 *                obtained when processing requests, with #tmpl_expand or #tmpl_aexpand.
777
 *  - ``flag`` #FR_TYPE_DEPRECATED    - @copybrief FR_TYPE_DEPRECATED
778
 *  - ``flag`` #CONF_FLAG_REQUIRED    - @copybrief CONF_FLAG_REQUIRED
779
 *  - ``flag`` #CONF_FLAG_ATTRIBUTE   - @copybrief CONF_FLAG_ATTRIBUTE
780
 *  - ``flag`` #CONF_FLAG_SECRET    - @copybrief CONF_FLAG_SECRET
781
 *  - ``flag`` #CONF_FLAG_FILE_READABLE - @copybrief CONF_FLAG_FILE_READABLE
782
 *  - ``flag`` #CONF_FLAG_FILE_WRITABLE - @copybrief CONF_FLAG_FILE_WRITABLE
783
 *  - ``flag`` #CONF_FLAG_NOT_EMPTY   - @copybrief CONF_FLAG_NOT_EMPTY
784
 *  - ``flag`` #CONF_FLAG_MULTI   - @copybrief CONF_FLAG_MULTI
785
 *  - ``flag`` #CONF_FLAG_IS_SET    - @copybrief CONF_FLAG_IS_SET
786
 * @param[out] data   Pointer to a global variable, or pointer to a field in the struct being populated with values.
787
 * @param[in] dflt    value to use, if no #CONF_PAIR is found.
788
 * @param[in] dflt_quote  around the dflt value.
789
 * @return
790
 *  - 1 if default value was used, or if there was no CONF_PAIR or dflt.
791
 *  - 0 on success.
792
 *  - -1 on error.
793
 *  - -2 if deprecated.
794
 */
795
int cf_pair_parse(TALLOC_CTX *ctx, CONF_SECTION *cs, char const *name,
796
      unsigned int type, void *data, char const *dflt, fr_token_t dflt_quote)
797
0
{
798
0
  conf_parser_t rule = {
799
0
    .name1 = name,
800
0
    .type = type,
801
0
    .dflt = dflt,
802
0
    .quote = dflt_quote
803
0
  };
804
805
0
  return cf_pair_parse_internal(ctx, data, NULL, cs, &rule);
806
0
}
807
808
/** Pre-allocate a config section structure to allow defaults to be set
809
 *
810
 * @param cs    The parent subsection.
811
 * @param base    pointer or variable.
812
 * @param rule    that may have defaults in this config section.
813
 * @return
814
 *  - 0 on success.
815
 *  - -1 on failure.
816
 */
817
static int cf_section_parse_init(CONF_SECTION *cs, void *base, conf_parser_t const *rule)
818
0
{
819
0
  CONF_PAIR *cp;
820
821
  /*
822
   *  This rule refers to a named subsection
823
   */
824
0
  if ((rule->flags & CONF_FLAG_SUBSECTION)) {
825
0
    char const  *name2 = NULL;
826
0
    CONF_SECTION  *subcs;
827
828
    /*
829
     *  Optional MUST be listed before required ones
830
     */
831
0
    if ((rule->flags & CONF_FLAG_OPTIONAL) != 0) {
832
0
      return 0;
833
0
    }
834
835
0
    subcs = cf_section_find(cs, rule->name1, rule->name2);
836
837
    /*
838
     *  Set the is_set field for the subsection.
839
     */
840
0
    if (rule->flags & CONF_FLAG_IS_SET) {
841
0
      bool *is_set;
842
843
0
      is_set = rule->data ? rule->is_set_ptr : ((uint8_t *)base) + rule->is_set_offset;
844
0
      if (is_set) *is_set = (subcs != NULL);
845
0
    }
846
847
    /*
848
     *  It exists, we don't have to do anything else.
849
     */
850
0
    if (subcs) return 0;
851
852
    /*
853
     *  If there is no subsection, either complain,
854
     *  allow it, or create it with default values.
855
     */
856
0
    if (rule->flags & CONF_FLAG_REQUIRED) {
857
0
        cf_log_err(cs, "Missing %s {} subsection", rule->name1);
858
0
        return -1;
859
0
    }
860
861
    /*
862
     *  It's OK for this to be missing.  Don't
863
     *  initialize it.
864
     */
865
0
    if ((rule->flags & CONF_FLAG_OK_MISSING) != 0) return 0;
866
867
    /*
868
     *  If there's no subsection in the
869
     *  config, BUT the conf_parser_t wants one,
870
     *  then create an empty one.  This is so
871
     *  that we can track the strings,
872
     *  etc. allocated in the subsection.
873
     */
874
0
    if (DEBUG_ENABLED4) cf_log_debug(cs, "Allocating fake section \"%s\"", rule->name1);
875
876
    /*
877
     *  If name1 is CF_IDENT_ANY, then don't
878
     *  alloc the section as we have no idea
879
     *  what it should be called.
880
     */
881
0
    if (rule->name1 == CF_IDENT_ANY) return 0;
882
883
    /*
884
     *  Don't specify name2 if it's CF_IDENT_ANY
885
     */
886
0
    if (rule->name2 != CF_IDENT_ANY) name2 = rule->name2;
887
0
    subcs = cf_section_alloc(cs, cs, rule->name1, name2);
888
0
    if (!subcs) return -1;
889
890
0
    return 0;
891
0
  }
892
893
  /*
894
   *  This rule refers to another conf_parse_t which is included in-line in
895
   *  this section.
896
   */
897
0
  if ((rule->flags & CONF_FLAG_REF) != 0) {
898
0
    conf_parser_t const *rule_p;
899
0
    uint8_t *sub_base = base;
900
901
0
    fr_assert(rule->subcs != NULL);
902
903
0
    sub_base += rule->offset;
904
905
0
    for (rule_p = rule->subcs; rule_p->name1; rule_p++) {
906
0
      int ret = cf_section_parse_init(cs, sub_base, rule_p);
907
0
      if (ret < 0) return ret;
908
0
    }
909
0
    return 0;
910
0
  }
911
912
  /*
913
   *  Don't re-initialize data which was already parsed.
914
   */
915
0
  cp = cf_pair_find(cs, rule->name1);
916
0
  if (cp && cp->item.parsed) return 0;
917
918
0
  if ((rule->type != FR_TYPE_STRING) &&
919
0
      (!(rule->flags & CONF_FLAG_FILE_READABLE)) &&
920
0
      (!(rule->flags & CONF_FLAG_FILE_WRITABLE))) {
921
0
    return 0;
922
0
  }
923
924
  /*
925
   *  CONF_FLAG_NO_OUTPUT means the rule has no framework-managed
926
   *  output slot - nothing to NULL-init.
927
   */
928
0
  if (rule->flags & CONF_FLAG_NO_OUTPUT) return 0;
929
930
0
  if (rule->data) {
931
0
    *(char **) rule->data = NULL;
932
0
  } else if (base) {
933
0
    *(char **) (((char *)base) + rule->offset) = NULL;
934
0
  } else {
935
0
    return 0;
936
0
  }
937
938
0
  return 0;
939
0
}
940
941
static void cf_section_parse_warn(CONF_SECTION *cs)
942
0
{
943
0
  cf_item_foreach(&cs->item, ci) {
944
    /*
945
     *  Don't recurse on sections. We can only safely
946
     *  check conf pairs at the same level as the
947
     *  section that was just parsed.
948
     */
949
0
    if (ci->type == CONF_ITEM_SECTION) continue;
950
0
    if (ci->type == CONF_ITEM_PAIR) {
951
0
      CONF_PAIR *cp;
952
953
0
      cp = cf_item_to_pair(ci);
954
0
      if (cp->item.parsed || cp->item.referenced || (ci->lineno < 0)) continue;
955
956
0
      WARN("%s[%d]: The item '%s' is defined, but is unused by the configuration",
957
0
           ci->filename, ci->lineno,
958
0
           cp->attr);
959
0
    }
960
961
    /*
962
     *  Skip everything else.
963
     */
964
0
  }
965
0
}
966
967
/** Parse a subsection
968
 *
969
 * @note Turns out using nested structures (instead of pointers) for subsections, was actually
970
 *  a pretty bad design decision, and will need to be fixed at some future point.
971
 *  For now we have a horrible hack where only multi-subsections get an array of structures
972
 *  of the appropriate size.
973
 *
974
 * @param[in] ctx to allocate any additional structures under.
975
 * @param[out] out  pointer to a struct/pointer to fill with data.
976
 * @param[in] base  address of the structure out points into.
977
 *      May be NULL in the case of manual parsing.
978
 * @param[in] cs  to parse.
979
 * @param[in] rule  to parse the subcs with.
980
 * @return
981
 *  - 0 on success.
982
 *  - -1 on general error.
983
 *  - -2 if a deprecated #CONF_ITEM was found.
984
 */
985
static int cf_subsection_parse(TALLOC_CTX *ctx, void *out, void *base, CONF_SECTION *cs, conf_parser_t const *rule)
986
0
{
987
0
  CONF_SECTION    *subcs = NULL;
988
0
  int     count = 0, i = 0, ret;
989
990
0
  size_t      subcs_size = rule->subcs_size;
991
0
  conf_parser_t const *rules = rule->subcs;
992
993
  /*
994
   *  CF_IDENT_ANY section rules act as a catch-all: skip any
995
   *  subsection that's already been claimed and marked parsed
996
   *  by an earlier specific rule, so the wildcard only handles
997
   *  the leftovers.  CONF_FLAG_ALWAYS_PARSE overrides this and
998
   *  makes the rule observe every matching subsection.
999
   */
1000
0
  bool const    skip_parsed = (rule->name1 == CF_IDENT_ANY) &&
1001
0
                !(rule->flags & CONF_FLAG_ALWAYS_PARSE);
1002
1003
0
  bool const    no_output = (rule->flags & CONF_FLAG_NO_OUTPUT);
1004
1005
0
  uint8_t     **array = NULL;
1006
1007
0
  fr_assert(rule->flags & CONF_FLAG_SUBSECTION);
1008
1009
0
  if (skip_parsed) {
1010
0
    while ((subcs = cf_section_find_next(cs, subcs, rule->name1, rule->name2))) {
1011
0
      if (!cf_item_is_parsed(cf_section_to_item(subcs))) break;
1012
0
    }
1013
0
  } else {
1014
0
    subcs = cf_section_find(cs, rule->name1, rule->name2);
1015
0
  }
1016
0
  if (!subcs) return 0;
1017
1018
  /*
1019
   *  Handle the single subsection case (which is simple)
1020
   */
1021
0
  if (!(rule->flags & CONF_FLAG_MULTI)) {
1022
0
    uint8_t *buff = NULL;
1023
1024
0
    if (DEBUG_ENABLED4) cf_log_debug(cs, "Evaluating rules for %s section.  Output %p",
1025
0
             cf_section_name1(subcs), out);
1026
1027
    /*
1028
     *  Add any rules, so the func can just call cf_section_parse
1029
     *  if it wants to continue after doing its stuff.
1030
     */
1031
0
    if (cf_section_rules_push(subcs, rules) < 0) return -1;
1032
0
    if (rule->func) return rule->func(ctx, out, base, cf_section_to_item(subcs), rule);
1033
1034
    /*
1035
     *  FIXME: We shouldn't allow nested structures like this.
1036
     *  Each subsection struct should be allocated separately so
1037
     *  we have a clean talloc hierarchy.
1038
     */
1039
0
    if (!subcs_size) return cf_section_parse(ctx, out, subcs);
1040
1041
0
    if (out && !no_output) {
1042
0
      MEM(buff = talloc_zero_array(ctx, uint8_t, subcs_size));
1043
0
      if (rule->subcs_type) talloc_set_name_const(buff, rule->subcs_type);
1044
0
    }
1045
1046
0
    ret = cf_section_parse(buff, buff, subcs);
1047
0
    if (ret < 0) {
1048
0
      talloc_free(buff);
1049
0
      return ret;
1050
0
    }
1051
1052
0
    if (out && !no_output) *((uint8_t **)out) = buff;
1053
1054
0
    return 0;
1055
0
  }
1056
1057
0
  fr_assert(subcs_size);
1058
1059
  /*
1060
   *  Handle the multi subsection case (which is harder)
1061
   */
1062
0
  subcs = NULL;
1063
0
  while ((subcs = cf_section_find_next(cs, subcs, rule->name1, rule->name2))) {
1064
0
    if (skip_parsed && cf_item_is_parsed(cf_section_to_item(subcs))) continue;
1065
0
    count++;
1066
0
  }
1067
1068
  /*
1069
   *  Allocate an array to hold the subsections
1070
   */
1071
0
  if (out && !no_output) {
1072
0
    MEM(array = talloc_zero_array(ctx, uint8_t *, count));
1073
0
    if (rule->subcs_type) talloc_set_name(array, "%s *", rule->subcs_type);
1074
0
  }
1075
  /*
1076
   *  Start parsing...
1077
   *
1078
   *  Note, we allocate each subsection structure individually
1079
   *  so that they can be used as talloc contexts and we can
1080
   *  keep the talloc hierarchy clean.
1081
   */
1082
0
  subcs = NULL;
1083
0
  while ((subcs = cf_section_find_next(cs, subcs, rule->name1, rule->name2))) {
1084
0
    uint8_t *buff = NULL;
1085
1086
0
    if (skip_parsed && cf_item_is_parsed(cf_section_to_item(subcs))) continue;
1087
1088
0
    if (DEBUG_ENABLED4) cf_log_debug(cs, "Evaluating rules for %s[%i] section.  Output %p",
1089
0
             cf_section_name1(subcs),
1090
0
             i, out);
1091
1092
0
    if (array) {
1093
0
      MEM(buff = talloc_zero_array(array, uint8_t, subcs_size));
1094
0
      if (rule->subcs_type) talloc_set_name_const(buff, rule->subcs_type);
1095
0
      array[i++] = buff;
1096
0
    }
1097
1098
    /*
1099
     *  Add any rules, so the func can just call cf_section_parse
1100
     *  if it wants to continue after doing its stuff.
1101
     */
1102
0
    if (cf_section_rules_push(subcs, rules) < 0) {
1103
0
      talloc_free(array);
1104
0
      return -1;
1105
0
    }
1106
0
    if (rule->func) {
1107
0
      ret = rule->func(ctx, buff, base, cf_section_to_item(subcs), rule);
1108
0
      if (ret < 0) {
1109
0
        talloc_free(array);
1110
0
        return ret;
1111
0
      }
1112
0
      continue;
1113
0
    }
1114
1115
0
    ret = cf_section_parse(buff, buff, subcs);
1116
0
    if (ret < 0) {
1117
0
      talloc_free(array);
1118
0
      return ret;
1119
0
    }
1120
0
  }
1121
1122
0
  if (out && !no_output) *((uint8_t ***)out) = array;
1123
1124
0
  return 0;
1125
0
}
1126
1127
static int cf_section_parse_rule(TALLOC_CTX *ctx, void *base, CONF_SECTION *cs, conf_parser_t const *rule)
1128
0
{
1129
0
  int   ret;
1130
0
  bool    *is_set = NULL;
1131
0
  void    *data = NULL;
1132
1133
  /*
1134
   *  Ignore ON_READ parse rules if there's no subsequent
1135
   *  parse functions.
1136
   */
1137
0
  if (!rule->func && rule->on_read) return 0;
1138
1139
  /*
1140
   *  Pre-allocate the config structure to hold default values
1141
   */
1142
0
  if (cf_section_parse_init(cs, base, rule) < 0) return -1;
1143
1144
0
  if (rule->data) {
1145
0
    data = rule->data; /* prefer this. */
1146
0
  } else if (base &&
1147
0
       (!(rule->flags & CONF_FLAG_NO_OUTPUT) || (rule->flags & CONF_FLAG_SUBSECTION))) {
1148
    /*
1149
     *  CONF_FLAG_NO_OUTPUT on a pair rule means leave
1150
     *  data NULL so the framework won't write through
1151
     *  base+offset.  For subsections it only suppresses
1152
     *  the end-of-MULTI array write (handled inside
1153
     *  cf_subsection_parse) - we still need to pass
1154
     *  `base` through so nested rules can address into it.
1155
     */
1156
0
    data = ((uint8_t *)base) + rule->offset;
1157
0
  }
1158
1159
  /*
1160
   *  Handle subsections specially
1161
   */
1162
0
  if (rule->flags & CONF_FLAG_SUBSECTION) {
1163
0
    return cf_subsection_parse(ctx, data, base, cs, rule);
1164
0
  }
1165
1166
  /*
1167
   *  A pair rule with name1 == CF_IDENT_ANY is a catch-all:
1168
   *  feed every still-unparsed CONF_PAIR in this section to the
1169
   *  rule's parser func.  Order matters - specific rules before
1170
   *  this entry have already claimed (and marked parsed) their
1171
   *  pairs, so the catch-all only sees the leftovers, unless
1172
   *  CONF_FLAG_ALWAYS_PARSE is set (in which case it sees every
1173
   *  pair regardless).
1174
   *
1175
   *  The rule must provide a func; there's no sensible default
1176
   *  output offset when the pair's name varies.  The func
1177
   *  receives one CONF_PAIR at a time via ci and can read the
1178
   *  name via cf_pair_attr().
1179
   */
1180
0
  if (!(rule->flags & CONF_FLAG_REF) && rule->name1 == CF_IDENT_ANY) {
1181
0
    bool const  always_parse = (rule->flags & CONF_FLAG_ALWAYS_PARSE);
1182
0
    CONF_PAIR *cp = NULL;
1183
1184
0
    if (!rule->func) {
1185
0
      cf_log_err(cs, "CF_IDENT_ANY pair rule must provide a parse function");
1186
0
      return -1;
1187
0
    }
1188
1189
0
    while ((cp = cf_pair_find_next(cs, cp, NULL))) {
1190
0
      if (!always_parse && cf_item_is_parsed(cf_pair_to_item(cp))) continue;
1191
1192
0
      if (rule->func(ctx, data, base, cf_pair_to_item(cp), rule) < 0) return -1;
1193
0
      cf_item_mark_parsed(cf_pair_to_item(cp));
1194
0
    }
1195
0
    return 0;
1196
0
  }
1197
1198
  /*
1199
   *  Ignore this rule if it's a reference, as the
1200
   *  rules it points to have been pushed by the
1201
   *  above function.
1202
   */
1203
0
  if ((rule->flags & CONF_FLAG_REF) != 0) {
1204
0
    conf_parser_t const *rule_p;
1205
0
    uint8_t *sub_base = base;
1206
1207
0
    fr_assert(rule->subcs != NULL);
1208
1209
0
    sub_base += rule->offset;
1210
1211
0
    for (rule_p = rule->subcs; rule_p->name1; rule_p++) {
1212
0
      if (rule_p->flags & CONF_FLAG_DEPRECATED) continue; /* Skip deprecated */
1213
1214
0
      ret = cf_section_parse_rule(ctx, sub_base, cs, rule_p);
1215
0
      if (ret < 0) return ret;
1216
0
    }
1217
1218
    /*
1219
     *  Ensure we have a proper terminator, type so we catch
1220
     *  missing terminators reliably
1221
     */
1222
0
    fr_cond_assert(rule_p->type == conf_term.type);
1223
1224
0
    return 0;
1225
0
  }
1226
1227
  /*
1228
   *  Else it's a CONF_PAIR
1229
   */
1230
1231
  /*
1232
   *  Pair either needs an output destination or
1233
   *  there needs to be a function associated with
1234
   *  it.
1235
   */
1236
0
  if (!data && !rule->func) {
1237
0
    cf_log_err(cs, "Rule doesn't specify output destination");
1238
0
    return -1;
1239
0
  }
1240
1241
  /*
1242
   *  Get pointer to where we need to write out
1243
   *  whether the pointer was set.
1244
   */
1245
0
  if (rule->flags & CONF_FLAG_IS_SET) {
1246
0
    is_set = rule->data ? rule->is_set_ptr : ((uint8_t *)base) + rule->is_set_offset;
1247
0
  }
1248
1249
  /*
1250
   *  Parse the pair we found, or a default value.
1251
   */
1252
0
  ret = cf_pair_parse_internal(ctx, data, base, cs, rule);
1253
0
  switch (ret) {
1254
0
  case 1:   /* Used default (or not present) */
1255
0
    if (is_set) *is_set = false;
1256
0
    ret = 0;
1257
0
    break;
1258
1259
0
  case 0:   /* OK */
1260
0
    if (is_set) *is_set = true;
1261
0
    break;
1262
1263
0
  case -1:  /* Parse error */
1264
0
    break;
1265
1266
0
  case -2:  /* Deprecated CONF ITEM */
1267
0
    if (((rule + 1)->offset && ((rule + 1)->offset == rule->offset)) ||
1268
0
        ((rule + 1)->data && ((rule + 1)->data == rule->data))) {
1269
0
      cf_log_err(cs, "Replace \"%s\" with \"%s\"", rule->name1,
1270
0
           (rule + 1)->name1);
1271
0
    }
1272
0
    break;
1273
0
  }
1274
1275
0
  return ret;
1276
0
}
1277
1278
/** Parse a configuration section into user-supplied variables
1279
 *
1280
 * @param[in] ctx   to allocate any strings, or additional structures in.
1281
 *        Usually the same as base, unless base is a nested struct.
1282
 * @param[out] base   pointer to a struct to fill with data.
1283
 * @param[in] cs    to parse.
1284
 * @return
1285
 *  - 0 on success.
1286
 *  - -1 on general error.
1287
 *  - -2 if a deprecated #CONF_ITEM was found.
1288
 */
1289
int cf_section_parse(TALLOC_CTX *ctx, void *base, CONF_SECTION *cs)
1290
0
{
1291
0
  CONF_DATA const *rule_cd = NULL;
1292
1293
0
  if (!cs->name2) {
1294
0
    cf_log_debug(cs, "%.*s%s {", SECTION_SPACE(cs), parse_spaces, cs->name1);
1295
0
  } else {
1296
0
    cf_log_debug(cs, "%.*s%s %s {", SECTION_SPACE(cs), parse_spaces, cs->name1, cs->name2);
1297
0
  }
1298
1299
  /*
1300
   *  Loop over all the child rules of the section
1301
   */
1302
0
  while ((rule_cd = cf_data_find_next(cs, rule_cd, conf_parser_t, CF_IDENT_ANY))) {
1303
0
    int   ret;
1304
0
    conf_parser_t *rule;
1305
1306
0
    rule = cf_data_value(rule_cd);
1307
1308
0
    ret = cf_section_parse_rule(ctx, base, cs, rule);
1309
0
    if (ret < 0) return ret;
1310
0
  }
1311
1312
0
  cs->base = base;
1313
1314
  /*
1315
   *  Warn about items in the configuration which weren't
1316
   *  checked during parsing.
1317
   */
1318
0
  if (DEBUG_ENABLED4) cf_section_parse_warn(cs);
1319
1320
0
  cf_log_debug(cs, "%.*s}", SECTION_SPACE(cs), parse_spaces);
1321
1322
0
  cf_item_mark_parsed(cs);
1323
0
  return 0;
1324
0
}
1325
1326
/*
1327
 *  Pass2 fixups on tmpl_t
1328
 *
1329
 *  We don't have (or need yet) cf_pair_parse_pass2(), so we just
1330
 *  do it for tmpls.
1331
 */
1332
static int cf_parse_tmpl_pass2(UNUSED CONF_SECTION *cs, tmpl_t **out, CONF_PAIR *cp, fr_type_t type,
1333
             bool attribute, fr_dict_t const *dict_def)
1334
0
{
1335
0
  tmpl_t *vpt = *out;
1336
1337
0
  fr_assert(vpt);  /* We need something to resolve */
1338
1339
0
  if (tmpl_resolve(vpt, &(tmpl_res_rules_t){ .dict_def = dict_def, .force_dict_def = (dict_def != NULL)}) < 0) {
1340
0
    cf_log_perr(cp, "Failed processing configuration item '%s'", cp->attr);
1341
0
    return -1;
1342
0
  }
1343
1344
0
  if (attribute) {
1345
0
    if (!tmpl_is_attr(vpt)) {
1346
0
      cf_log_err(cp, "Expected attr got %s",
1347
0
           tmpl_type_to_str(vpt->type));
1348
0
      return -1;
1349
0
    }
1350
0
  }
1351
1352
0
  switch (vpt->type) {
1353
  /*
1354
   *  All attributes should have been defined by this point.
1355
   */
1356
0
  case TMPL_TYPE_ATTR_UNRESOLVED:
1357
0
    cf_log_err(cp, "Unknown attribute '%s'", tmpl_attr_tail_unresolved(vpt));
1358
0
    return -1;
1359
1360
0
  case TMPL_TYPE_DATA_UNRESOLVED:
1361
    /*
1362
     *  Try to realize the underlying type, if at all possible.
1363
     */
1364
0
    if (!attribute && type && (tmpl_cast_in_place(vpt, type, NULL) < 0)) {
1365
0
      cf_log_perr(cp, "Failed processing configuration item '%s'", cp->attr);
1366
0
      return -1;
1367
0
    }
1368
0
    break;
1369
1370
0
  case TMPL_TYPE_ATTR:
1371
0
  case TMPL_TYPE_DATA:
1372
0
  case TMPL_TYPE_EXEC:
1373
0
  case TMPL_TYPE_EXEC_UNRESOLVED:
1374
0
  case TMPL_TYPE_XLAT:
1375
0
  case TMPL_TYPE_XLAT_UNRESOLVED:
1376
0
    break;
1377
1378
0
  case TMPL_TYPE_UNINITIALISED:
1379
0
  case TMPL_TYPE_REGEX:
1380
0
  case TMPL_TYPE_REGEX_UNCOMPILED:
1381
0
  case TMPL_TYPE_REGEX_XLAT:
1382
0
  case TMPL_TYPE_REGEX_XLAT_UNRESOLVED:
1383
0
  case TMPL_TYPE_MAX:
1384
0
    fr_assert(0);
1385
    /* Don't add default */
1386
0
  }
1387
1388
0
  return 0;
1389
0
}
1390
1391
/** Fixup xlat expansions and attributes
1392
 *
1393
 * @param[out] base start of structure to write #tmpl_t s to.
1394
 * @param[in] cs CONF_SECTION to fixup.
1395
 * @return
1396
 *  - 0 on success.
1397
 *  - -1 on failure (parse errors etc...).
1398
 */
1399
int cf_section_parse_pass2(void *base, CONF_SECTION *cs)
1400
0
{
1401
0
  CONF_DATA const *rule_cd = NULL;
1402
1403
0
  while ((rule_cd = cf_data_find_next(cs, rule_cd, conf_parser_t, CF_IDENT_ANY))) {
1404
0
    bool      attribute, multi, is_tmpl, is_xlat;
1405
0
    CONF_PAIR   *cp;
1406
0
    conf_parser_t   *rule = cf_data_value(rule_cd);
1407
0
    void      *data;
1408
0
    fr_type_t   type = rule->type;
1409
0
    conf_parser_flags_t   flags = rule->flags;
1410
0
    fr_dict_t const   *dict = NULL;
1411
1412
0
    is_tmpl = (flags & CONF_FLAG_TMPL);
1413
0
    is_xlat = (flags & CONF_FLAG_XLAT);
1414
0
    attribute = (flags & CONF_FLAG_ATTRIBUTE);
1415
0
    multi = (flags & CONF_FLAG_MULTI);
1416
1417
    /*
1418
     *  It's a section, recurse!
1419
     */
1420
0
    if (flags & CONF_FLAG_SUBSECTION) {
1421
0
      uint8_t   *subcs_base;
1422
0
      CONF_SECTION  *subcs = cf_section_find(cs, rule->name1, rule->name2);
1423
1424
      /*
1425
       *  Select base by whether this is a nested struct,
1426
       *  or a pointer to another struct.
1427
       */
1428
0
      if (!base || (flags & CONF_FLAG_NO_OUTPUT)) {
1429
0
        subcs_base = NULL;
1430
0
      } else if (multi) {
1431
0
        size_t    j, len;
1432
0
        uint8_t   **array;
1433
1434
0
        array = *(uint8_t ***)(((uint8_t *)base) + rule->offset);
1435
0
        len = talloc_array_length(array);
1436
1437
0
        for (j = 0; j < len; j++) if (cf_section_parse_pass2(array[j], subcs) < 0) return -1;
1438
0
        continue;
1439
0
      } else {
1440
0
        subcs_base = (uint8_t *)base + rule->offset;
1441
0
      }
1442
1443
0
      if (cf_section_parse_pass2(subcs_base, subcs) < 0) return -1;
1444
1445
0
      continue;
1446
0
    }
1447
1448
    /*
1449
     *  Find the CONF_PAIR, may still not exist if there was
1450
     *  no default set for the conf_parser_t.
1451
     */
1452
0
    cp = cf_pair_find(cs, rule->name1);
1453
0
    if (!cp) continue;
1454
1455
    /*
1456
     *  Figure out which data we need to fix.
1457
     */
1458
0
    data = rule->data; /* prefer this. */
1459
0
    if (!data && base && !(rule->flags & CONF_FLAG_NO_OUTPUT)) data = ((char *)base) + rule->offset;
1460
0
    if (!data) continue;
1461
1462
    /*
1463
     *  Non-xlat expansions shouldn't have xlat!
1464
     *
1465
     *  Except other libraries like libkafka may be the ones
1466
     *  doing the actual expansion, so we don't _know_
1467
     *  if the xlatlike value is destined for use in FreeRADIUS
1468
     *  or not, so we can't definitely determine if this is an
1469
     *  error.
1470
     *
1471
     *  Code left in place to warn other people off re-adding
1472
     *  this check in future.
1473
     */
1474
#if 0
1475
    if (!is_xlat && !is_tmpl) {
1476
      /*
1477
       *  Ignore %{... in shared secrets.
1478
       *  They're never dynamically expanded.
1479
       */
1480
      if ((rule->flags & CONF_FLAG_SECRET) != 0) continue;
1481
1482
      if (strstr(cp->value, "%{") != NULL) {
1483
        cf_log_err(cp, "Found dynamic expansion in string which "
1484
             "will not be dynamically expanded");
1485
        return -1;
1486
      }
1487
      continue;
1488
    }
1489
#endif
1490
1491
    /*
1492
     *  Search for dictionary data somewhere in the virtual
1493
     *      server.
1494
     */
1495
0
    dict = virtual_server_dict_by_child_ci(cf_section_to_item(cs));
1496
1497
    /*
1498
     *  Parse (and throw away) the xlat string (for validation).
1499
     *
1500
     *  FIXME: All of these should be converted from CONF_FLAG_XLAT
1501
     *  to CONF_FLAG_TMPL.
1502
     */
1503
0
    if (is_xlat) {
1504
0
      fr_slen_t slen;
1505
0
      xlat_exp_head_t *xlat;
1506
1507
0
    redo:
1508
0
      xlat = NULL;
1509
1510
      /*
1511
       *  xlat expansions should be parseable.
1512
       */
1513
0
      slen = xlat_tokenize(cs, &xlat,
1514
0
               &FR_SBUFF_IN(cp->value, talloc_strlen(cp->value)), NULL,
1515
0
               &(tmpl_rules_t) {
1516
0
                 .attr = {
1517
0
                   .dict_def = dict,
1518
0
                   .list_def = request_attr_request,
1519
0
                   .allow_unknown = false,
1520
0
                   .allow_unresolved = false,
1521
0
                   .allow_foreign = (dict == NULL)
1522
0
                 },
1523
0
               });
1524
0
      if (slen < 0) {
1525
0
        char *spaces, *text;
1526
1527
0
        fr_canonicalize_error(cs, &spaces, &text, slen, cp->value);
1528
1529
0
        cf_log_err(cp, "Failed parsing expansion string:");
1530
0
        cf_log_err(cp, "%s", text);
1531
0
        cf_log_perr(cp, "%s^", spaces);
1532
1533
0
        talloc_free(spaces);
1534
0
        talloc_free(text);
1535
0
        talloc_free(xlat);
1536
0
        return -1;
1537
0
      }
1538
1539
0
      talloc_free(xlat);
1540
1541
      /*
1542
       *  If the "multi" flag is set, check all of them.
1543
       */
1544
0
      if (multi) {
1545
0
        cp = cf_pair_find_next(cs, cp, cp->attr);
1546
0
        if (cp) goto redo;
1547
0
      }
1548
0
      continue;
1549
1550
    /*
1551
     *  Parse the pair into a template
1552
     */
1553
0
    } else if (is_tmpl && !multi) {
1554
0
      if (cf_parse_tmpl_pass2(cs, (tmpl_t **)data, cp, type, attribute, dict) < 0) {
1555
0
        return -1;
1556
0
      }
1557
1558
0
    } else if (is_tmpl) {
1559
0
      size_t i;
1560
0
      char const *name = cp->attr;
1561
0
      tmpl_t **array = *(tmpl_t ***) data;
1562
1563
0
      for (i = 0; i < talloc_array_length(array); i++, cp = cf_pair_find_next(cs, cp, name)) {
1564
0
        if (!cp) break;
1565
1566
0
        if (cf_parse_tmpl_pass2(cs, &array[i], cp, type, attribute, dict) < 0) {
1567
0
          return -1;
1568
0
        }
1569
0
      }
1570
0
    }
1571
0
  }
1572
1573
0
  return 0;
1574
0
}
1575
1576
1577
/** Add a single rule to a #CONF_SECTION
1578
 *
1579
 * @param[in] cs  to add rules to.
1580
 * @param[in] rule  to add.
1581
 * @param[in] filename  where the rule was pushed.
1582
 * @param[in] lineno  where the rule was pushed.
1583
 * @return
1584
 *  - 0 on success.
1585
 *  - -1 if the rules added conflict.
1586
 */
1587
int _cf_section_rule_push(CONF_SECTION *cs, conf_parser_t const *rule, char const *filename, int lineno)
1588
0
{
1589
0
  char const *name1, *name2;
1590
1591
0
  if (!cs || !rule) return 0;
1592
1593
0
  name1 = rule->name1 == CF_IDENT_ANY ? "__any__" : rule->name1;
1594
0
  name2 = rule->name2 == CF_IDENT_ANY ? "__any__" : rule->name2;
1595
1596
0
  if (DEBUG_ENABLED4) {
1597
0
    cf_log_debug(cs, "Pushed parse rule to %s section: %s %s",
1598
0
           cf_section_name1(cs),
1599
0
           name1, rule->flags & CONF_FLAG_SUBSECTION ? "{}": "");
1600
0
  }
1601
1602
  /*
1603
   *  Qualifying with name prevents duplicate rules being added
1604
   *
1605
   *  Fixme maybe?.. Can't have a section and pair with the same name.
1606
   */
1607
0
  if (!_cf_data_add_static(CF_TO_ITEM(cs), rule, "conf_parser_t", name1, filename, lineno)) {
1608
0
    CONF_DATA const *cd;
1609
0
    conf_parser_t *old;
1610
1611
0
    cd = cf_data_find(CF_TO_ITEM(cs), conf_parser_t, name1);
1612
0
    old = cf_data_value(cd);
1613
0
    fr_assert(old != NULL);
1614
1615
    /*
1616
     *  Shut up about duplicates.
1617
     */
1618
0
    if (memcmp(rule, old, sizeof(*rule)) == 0) {
1619
0
      return 0;
1620
0
    }
1621
1622
    /*
1623
     *  Remove any ON_READ callbacks, and add the new
1624
     *  rule in its place.
1625
     */
1626
0
    if (old->on_read) {
1627
0
      CONF_DATA *cd1;
1628
1629
      /*
1630
       *  Over-write the rule in place.
1631
       *
1632
       *  We'd like to call cf_item_remove(), but
1633
       *  that apparently doesn't work for
1634
       *  CONF_DATA.  We don't need to
1635
       *  free/alloc one, so re-using this is
1636
       *  fine.
1637
       */
1638
0
      memcpy(&cd1, &cd, sizeof(cd1));
1639
0
      cd1->data = rule;
1640
0
      cd1->item.filename = filename;
1641
0
      cd1->item.lineno = lineno;
1642
0
      return 0;
1643
0
    }
1644
1645
    /*
1646
     *  If we have a duplicate sub-section, just
1647
     *  recurse and add the new sub-rules to the
1648
     *  existing sub-section.
1649
     */
1650
0
    if (rule->flags & CONF_FLAG_SUBSECTION) {
1651
0
      CONF_SECTION *subcs;
1652
1653
0
      subcs = cf_section_find(cs, name1, name2);
1654
0
      if (!subcs) {
1655
0
        if ((rule->flags & CONF_FLAG_OK_MISSING) != 0) return 0;
1656
1657
0
        if (!name2 || (name2 == CF_IDENT_ANY)) {
1658
0
          cf_log_err(cs, "Failed finding '%s' subsection", name1);
1659
0
        } else {
1660
0
          cf_log_err(cs, "Failed finding '%s %s' subsection", name1, name2);
1661
0
        }
1662
1663
0
        cf_item_debug(cs);
1664
0
        return -1;
1665
0
      }
1666
1667
      /*
1668
       *  The old rules were delayed until we pushed a matching subsection which is actually used.
1669
       */
1670
0
      if ((old->flags & CONF_FLAG_OPTIONAL) != 0) {
1671
0
        if (cf_section_rules_push(subcs, old->subcs) < 0) return -1;
1672
0
      }
1673
1674
0
      return cf_section_rules_push(subcs, rule->subcs);
1675
0
    }
1676
1677
0
    cf_log_err(cs, "Data of type %s with name \"%s\" already exists. "
1678
0
               "Existing data added %s[%i]", "conf_parser_t",
1679
0
         name1, cd->item.filename, cd->item.lineno);
1680
1681
0
    cf_item_debug(cs);
1682
0
    return -1;
1683
0
  }
1684
1685
0
  return 0;
1686
0
}
1687
1688
/** Add an array of parse rules to a #CONF_SECTION
1689
 *
1690
 * @param[in] cs  to add rules to.
1691
 * @param[in] rules to add.  Last element should have NULL name field.
1692
 * @param[in] filename  where the rule was pushed.
1693
 * @param[in] lineno  where the rule was pushed.
1694
 * @return
1695
 *  - 0 on success.
1696
 *  - -1 on failure.
1697
 */
1698
int _cf_section_rules_push(CONF_SECTION *cs, conf_parser_t const *rules, char const *filename, int lineno)
1699
{
1700
  conf_parser_t const *rule_p;
1701
1702
  if (!cs || !rules) return 0;
1703
1704
  for (rule_p = rules; rule_p->name1; rule_p++) {
1705
    if (rule_p->flags & CONF_FLAG_DEPRECATED) continue; /* Skip deprecated */
1706
    if (_cf_section_rule_push(cs, rule_p, filename, lineno) < 0) return -1;
1707
  }
1708
1709
  /*
1710
   *  Ensure we have a proper terminator, type so we catch
1711
   *  missing terminators reliably
1712
   */
1713
  fr_cond_assert(rule_p->type == conf_term.type);
1714
1715
  return 0;
1716
}
1717
1718
/** Generic function for parsing conf pair values as int
1719
 *
1720
 * @note This should be used for enum types as c99 6.4.4.3 states that the enumeration
1721
 * constants are of type int.
1722
 *
1723
 */
1724
int cf_table_parse_int(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
1725
           CONF_ITEM *ci, conf_parser_t const *rule)
1726
0
{
1727
0
  int32_t       num;
1728
0
  cf_table_parse_ctx_t const  *parse_ctx = rule->uctx;
1729
1730
0
  if (cf_pair_in_table(&num, parse_ctx->table, *parse_ctx->len, cf_item_to_pair(ci)) < 0) return -1;
1731
1732
0
  *((int *)out) = num;
1733
1734
0
  return 0;
1735
0
}
1736
1737
/** Generic function for parsing conf pair values as int32_t (FR_TYPE_INT32)
1738
 *
1739
 */
1740
int cf_table_parse_int32(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
1741
       CONF_ITEM *ci, conf_parser_t const *rule)
1742
0
{
1743
0
  int32_t       num;
1744
0
  cf_table_parse_ctx_t const  *parse_ctx = rule->uctx;
1745
1746
0
  if (cf_pair_in_table(&num, parse_ctx->table, *parse_ctx->len, cf_item_to_pair(ci)) < 0) return -1;
1747
1748
0
  *((int32_t *)out) = num;
1749
1750
0
  return 0;
1751
0
}
1752
1753
/** Generic function for parsing conf pair values as int32_t (FR_TYPE_UINT32)
1754
 *
1755
 */
1756
int cf_table_parse_uint32(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
1757
        CONF_ITEM *ci, conf_parser_t const *rule)
1758
0
{
1759
0
  int32_t       num;
1760
0
  cf_table_parse_ctx_t const  *parse_ctx = rule->uctx;
1761
1762
0
  if (cf_pair_in_table(&num, parse_ctx->table, *parse_ctx->len, cf_item_to_pair(ci)) < 0) return -1;
1763
0
  if (num < 0) {
1764
0
    cf_log_err(ci, "Resolved value must be a positive integer, got %i", num);
1765
0
    return -1;
1766
0
  }
1767
0
  *((uint32_t *)out) = (uint32_t)num;
1768
1769
0
  return 0;
1770
0
}
1771
1772
/** Generic function for resolving UID strings to uid_t values
1773
 *
1774
 * Type should be FR_TYPE_VOID, struct field should be a uid_t.
1775
 */
1776
int cf_parse_uid(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
1777
     CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
1778
0
{
1779
0
  if (fr_perm_uid_from_str(ctx, (uid_t *)out, cf_pair_value(cf_item_to_pair(ci))) < 0) {
1780
0
    cf_log_perr(ci, "Failed resolving UID");
1781
0
    return -1;
1782
0
  }
1783
1784
0
  return 0;
1785
0
}
1786
1787
/** Generic function for resolving GID strings to uid_t values
1788
 *
1789
 * Type should be FR_TYPE_VOID, struct field should be a gid_t.
1790
 */
1791
int cf_parse_gid(TALLOC_CTX *ctx, void *out, UNUSED void *parent,
1792
     CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
1793
0
{
1794
0
  if (fr_perm_gid_from_str(ctx, (gid_t *)out, cf_pair_value(cf_item_to_pair(ci))) < 0) {
1795
0
    cf_log_perr(ci, "Failed resolving GID");
1796
0
    return -1;
1797
0
  }
1798
1799
0
  return 0;
1800
0
}
1801
1802
/** Generic function for resolving permissions to a mode-t
1803
 *
1804
 * Type should be FR_TYPE_VOID, struct field should be a gid_t.
1805
 */
1806
int cf_parse_permissions(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
1807
       CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
1808
0
{
1809
0
  mode_t mode;
1810
0
  char const *name = cf_pair_value(cf_item_to_pair(ci));
1811
1812
0
  if (fr_perm_mode_from_str(&mode, name) < 0) {
1813
0
    cf_log_perr(ci, "Invalid permissions string");
1814
0
    return -1;
1815
0
  }
1816
1817
0
  *(mode_t *) out = mode;
1818
1819
0
  return 0;
1820
0
}
1821
1822
/** NULL callback for sections
1823
 *
1824
 *  This callback exists only as a place-holder to ensure that the
1825
 *  nested on_read functions are called.  The conf file routines won't
1826
 *  recurse into every conf_parser_t section to check if there's an
1827
 *  "on_read" callback.  So this place-holder is a signal to do that.
1828
 *
1829
 * @param[in] ctx to allocate data in.
1830
 * @param[out] out  Unused
1831
 * @param[in] parent  Base structure address.
1832
 * @param[in] ci  #CONF_SECTION containing the current section.
1833
 * @param[in] rule  unused.
1834
 * @return
1835
 *  - 0 on success.
1836
 *  - -1 on failure.
1837
 */
1838
int cf_null_on_read(UNUSED TALLOC_CTX *ctx, UNUSED void *out, UNUSED void *parent,
1839
        UNUSED CONF_ITEM *ci, UNUSED conf_parser_t const *rule)
1840
0
{
1841
0
  return 0;
1842
0
}
1843
1844
/** Bit-pos indexed table of `CONF_FLAG_*` names.
1845
 *
1846
 * Index is `fr_high_bit_pos()` of the flag value, which matches the
1847
 * `_Generic` dispatch on `fr_table_num_indexed_bit_pos_t` in
1848
 * `fr_table_str_by_value`.
1849
 */
1850
static fr_table_num_indexed_bit_pos_t const cf_parser_flag_table[] = {
1851
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_SUBSECTION),
1852
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_DEPRECATED),
1853
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_REQUIRED),
1854
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_ATTRIBUTE),
1855
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_SECRET),
1856
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_FILE_READABLE),
1857
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_FILE_WRITABLE),
1858
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_FILE_SOCKET),
1859
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_FILE_EXISTS),
1860
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_XLAT),
1861
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_TMPL),
1862
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_MULTI),
1863
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_NOT_EMPTY),
1864
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_IS_SET),
1865
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_OK_MISSING),
1866
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_HIDDEN),
1867
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_REF),
1868
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_OPTIONAL),
1869
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_ALWAYS_PARSE),
1870
  FR_TABLE_INDEXED_BIT_POS_ENTRY(CONF_FLAG_NO_OUTPUT),
1871
};
1872
static size_t cf_parser_flag_table_len = NUM_ELEMENTS(cf_parser_flag_table);
1873
1874
char const *cf_parser_flag_to_enum_str(conf_parser_flags_t mask)
1875
0
{
1876
0
  return fr_table_str_by_value(cf_parser_flag_table, mask, NULL);
1877
0
}