The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
cf_file.c
Go to the documentation of this file.
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: a1ee38f160ea7d6a8d1862baa87205fb54fc6d91 $
19 * @file cf_file.c
20 * @brief Read the radiusd.conf file.
21 *
22 * @note Yep I should learn to use lex & yacc, or at least
23 * write a decent parser. I know how to do that, really :)
24 * miquels@cistron.nl
25 *
26 * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
27 * @copyright 2000,2006 The FreeRADIUS server project
28 * @copyright 2000 Miquel van Smoorenburg (miquels@cistron.nl)
29 * @copyright 2000 Alan DeKok (aland@freeradius.org)
30 */
31RCSID("$Id: a1ee38f160ea7d6a8d1862baa87205fb54fc6d91 $")
32
33#include <sys/errno.h>
34
35#include <freeradius-devel/server/cf_file.h>
36#include <freeradius-devel/server/cf_priv.h>
37#include <freeradius-devel/server/cf_util.h>
38#include <freeradius-devel/server/log.h>
39#include <freeradius-devel/server/tmpl.h>
40#include <freeradius-devel/server/util.h>
41#include <freeradius-devel/server/virtual_servers.h>
42#include <freeradius-devel/util/debug.h>
43#include <freeradius-devel/util/file.h>
44#include <freeradius-devel/util/misc.h>
45#include <freeradius-devel/util/perm.h>
46#include <freeradius-devel/util/strerror.h>
47#include <freeradius-devel/util/skip.h>
48#include <freeradius-devel/util/md5.h>
49
50#ifdef HAVE_DIRENT_H
51# include <dirent.h>
52#endif
53
54#ifdef HAVE_GLOB_H
55# include <glob.h>
56#endif
57
58#ifdef HAVE_SYS_STAT_H
59# include <sys/stat.h>
60#endif
61
62#include <fcntl.h>
63
64#include <freeradius-devel/server/main_config.h>
65
66bool check_config = false;
67static uid_t conf_check_uid = (uid_t)-1;
68static gid_t conf_check_gid = (gid_t)-1;
69
75
77 { L("instance"), CONF_PROPERTY_INSTANCE },
78 { L("name"), CONF_PROPERTY_NAME }
79};
81
83 { L("accounting"), true },
84 { L("add"), true },
85 { L("authenticate"), true },
86 { L("clear"), true },
87 { L("deny"), true },
88 { L("error"), true },
89 { L("establish"), true },
90 { L("finally"), true },
91 { L("load"), true },
92 { L("new"), true },
93 { L("recv"), true },
94 { L("send"), true },
95 { L("store"), true },
96 { L("verify"), true },
97};
99
100typedef enum {
102#ifdef HAVE_DIRENT_H
103 CF_STACK_DIR,
104#endif
105#ifdef HAVE_GLOB_H
106 CF_STACK_GLOB
107#endif
109
110#define MAX_STACK (32)
111typedef struct {
113
114 char const *filename; //!< filename we're reading
115 int lineno; //!< line in that filename
116
117 union {
118 struct {
119 FILE *fp; //!< FP we're reading
120 };
121
122#ifdef HAVE_DIRENT_H
123 struct {
124 fr_heap_t *heap; //!< sorted heap of files
125 char *directory; //!< directory name we're reading
126 };
127#endif
128
129#ifdef HAVE_GLOB_H
130 struct {
131 size_t gl_current;
132 glob_t glob; //! reading glob()
133 bool required;
134 };
135#endif
136 };
137
138 CONF_SECTION *parent; //!< which started this file
139 CONF_SECTION *current; //!< sub-section we're reading
140 CONF_SECTION *at_reference; //!< was this thing an @foo ?
141
143 bool from_dir; //!< this file was read from $include foo/
145
146/*
147 * buff[0] is the data we read from the file
148 * buff[1] is name
149 * buff[2] is name2 OR value for pair
150 * buff[3] is a temporary buffer
151 */
152typedef struct {
153 char **buff; //!< buffers for reading / parsing
154 size_t bufsize; //!< size of the buffers
155 int depth; //!< stack depth
156 char const *ptr; //!< current parse pointer
157 char *fill; //!< where we start filling the buffer from
158 cf_stack_frame_t frame[MAX_STACK]; //!< stack frames
159} cf_stack_t;
160
161
162static inline CC_HINT(always_inline) int cf_tmpl_rules_verify(CONF_SECTION *cs, tmpl_rules_t const *rules)
163{
164 if (cf_section_find_parent(cs, "policy", NULL)) {
165 if (!fr_cond_assert_msg(!rules->attr.dict_def || (rules->attr.dict_def == fr_dict_internal()),
166 "Protocol dictionary must be NULL not %s",
167 fr_dict_root(rules->attr.dict_def)->name)) return -1;
168
169 } else {
170 if (!fr_cond_assert_msg(rules->attr.dict_def, "No protocol dictionary set")) return -1;
171 if (!fr_cond_assert_msg(rules->attr.dict_def != fr_dict_internal(), "rules->attr.dict_def must not be the internal dictionary")) return -1;
172 }
173
174 if (!fr_cond_assert_msg(!rules->attr.allow_foreign, "rules->allow_foreign must be false")) return -1;
175 if (!fr_cond_assert_msg(!rules->at_runtime, "rules->at_runtime must be false")) return -1;
176
177 return 0;
178}
179
180#define RULES_VERIFY(_cs, _rules) if (cf_tmpl_rules_verify(_cs, _rules) < 0) return NULL
181
182/*
183 * Expand the variables in an input string.
184 *
185 * Input and output should be two different buffers, as the
186 * output may be longer than the input.
187 */
188char const *cf_expand_variables(char const *cf, int lineno,
189 CONF_SECTION *outer_cs,
190 char *output, size_t outsize,
191 char const *input, ssize_t inlen, bool *soft_fail)
192{
193 char *p;
194 char const *end, *next, *ptr;
195 CONF_SECTION const *parent_cs;
196 char name[8192];
197
198 if (soft_fail) *soft_fail = false;
199
200 /*
201 * Find the master parent conf section.
202 * We can't use main_config->root_cs, because we're in the
203 * process of re-building it, and it isn't set up yet...
204 */
205 parent_cs = cf_root(outer_cs);
206
207 p = output;
208 ptr = input;
209
210 if (inlen < 0) {
211 end = NULL;
212 } else {
213 end = input + inlen;
214 }
215
216 /*
217 * Note that this CAN go over "end" if the input string
218 * is malformed. e.g. pass "${foo.bar}", and pass
219 * "inlen=5". Well, too bad.
220 */
221 while (*ptr && (!end || (ptr < end))) {
222 /*
223 * Ignore anything other than "${"
224 */
225 if ((*ptr == '$') && (ptr[1] == '{')) {
226 CONF_ITEM *ci;
227 CONF_PAIR *cp;
228 char *q;
229 ssize_t len;
230
231 len = fr_skip_xlat(ptr, end);
232 if (len <= 0) {
233 ERROR("%s[%d]: Failed parsing variable expansion '%s''",
234 cf, lineno, input);
235 return NULL;
236 }
237
238 next = ptr + len;
239 ptr += 2;
240
241 /*
242 * Can't really happen because input lines are
243 * capped at 8k, which is sizeof(name)
244 */
245 if ((size_t) len >= sizeof(name)) {
246 ERROR("%s[%d]: Reference string is too large",
247 cf, lineno);
248 return NULL;
249 }
250
251 memcpy(name, ptr, len - 3);
252 name[len - 3] = '\0';
253
254 /*
255 * Read configuration value from a file.
256 *
257 * Note that this is "read binary data", and the contents aren't stripped of
258 * CRLF.
259 */
260 if (name[0] == '/') {
261 int fd = open(name, O_RDONLY);
262 struct stat buf;
263
264 if (fd < 0) {
265 ERROR("%s[%d]: Reference \"${%s}\" failed opening file - %s", cf, lineno, name, fr_syserror(errno));
266 return NULL;
267 }
268
269 if (fstat(fd, &buf) < 0) {
270 fail_fd:
271 close(fd);
272 ERROR("%s[%d]: Reference \"${%s}\" failed reading file - %s", cf, lineno, name, fr_syserror(errno));
273 return NULL;
274 }
275
276 if (buf.st_size >= ((output + outsize) - p)) {
277 close(fd);
278 ERROR("%s[%d]: Reference \"${%s}\" file is too large (%zu >= %zu)", cf, lineno, name,
279 (size_t) buf.st_size, (size_t) ((output + outsize) - p));
280 return NULL;
281 }
282
283 len = read(fd, p, (output + outsize) - p);
284 if (len < 0) goto fail_fd;
285
286 close(fd);
287 p += len;
288 *p = '\0';
289 ptr = next;
290 goto check_eos;
291 }
292
293 q = strchr(name, ':');
294 if (q) {
295 *(q++) = '\0';
296 }
297
298 ci = cf_reference_item(parent_cs, outer_cs, name);
299 if (!ci) {
300 if (soft_fail) *soft_fail = true;
301 PERROR("%s[%d]: Failed finding reference \"${%s}\"", cf, lineno, name);
302 return NULL;
303 }
304
305 /*
306 * The expansion doesn't refer to another item or section
307 * it's the property of a section.
308 */
309 if (q) {
311
312 if (ci->type != CONF_ITEM_SECTION) {
313 ERROR("%s[%d]: Can only reference properties of sections", cf, lineno);
314 return NULL;
315 }
316
319 strcpy(p, find->name1);
320 break;
321
323 strcpy(p, find->name2 ? find->name2 : find->name1);
324 break;
325
326 default:
327 ERROR("%s[%d]: Invalid property '%s'", cf, lineno, q);
328 return NULL;
329 }
330 p += strlen(p);
331 ptr = next;
332
333 } else if (ci->type == CONF_ITEM_PAIR) {
334 /*
335 * Substitute the value of the variable.
336 */
337 cp = cf_item_to_pair(ci);
338
339 /*
340 * If the thing we reference is
341 * marked up as being expanded in
342 * pass2, don't expand it now.
343 * Let it be expanded in pass2.
344 */
345 if (cp->pass2) {
346 if (soft_fail) *soft_fail = true;
347
348 ERROR("%s[%d]: Reference \"%s\" points to a variable which has not been expanded.",
349 cf, lineno, input);
350 return NULL;
351 }
352
353 if (!cp->value) {
354 ERROR("%s[%d]: Reference \"%s\" has no value",
355 cf, lineno, input);
356 return NULL;
357 }
358
359 if (p + strlen(cp->value) >= output + outsize) {
360 ERROR("%s[%d]: Reference \"%s\" is too long",
361 cf, lineno, input);
362 return NULL;
363 }
364
365 strcpy(p, cp->value);
366 p += strlen(p);
367 ptr = next;
368
369 } else if (ci->type == CONF_ITEM_SECTION) {
370 CONF_SECTION *subcs;
371
372 /*
373 * Adding an entry again to a
374 * section is wrong. We don't
375 * want an infinite loop.
376 */
377 if (cf_item_to_section(ci->parent) == outer_cs) {
378 ERROR("%s[%d]: Cannot reference different item in same section", cf, lineno);
379 return NULL;
380 }
381
382 /*
383 * Copy the section instead of
384 * referencing it.
385 */
386 subcs = cf_item_to_section(ci);
387 subcs = cf_section_dup(outer_cs, outer_cs, subcs,
388 cf_section_name1(subcs), cf_section_name2(subcs),
389 false);
390 if (!subcs) {
391 ERROR("%s[%d]: Failed copying reference %s", cf, lineno, name);
392 return NULL;
393 }
394
395 cf_filename_set(subcs, ci->filename);
396 cf_lineno_set(subcs, ci->lineno);
397
398 ptr = next;
399
400 } else {
401 ERROR("%s[%d]: Reference \"%s\" type is invalid", cf, lineno, input);
402 return NULL;
403 }
404 } else if (strncmp(ptr, "$ENV{", 5) == 0) {
405 char *env;
406
407 ptr += 5;
408
409 /*
410 * Look for trailing '}', and log a
411 * warning for anything that doesn't match,
412 * and exit with a fatal error.
413 */
414 next = strchr(ptr, '}');
415 if (next == NULL) {
416 *p = '\0';
417 ERROR("%s[%d]: Environment variable expansion missing }",
418 cf, lineno);
419 return NULL;
420 }
421
422 /*
423 * Can't really happen because input lines are
424 * capped at 8k, which is sizeof(name)
425 */
426 if ((size_t) (next - ptr) >= sizeof(name)) {
427 ERROR("%s[%d]: Environment variable name is too large",
428 cf, lineno);
429 return NULL;
430 }
431
432 memcpy(name, ptr, next - ptr);
433 name[next - ptr] = '\0';
434
435 /*
436 * Get the environment variable.
437 * If none exists, then make it an empty string.
438 */
439 env = getenv(name);
440 if (env == NULL) {
441 *name = '\0';
442 env = name;
443 }
444
445 if (p + strlen(env) >= output + outsize) {
446 ERROR("%s[%d]: Reference \"%s\" is too long",
447 cf, lineno, input);
448 return NULL;
449 }
450
451 strcpy(p, env);
452 p += strlen(p);
453 ptr = next + 1;
454
455 } else {
456 /*
457 * Copy it over verbatim.
458 */
459 *(p++) = *(ptr++);
460 }
461
462 check_eos:
463 if (p >= (output + outsize)) {
464 ERROR("%s[%d]: Reference \"%s\" is too long",
465 cf, lineno, input);
466 return NULL;
467 }
468 } /* loop over all of the input string. */
469
470 *p = '\0';
471
472 return output;
473}
474
475/*
476 * Merge the template so everything else "just works".
477 */
478static bool cf_template_merge(CONF_SECTION *cs, CONF_SECTION const *template)
479{
480 if (!cs || !template) return true;
481
482 cs->template = NULL;
483
484 /*
485 * Walk over the template, adding its' entries to the
486 * current section. But only if the entries don't
487 * already exist in the current section.
488 */
489 cf_item_foreach(&template->item, ci) {
490 if (ci->type == CONF_ITEM_PAIR) {
491 CONF_PAIR *cp1, *cp2;
492
493 /*
494 * It exists, don't over-write it.
495 */
496 cp1 = cf_item_to_pair(ci);
497 if (cf_pair_find(cs, cp1->attr)) {
498 continue;
499 }
500
501 /*
502 * Create a new pair with all of the data
503 * of the old one.
504 */
505 cp2 = cf_pair_dup(cs, cp1, true);
506 if (!cp2) return false;
507
508 cf_filename_set(cp2, cp1->item.filename);
509 cf_lineno_set(cp2, cp1->item.lineno);
510 continue;
511 }
512
513 if (ci->type == CONF_ITEM_SECTION) {
514 CONF_SECTION *subcs1, *subcs2;
515
516 subcs1 = cf_item_to_section(ci);
517 fr_assert(subcs1 != NULL);
518
519 subcs2 = cf_section_find(cs, subcs1->name1, subcs1->name2);
520 if (subcs2) {
521 /*
522 * sub-sections get merged.
523 */
524 if (!cf_template_merge(subcs2, subcs1)) {
525 return false;
526 }
527 continue;
528 }
529
530 /*
531 * Our section doesn't have a matching
532 * sub-section. Copy it verbatim from
533 * the template.
534 */
535 subcs2 = cf_section_dup(cs, cs, subcs1,
536 cf_section_name1(subcs1), cf_section_name2(subcs1),
537 false);
538 if (!subcs2) return false;
539
540 cf_filename_set(subcs2, subcs1->item.filename);
541 cf_lineno_set(subcs2, subcs1->item.lineno);
542 continue;
543 }
544
545 /* ignore everything else */
546 }
547
548 return true;
549}
550
551/*
552 * Functions for tracking files by inode
553 */
554static int8_t _inode_cmp(void const *one, void const *two)
555{
556 cf_file_t const *a = one, *b = two;
557
558 CMP_RETURN(a, b, buf.st_dev);
559
560 return CMP(a->buf.st_ino, b->buf.st_ino);
561}
562
563static int cf_file_open(CONF_SECTION *cs, char const *filename, bool from_dir, FILE **fp_p)
564{
566 CONF_SECTION *top;
567 fr_rb_tree_t *tree;
568 int fd = -1;
569 FILE *fp;
570
571 top = cf_root(cs);
572 tree = cf_data_value(cf_data_find(top, fr_rb_tree_t, "filename"));
573 fr_assert(tree);
574
575 /*
576 * If we're including a wildcard directory, then ignore
577 * any files the users has already explicitly loaded in
578 * that directory.
579 */
580 if (from_dir) {
581 cf_file_t my_file;
582 char const *r;
583 int my_fd;
584
585 my_file.cs = cs;
586 my_file.filename = filename;
587
588 /*
589 * Find and open the directory containing filename so we can use
590 * the "at"functions to avoid time of check/time of use insecurities.
591 */
592 if (fr_dirfd(&my_fd, &r, filename) < 0) {
593 ERROR("Failed to open directory containing %s", filename);
594 return -1;
595 }
596
597 if (fstatat(my_fd, r, &my_file.buf, 0) < 0) goto error;
598
599 file = fr_rb_find(tree, &my_file);
600
601 /*
602 * The file was previously read by including it
603 * explicitly. After it was read, we have a
604 * $INCLUDE of the directory it is in. In that
605 * case, we ignore the file.
606 *
607 * However, if the file WAS read from a wildcard
608 * $INCLUDE directory, then we read it again.
609 */
610 if (file && !file->from_dir) {
611 if (my_fd != AT_FDCWD) close(my_fd);
612 return 1;
613 }
614 fd = openat(my_fd, r, O_RDONLY, 0);
615 fp = (fd < 0) ? NULL : fdopen(fd, "r");
616 if (my_fd != AT_FDCWD) close(my_fd);
617 } else {
618 fp = fopen(filename, "r");
619 if (fp) fd = fileno(fp);
620 }
621
622 DEBUG2("including configuration file %s", filename);
623
624 if (!fp) {
625 error:
626 ERROR("Unable to open file \"%s\": %s", filename, fr_syserror(errno));
627 return -1;
628 }
629
630 MEM(file = talloc(tree, cf_file_t));
631
632 file->filename = talloc_strdup(file, filename); /* The rest of the code expects this to be a talloced buffer */
633 file->cs = cs;
634 file->from_dir = from_dir;
635
636 if (fstat(fd, &file->buf) == 0) {
637#ifdef S_IWOTH
638 if ((file->buf.st_mode & S_IWOTH) != 0) {
639 ERROR("Configuration file %s is globally writable. "
640 "Refusing to start due to insecure configuration.", filename);
641
642 fclose(fp);
644 return -1;
645 }
646#endif
647 }
648
649 /*
650 * We can include the same file twice. e.g. when it
651 * contains common definitions, such as for SQL.
652 *
653 * Though the admin should really use templates for that.
654 */
655 if (!fr_rb_insert(tree, file)) talloc_free(file);
656
657 *fp_p = fp;
658 return 0;
659}
660
661/** Set the euid/egid used when performing file checks
662 *
663 * Sets the euid, and egid used when cf_file_check is called to check
664 * permissions on conf items of type #CONF_FLAG_FILE_READABLE
665 *
666 * @note This is probably only useful for the freeradius daemon itself.
667 *
668 * @param uid to set, (uid_t)-1 to use current euid.
669 * @param gid to set, (gid_t)-1 to use current egid.
670 */
671void cf_file_check_set_uid_gid(uid_t uid, gid_t gid)
672{
673 if (uid != 0) conf_check_uid = uid;
674 if (gid != 0) conf_check_gid = gid;
675}
676
677/** Perform an operation with the effect/group set to conf_check_gid and conf_check_uid
678 *
679 * @param filename CONF_PAIR for the file being checked
680 * @param cb callback function to perform the check
681 * @param uctx user context for the callback
682 * @return
683 * - CF_FILE_OTHER_ERROR if there was a problem modifying permissions
684 * - The return value from the callback
685 */
687 cf_file_check_err_t (*cb)(char const *filename, void *uctx), void *uctx)
688{
689 int ret;
690
691 uid_t euid = (uid_t)-1;
692 gid_t egid = (gid_t)-1;
693
694 if ((conf_check_gid != (gid_t)-1) && ((egid = getegid()) != conf_check_gid)) {
695 if (setegid(conf_check_gid) < 0) {
696 fr_strerror_printf("Failed setting effective group ID (%d) for file check: %s",
697 (int) conf_check_gid, fr_syserror(errno));
698 return CF_FILE_OTHER_ERROR;
699 }
700 }
701 if ((conf_check_uid != (uid_t)-1) && ((euid = geteuid()) != conf_check_uid)) {
702 if (seteuid(conf_check_uid) < 0) {
703 fr_strerror_printf("Failed setting effective user ID (%d) for file check: %s",
704 (int) conf_check_uid, fr_syserror(errno));
705 return CF_FILE_OTHER_ERROR;
706 }
707 }
708 ret = cb(filename, uctx);
709 if (conf_check_uid != euid) {
710 if (seteuid(euid) < 0) {
711 fr_strerror_printf("Failed restoring effective user ID (%d) after file check: %s",
712 (int) euid, fr_syserror(errno));
713 return CF_FILE_OTHER_ERROR;
714 }
715 }
716 if (conf_check_gid != egid) {
717 if (setegid(egid) < 0) {
718 fr_strerror_printf("Failed restoring effective group ID (%d) after file check: %s",
719 (int) egid, fr_syserror(errno));
720 return CF_FILE_OTHER_ERROR;
721 }
722 }
723
724 return ret;
725}
726
727/** Check if we can connect to a unix socket
728 *
729 * @param[in] filename CONF_PAIR for the unix socket path
730 * @param[in] uctx user context, not used
731 * @return
732 * - CF_FILE_OK if the socket exists and is a socket.
733 * - CF_FILE_NO_EXIST if the file doesn't exist.
734 * - CF_FILE_NO_PERMISSION if the file exists but is not accessible.
735 * - CF_FILE_NO_UNIX_SOCKET if the file exists but is not a socket.
736 * - CF_FILE_OTHER_ERROR any other error.
737 */
739{
740 int fd;
742
743 struct sockaddr_un addr = { .sun_family = AF_UNIX };
744
746
747 if (talloc_strlen(filename) >= sizeof(addr.sun_path)) {
748 fr_strerror_printf("Socket path \"%s\" to long", filename);
749 return CF_FILE_OTHER_ERROR;
750 }
751
752 strncpy(addr.sun_path, filename, sizeof(addr.sun_path) - 1);
753
754 fd = socket(AF_UNIX, SOCK_STREAM, 0);
755 if (fd < 0) {
756 fr_strerror_printf("Failed checking permissions for \"%s\": %s",
757 filename, fr_syserror(errno));
758 return CF_FILE_OTHER_ERROR;
759 }
760 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
761 fr_strerror_printf("Failed setting non-blocking mode for socket %s: %s",
762 filename, fr_syserror(errno));
763 close(fd);
764 return CF_FILE_OTHER_ERROR;
765 }
766
767 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
768 switch (errno) {
769 case EINPROGRESS: /* This is fine */
770 break;
771
772 case ENOENT:
773 fr_strerror_printf("Socket path \"%s\" does not exist", filename);
774 ret = CF_FILE_NO_EXIST;
775 break;
776
777 case EACCES:
778 fr_perm_file_error(errno);
779 fr_strerror_printf_push("Socket path \"%s\" exists but is not accessible", filename);
781 break;
782
783 case ENOTSOCK:
784 fr_strerror_printf("File \"%s\" is not a socket", filename);
786 break;
787
788 default:
789 fr_strerror_printf("Failed connecting to socket %s: %s", filename, fr_syserror(errno));
791 break;
792 }
793 }
794
795 close(fd);
796
797 return ret;
798}
799
800/** Check if file exists, and is a socket
801 *
802 * @param[in] filename CONF_PAIR for the unix socket path
803 * @param[in] uctx user context, not used
804 * @return
805 * - CF_FILE_OK if the socket exists and is a socket.
806 * - CF_FILE_NO_EXIST if the file doesn't exist.
807 * - CF_FILE_NO_PERMISSION if the file exists but is not accessible.
808 * - CF_FILE_NO_UNIX_SOCKET if the file exists but is not a socket.
809 * - CF_FILE_OTHER_ERROR any other error.
810 */
811cf_file_check_err_t cf_file_check_unix_perm(char const *filename, UNUSED void *uctx)
812{
813 struct stat buf;
814
816
817 if (stat(filename, &buf) < 0) {
818 switch (errno) {
819 case ENOENT:
820 fr_strerror_printf("Socket path \"%s\" does not exist", filename);
821 return CF_FILE_NO_EXIST;
822
823 case EPERM:
824 case EACCES:
825 fr_perm_file_error(errno);
826 fr_strerror_printf_push("Socket path \"%s\" exists but is not accessible: %s",
827 filename, fr_syserror(errno));
829
830 default:
831 fr_strerror_printf("Unable to stat socket \"%s\": %s", filename, fr_syserror(errno));
832 return CF_FILE_OTHER_ERROR;
833 }
834 }
835
836 if (!S_ISSOCK(buf.st_mode)) {
837 fr_strerror_printf("File \"%s\" is not a socket", filename);
839 }
840
841 return CF_FILE_OK;
842}
843
844/** Callback for cf_file_check to open a file and check permissions.
845 *
846 * This is used to check if a file exists, and is readable by the
847 * unprivileged user/group.
848 *
849 * @param filename currently being processed.
850 * @param uctx user context, which is a pointer to cf_file_t
851 * @return
852 * - CF_FILE_OK if the file exists and is readable.
853 * - CF_FILE_NO_EXIST if the file does not exist.
854 * - CF_FILE_NO_PERMISSION if the file exists but is not accessible.
855 * - CF_FILE_OTHER_ERROR if there was any other error.
856 */
857cf_file_check_err_t cf_file_check_open_read(char const *filename, void *uctx)
858{
859 int fd;
860 cf_file_t *file = uctx;
861
863
864 fd = open(filename, O_RDONLY);
865 if (fd < 0) {
866 error:
867 if (fd >= 0) close(fd);
868
869 switch (errno) {
870 case ENOENT:
871 fr_strerror_printf("File \"%s\" does not exist", filename);
872 return CF_FILE_NO_EXIST;
873
874 case EPERM:
875 case EACCES:
876 fr_perm_file_error(errno);
877 fr_strerror_printf_push("File \"%s\" exists but is not accessible: %s",
878 filename, fr_syserror(errno));
880
881 default:
882 fr_strerror_printf("Unable to open file \"%s\": %s", filename, fr_syserror(errno));
883 return CF_FILE_OTHER_ERROR;
884
885 }
886 }
887
888 if (file && fstat(fd, &file->buf) < 0) goto error;
889
890 close(fd);
891 return CF_FILE_OK;
892}
893
894/** Do some checks on the file as an "input" file. i.e. one read by a module.
895 *
896 * @note Must be called with super user privileges.
897 *
898 * @param cp currently being processed.
899 * @param check_perms If true - will return error if file is world readable,
900 * or not readable by the unprivileged user/group.
901 * @return
902 * - CF_FILE_OK if the socket exists and is a socket.
903 * - CF_FILE_NO_EXIST if the file doesn't exist.
904 * - CF_FILE_NO_PERMISSION if the file exists but is not accessible.
905 * - CF_FILE_OTHER_ERROR any other error.
906 */
908{
910 CONF_SECTION *top;
911 fr_rb_tree_t *tree;
912 char const *filename = cf_pair_value(cp);
914
915 top = cf_root(cp);
916 tree = cf_data_value(cf_data_find(top, fr_rb_tree_t, "filename"));
917 if (!tree) return false;
918
919 file = talloc(tree, cf_file_t);
920 if (!file) return false;
921
922 file->filename = talloc_strdup(file, filename); /* The rest of the code expects this to be talloced */
924
925 if (!check_perms) {
926 if (stat(filename, &file->buf) < 0) {
927 fr_perm_file_error(errno); /* Write error and euid/egid to error buff */
928 cf_log_perr(cp, "Unable to open file \"%s\"", filename);
929 error:
931 return CF_FILE_OTHER_ERROR;
932 }
934 return CF_FILE_OK;
935 }
936
937 /*
938 * This really does seem to be the simplest way
939 * to check that the file can be read with the
940 * euid/egid.
941 */
943 if (ret < 0) {
944 cf_log_perr(cp, "Permissions check failed");
945 goto error;
946 }
947#ifdef S_IWOTH
948 if ((file->buf.st_mode & S_IWOTH) != 0) {
949 cf_log_perr(cp, "Configuration file %s is globally writable. "
950 "Refusing to start due to insecure configuration.", filename);
952 return CF_FILE_OTHER_ERROR;
953 }
954#endif
955
956 /*
957 * It's OK to include the same file twice...
958 */
959 if (!fr_rb_insert(tree, file)) talloc_free(file);
960
961 return CF_FILE_OK;
962}
963
964/*
965 * Do variable expansion in pass2.
966 *
967 * This is a breadth-first expansion. "deep
968 */
970{
971 cf_item_foreach(&cs->item, ci) {
972 char const *value;
973 CONF_PAIR *cp;
974 char buffer[8192];
975
976 if (ci->type != CONF_ITEM_PAIR) continue;
977
978 cp = cf_item_to_pair(ci);
979 if (!cp->value || !cp->pass2) continue;
980
982 (cp->rhs_quote == T_HASH) ||
985
986 value = cf_expand_variables(ci->filename, ci->lineno, cs, buffer, sizeof(buffer), cp->value, -1, NULL);
987 if (!value) return -1;
988
991 }
992
993 cf_item_foreach(&cs->item, ci) {
994 if (ci->type != CONF_ITEM_SECTION) continue;
995
996 if (cf_section_pass2(cf_item_to_section(ci)) < 0) return -1;
997 }
998
999 return 0;
1000}
1001
1002
1003static char const *cf_local_file(char const *base, char const *filename,
1004 char *buffer, size_t bufsize)
1005{
1006 size_t dirsize;
1007 char *p;
1008
1009 strlcpy(buffer, base, bufsize);
1010
1011 p = strrchr(buffer, FR_DIR_SEP);
1012 if (!p) return filename;
1013 if (p[1]) { /* ./foo */
1014 p[1] = '\0';
1015 }
1016
1017 dirsize = (p - buffer) + 1;
1018
1019 if ((dirsize + strlen(filename)) >= bufsize) {
1020 return NULL;
1021 }
1022
1023 strlcpy(p + 1, filename, bufsize - dirsize);
1024
1025 return buffer;
1026}
1027
1028/*
1029 * Like gettoken(), but uses the new API which seems better for a
1030 * host of reasons.
1031 */
1032static int cf_get_token(CONF_SECTION *parent, char const **ptr_p, fr_token_t *token, char *buffer, size_t buflen,
1033 char const *filename, int lineno)
1034{
1035 char const *ptr = *ptr_p;
1036 ssize_t slen;
1037 char const *out;
1038 size_t outlen;
1039
1040 /*
1041 * Discover the string content, returning what kind of
1042 * string it is.
1043 *
1044 * Don't allow casts or regexes. But do allow bare
1045 * %{...} expansions.
1046 */
1047 slen = tmpl_preparse(&out, &outlen, ptr, strlen(ptr), token);
1048 if (slen <= 0) {
1049 char *spaces, *text;
1050
1051 fr_canonicalize_error(parent, &spaces, &text, slen, ptr);
1052
1053 ERROR("%s[%d]: %s", filename, lineno, text);
1054 ERROR("%s[%d]: %s^ - %s", filename, lineno, spaces, fr_strerror());
1055
1057 talloc_free(text);
1058 return -1;
1059 }
1060
1061 if ((size_t) slen >= buflen) {
1062 ERROR("%s[%d]: Name is too long", filename, lineno);
1063 return -1;
1064 }
1065
1066 /*
1067 * Unescape it or copy it verbatim as necessary.
1068 */
1069 if (!cf_expand_variables(filename, lineno, parent, buffer, buflen,
1070 out, outlen, NULL)) {
1071 return -1;
1072 }
1073
1074 ptr += slen;
1075 fr_skip_whitespace(ptr);
1076
1077 *ptr_p = ptr;
1078 return 0;
1079}
1080
1085
1086static int8_t filename_cmp(void const *one, void const *two)
1087{
1088 int ret;
1089 cf_file_heap_t const *a = one;
1090 cf_file_heap_t const *b = two;
1091
1092 ret = strcmp(a->filename, b->filename);
1093 return CMP(ret, 0);
1094}
1095
1096
1097static int process_include(cf_stack_t *stack, CONF_SECTION *parent, char const *ptr, bool required, bool relative)
1098{
1099 char const *value;
1100 cf_stack_frame_t *frame = &stack->frame[stack->depth];
1101
1102 /*
1103 * Can't do this inside of update / map.
1104 */
1105 if (parent->unlang == CF_UNLANG_ASSIGNMENT) {
1106 ERROR("%s[%d]: Parse error: Invalid location for $INCLUDE",
1107 frame->filename, frame->lineno);
1108 return -1;
1109 }
1110
1111 fr_skip_whitespace(ptr);
1112
1113 /*
1114 * Grab all of the non-whitespace text.
1115 */
1116 value = ptr;
1117 while (*ptr && !isspace((uint8_t) *ptr)) ptr++;
1118
1119 /*
1120 * We're OK with whitespace after the filename.
1121 */
1122 fr_skip_whitespace(ptr);
1123
1124 /*
1125 * But anything else after the filename is wrong.
1126 */
1127 if (*ptr) {
1128 ERROR("%s[%d]: Unexpected text after $INCLUDE", frame->filename, frame->lineno);
1129 return -1;
1130 }
1131
1132 /*
1133 * Hack for ${confdir}/foo
1134 */
1135 if (*value == '$') relative = false;
1136
1137 value = cf_expand_variables(frame->filename, frame->lineno, parent, stack->buff[1], stack->bufsize,
1138 value, ptr - value, NULL);
1139 if (!value) return -1;
1140
1141 if (!FR_DIR_IS_RELATIVE(value)) relative = false;
1142
1143 if (relative) {
1144 value = cf_local_file(frame->filename, value, stack->buff[2], stack->bufsize);
1145 if (!value) {
1146 ERROR("%s[%d]: Directories too deep", frame->filename, frame->lineno);
1147 return -1;
1148 }
1149 }
1150
1151 if (strchr(value, '*') != 0) {
1152#ifndef HAVE_GLOB_H
1153 ERROR("%s[%d]: Filename globbing is not supported.", frame->filename, frame->lineno);
1154 return -1;
1155#else
1156 stack->depth++;
1157 frame = &stack->frame[stack->depth];
1158 memset(frame, 0, sizeof(*frame));
1159
1160 frame->type = CF_STACK_GLOB;
1161 frame->required = required;
1162 frame->parent = parent;
1163 frame->current = parent;
1164
1165 /*
1166 * For better debugging.
1167 */
1168 frame->filename = frame[-1].filename;
1169 frame->lineno = frame[-1].lineno;
1170
1171 if (glob(value, GLOB_ERR | GLOB_NOESCAPE, NULL, &frame->glob) < 0) {
1172 stack->depth--;
1173 ERROR("%s[%d]: Failed expanding '%s' - %s", frame->filename, frame->lineno,
1174 value, fr_syserror(errno));
1175 return -1;
1176 }
1177
1178 /*
1179 * If nothing matches, that may be an error.
1180 */
1181 if (frame->glob.gl_pathc == 0) {
1182 if (!required) {
1183 stack->depth--;
1184 return 0;
1185 }
1186
1187 ERROR("%s[%d]: Failed expanding '%s' - No matching files", frame->filename, frame->lineno,
1188 value);
1189 return -1;
1190 }
1191
1192 return 1;
1193#endif
1194 }
1195
1196 /*
1197 * Allow $-INCLUDE for directories, too.
1198 */
1199 if (!required) {
1200 struct stat statbuf;
1201
1202 if (stat(value, &statbuf) < 0) {
1203 WARN("Not including file %s: %s", value, fr_syserror(errno));
1204 return 0;
1205 }
1206 }
1207
1208 /*
1209 * The filename doesn't end in '/', so it must be a file.
1210 */
1211 if (value[strlen(value) - 1] != '/') {
1212 if ((stack->depth + 1) >= MAX_STACK) {
1213 ERROR("%s[%d]: Directories too deep", frame->filename, frame->lineno);
1214 return -1;
1215 }
1216
1217 stack->depth++;
1218 frame = &stack->frame[stack->depth];
1219 memset(frame, 0, sizeof(*frame));
1220
1221 frame->type = CF_STACK_FILE;
1222 frame->fp = NULL;
1223 frame->parent = parent;
1224 frame->current = parent;
1225 frame->filename = talloc_strdup(frame->parent, value);
1226 return 1;
1227 }
1228
1229#ifdef HAVE_DIRENT_H
1230 /*
1231 * $INCLUDE foo/
1232 *
1233 * Include ALL non-"dot" files in the directory.
1234 * careful!
1235 */
1236 {
1237 char *directory;
1238 DIR *dir;
1239 struct dirent *dp;
1240 struct stat stat_buf;
1241 cf_file_heap_t *h;
1242#ifdef S_IWOTH
1243 int my_fd;
1244#endif
1245
1246 /*
1247 * We need to keep a copy of parent while the
1248 * included files mangle our buff[] array.
1249 */
1250 directory = talloc_strdup(parent, value);
1251
1252 cf_log_debug(parent, "Including files in directory \"%s\"", directory);
1253
1254 dir = opendir(directory);
1255 if (!dir) {
1256 ERROR("%s[%d]: Error reading directory %s: %s",
1257 frame->filename, frame->lineno, value,
1258 fr_syserror(errno));
1259 error:
1260 talloc_free(directory);
1261 return -1;
1262 }
1263
1264#ifdef S_IWOTH
1265 my_fd = dirfd(dir);
1266 fr_assert(my_fd >= 0);
1267
1268 /*
1269 * Security checks.
1270 */
1271 if (fstat(my_fd, &stat_buf) < 0) {
1272 ERROR("%s[%d]: Failed reading directory %s: %s", frame->filename, frame->lineno,
1273 directory, fr_syserror(errno));
1274 goto error;
1275 }
1276
1277 if ((stat_buf.st_mode & S_IWOTH) != 0) {
1278 ERROR("%s[%d]: Directory %s is globally writable. Refusing to start due to "
1279 "insecure configuration", frame->filename, frame->lineno, directory);
1280 goto error;
1281 }
1282#endif
1283
1284 /*
1285 * Directory plus next filename.
1286 */
1287 if ((stack->depth + 2) >= MAX_STACK) {
1288 ERROR("%s[%d]: Directories too deep", frame->filename, frame->lineno);
1289 goto error;
1290 }
1291
1292 stack->depth++;
1293 frame = &stack->frame[stack->depth];
1294 *frame = (cf_stack_frame_t){
1295 .type = CF_STACK_DIR,
1296 .directory = directory,
1297 .parent = parent,
1298 .current = parent,
1299 .from_dir = true
1300 };
1301
1302 MEM(frame->heap = fr_heap_alloc(frame->directory, filename_cmp, cf_file_heap_t, heap_id, 0));
1303
1304 /*
1305 * Read the whole directory before loading any
1306 * individual file. We stat() files to ensure
1307 * that they're readable. We ignore
1308 * subdirectories and files with odd filenames.
1309 */
1310 while ((dp = readdir(dir)) != NULL) {
1311 char const *p;
1312 size_t len;
1313
1314 if (dp->d_name[0] == '.') continue;
1315
1316 /*
1317 * Check for valid characters
1318 */
1319 for (p = dp->d_name; *p != '\0'; p++) {
1320 if (isalpha((uint8_t)*p) ||
1321 isdigit((uint8_t)*p) ||
1322 (*p == '-') ||
1323 (*p == '_') ||
1324 (*p == '.')) continue;
1325 break;
1326 }
1327 if (*p != '\0') continue;
1328
1329 /*
1330 * Ignore config files generated by deb / rpm packaging updates.
1331 */
1332 len = strlen(dp->d_name);
1333 if ((len > 10) && (strncmp(&dp->d_name[len - 10], ".dpkg-dist", 10) == 0)) {
1334 pkg_file:
1335 WARN("Ignoring packaging system produced file %s%s", frame->directory, dp->d_name);
1336 continue;
1337 }
1338 if ((len > 9) && (strncmp(&dp->d_name[len - 9], ".dpkg-old", 9) == 0)) goto pkg_file;
1339 if ((len > 7) && (strncmp(&dp->d_name[len - 7], ".rpmnew", 9) == 0)) goto pkg_file;
1340 if ((len > 8) && (strncmp(&dp->d_name[len - 8], ".rpmsave", 10) == 0)) goto pkg_file;
1341
1342 snprintf(stack->buff[1], stack->bufsize, "%s%s",
1343 frame->directory, dp->d_name);
1344
1345 if (stat(stack->buff[1], &stat_buf) != 0) {
1346 ERROR("%s[%d]: Failed checking file %s: %s",
1347 (frame - 1)->filename, (frame - 1)->lineno,
1348 stack->buff[1], fr_syserror(errno));
1349 continue;
1350 }
1351
1352 if (S_ISDIR(stat_buf.st_mode)) {
1353 WARN("%s[%d]: Ignoring directory %s",
1354 (frame - 1)->filename, (frame - 1)->lineno,
1355 stack->buff[1]);
1356 continue;
1357 }
1358
1359 MEM(h = talloc_zero(frame->heap, cf_file_heap_t));
1360 MEM(h->filename = talloc_typed_strdup(h, stack->buff[1]));
1361 h->heap_id = FR_HEAP_INDEX_INVALID;
1362 (void) fr_heap_insert(&frame->heap, h);
1363 }
1364 closedir(dir);
1365 return 1;
1366 }
1367#else
1368 ERROR("%s[%d]: Error including %s: No support for directories!",
1369 frame->filename, frame->lineno, value);
1370 return -1;
1371#endif
1372}
1373
1374
1376{
1377 CONF_ITEM *ci;
1378 CONF_SECTION *parent_cs, *templatecs;
1379 fr_token_t token;
1380 cf_stack_frame_t *frame = &stack->frame[stack->depth];
1381 CONF_SECTION *parent = frame->current;
1382
1383 token = getword(&stack->ptr, stack->buff[2], stack->bufsize, true);
1384 if (token != T_EOL) {
1385 ERROR("%s[%d]: Unexpected text after $TEMPLATE", frame->filename, frame->lineno);
1386 return -1;
1387 }
1388
1389 if (!parent) {
1390 ERROR("%s[%d]: Internal sanity check error in template reference", frame->filename, frame->lineno);
1391 return -1;
1392 }
1393
1394 if (parent->template) {
1395 ERROR("%s[%d]: Section already has a template", frame->filename, frame->lineno);
1396 return -1;
1397 }
1398
1399 /*
1400 * Allow in-line templates.
1401 */
1402 templatecs = cf_section_find(cf_item_to_section(cf_parent(parent)), "template", stack->buff[2]);
1403 if (templatecs) {
1404 parent->template = templatecs;
1405 return 0;
1406 }
1407
1408 parent_cs = cf_root(parent);
1409
1410 templatecs = cf_section_find(parent_cs, "templates", NULL);
1411 if (!templatecs) {
1412 ERROR("%s[%d]: Cannot find template \"%s\", as no 'templates' section exists.",
1413 frame->filename, frame->lineno, stack->buff[2]);
1414 return -1;
1415 }
1416
1417 ci = cf_reference_item(parent_cs, templatecs, stack->buff[2]);
1418 if (!ci || (ci->type != CONF_ITEM_SECTION)) {
1419 PERROR("%s[%d]: Failed finding item \"%s\" in the 'templates' section.",
1420 frame->filename, frame->lineno, stack->buff[2]);
1421 return -1;
1422 }
1423
1424 parent->template = cf_item_to_section(ci);
1425 return 0;
1426}
1427
1428
1429static int cf_file_fill(cf_stack_t *stack);
1430
1431
1432static const bool terminal_end_section[UINT8_MAX + 1] = {
1433 ['{'] = true,
1434};
1435
1436static const bool terminal_end_line[UINT8_MAX + 1] = {
1437 [0] = true,
1438
1439 ['\r'] = true,
1440 ['\n'] = true,
1441
1442 ['#'] = true,
1443 [','] = true,
1444 [';'] = true,
1445 ['}'] = true,
1446};
1447
1449{
1450 ssize_t slen = 0;
1451 fr_dict_t const *dict = NULL;
1452 CONF_SECTION *cs;
1453 uint8_t const *p;
1454 char const *ptr = stack->ptr;
1455 cf_stack_frame_t *frame = &stack->frame[stack->depth];
1456 CONF_SECTION *parent = frame->current;
1457 char *buff[4];
1458 tmpl_rules_t t_rules;
1459
1460 /*
1461 * Short names are nicer.
1462 */
1463 buff[1] = stack->buff[1];
1464 buff[2] = stack->buff[2];
1465 buff[3] = stack->buff[3];
1466
1468
1469 t_rules = (tmpl_rules_t) {
1470 .attr = {
1471 .dict_def = dict,
1472 .list_def = request_attr_request,
1473 .allow_unresolved = true,
1474 .allow_unknown = true
1475 },
1476 .literals_safe_for = FR_VALUE_BOX_SAFE_FOR_ANY,
1477 };
1478
1479 /*
1480 * Create the CONF_SECTION. We don't pass a name2, as it
1481 * hasn't yet been parsed.
1482 */
1483 cs = cf_section_alloc(parent, parent, buff[1], NULL);
1484 if (!cs) {
1485 cf_log_err(parent, "Failed allocating memory for section");
1486 return NULL;
1487 }
1488 cf_filename_set(cs, frame->filename);
1489 cf_lineno_set(cs, frame->lineno);
1490
1491 RULES_VERIFY(cs, &t_rules);
1492
1493 /*
1494 * Keep "parsing" the condition until we hit EOL.
1495 *
1496 *
1497 */
1498 while (true) {
1499 int rcode;
1500 bool eol;
1501
1502 /*
1503 * Try to parse the condition. We can have a parse success, or a parse failure.
1504 */
1505 slen = fr_skip_condition(ptr, NULL, terminal_end_section, &eol);
1506
1507 /*
1508 * Parse success means we stop reading more data.
1509 */
1510 if (slen > 0) break;
1511
1512 /*
1513 * Parse failures not at EOL are real errors.
1514 */
1515 if (!eol) {
1516 slen = 0;
1517 fr_strerror_const("Unexpected EOF");
1518 error:
1519 cf_canonicalize_error(cs, slen, "Parse error in condition", ptr);
1520 talloc_free(cs);
1521 return NULL;
1522 }
1523
1524 /*
1525 * Parse failures at EOL means that we read more data.
1526 */
1527 p = (uint8_t const *) ptr + (-slen);
1528
1529 /*
1530 * Auto-continue across CR LF until we reach the
1531 * end of the string. We mash everything into one line.
1532 */
1533 if (*p && (*p < ' ')) {
1534 while ((*p == '\r') || (*p == '\n')) {
1535 char *q;
1536
1537 q = UNCONST(char *, p);
1538 *q = ' ';
1539 p++;
1540 continue;
1541 }
1542
1543 /*
1544 * Hopefully the next line is already in
1545 * the buffer, and we don't have to read
1546 * more data.
1547 */
1548 continue;
1549 }
1550
1551 /*
1552 * Anything other than EOL is a problem at this point.
1553 */
1554 if (*p) {
1555 fr_strerror_const("Unexpected text after condition");
1556 goto error;
1557 }
1558
1559 /*
1560 * We hit EOL, so the parse error is really "read more data".
1561 */
1562 stack->fill = UNCONST(char *, p);
1563 rcode = cf_file_fill(stack);
1564 if (rcode < 0) {
1565 cf_log_err(cs, "Failed parsing condition");
1566 return NULL;
1567 }
1568 }
1569
1570 fr_assert((size_t) slen < (stack->bufsize - 1));
1571
1572 ptr += slen;
1573 fr_skip_whitespace(ptr);
1574
1575 if (*ptr != '{') {
1576 cf_log_err(cs, "Expected '{' instead of %s", ptr);
1577 talloc_free(cs);
1578 return NULL;
1579 }
1580 ptr++;
1581
1582 /*
1583 * Save the parsed condition (minus trailing whitespace)
1584 * into a buffer.
1585 */
1586 memcpy(buff[2], stack->ptr, slen);
1587 buff[2][slen] = '\0';
1588
1589 while (slen > 0) {
1590 if (!isspace((uint8_t) buff[2][slen])) break;
1591
1592 buff[2][slen] = '\0';
1593 slen--;
1594 }
1595
1596 /*
1597 * Expand the variables in the pre-parsed condition.
1598 */
1599 if (!cf_expand_variables(frame->filename, frame->lineno, parent,
1600 buff[3], stack->bufsize, buff[2], slen, NULL)) {
1601 fr_strerror_const("Failed expanding configuration variable");
1602 return NULL;
1603 }
1604
1605 MEM(cs->name2 = talloc_typed_strdup(cs, buff[3]));
1607
1608 stack->ptr = ptr;
1609
1610 cs->allow_locals = true;
1611 cs->unlang = CF_UNLANG_ALLOW;
1612 return cf_section_to_item(cs);
1613}
1614
1616{
1617 char const *mod;
1618 char const *value = NULL;
1619 CONF_SECTION *css;
1620 fr_token_t token;
1621 char const *ptr = stack->ptr;
1622 cf_stack_frame_t *frame = &stack->frame[stack->depth];
1623 CONF_SECTION *parent = frame->current;
1624 char *buff[4];
1625
1626 /*
1627 * Short names are nicer.
1628 */
1629 buff[1] = stack->buff[1];
1630 buff[2] = stack->buff[2];
1631
1632 if (cf_get_token(parent, &ptr, &token, buff[1], stack->bufsize,
1633 frame->filename, frame->lineno) < 0) {
1634 return NULL;
1635 }
1636
1637 if (token != T_BARE_WORD) {
1638 ERROR("%s[%d]: Invalid syntax for 'map' - module name must not be a quoted string",
1639 frame->filename, frame->lineno);
1640 return NULL;
1641 }
1642 mod = buff[1];
1643
1644 /*
1645 * Maps without an expansion string are allowed, though
1646 * it's not clear why.
1647 */
1648 if (*ptr == '{') {
1649 ptr++;
1650 goto alloc_section;
1651 }
1652
1653 /*
1654 * Now get the expansion string.
1655 */
1656 if (cf_get_token(parent, &ptr, &token, buff[2], stack->bufsize,
1657 frame->filename, frame->lineno) < 0) {
1658 return NULL;
1659 }
1660 if (!fr_str_tok[token]) {
1661 ERROR("%s[%d]: Expecting string expansions in 'map' definition",
1662 frame->filename, frame->lineno);
1663 return NULL;
1664 }
1665
1666 if (*ptr != '{') {
1667 ERROR("%s[%d]: Expecting section start brace '{' in 'map' definition",
1668 frame->filename, frame->lineno);
1669 return NULL;
1670 }
1671 ptr++;
1672 value = buff[2];
1673
1674 /*
1675 * Allocate the section
1676 */
1677alloc_section:
1678 css = cf_section_alloc(parent, parent, "map", mod);
1679 if (!css) {
1680 ERROR("%s[%d]: Failed allocating memory for section",
1681 frame->filename, frame->lineno);
1682 return NULL;
1683 }
1684 cf_filename_set(css, frame->filename);
1685 cf_lineno_set(css, frame->lineno);
1686 css->name2_quote = T_BARE_WORD;
1687
1688 css->argc = 0;
1689 if (value) {
1690 css->argv = talloc_array(css, char const *, 1);
1691 css->argv[0] = talloc_typed_strdup(css->argv, value);
1692 css->argv_quote = talloc_array(css, fr_token_t, 1);
1693 css->argv_quote[0] = token;
1694 css->argc++;
1695 }
1696 stack->ptr = ptr;
1698
1699 return cf_section_to_item(css);
1700}
1701
1702
1704{
1705 char const *mod = NULL;
1706 CONF_SECTION *css;
1707 fr_token_t token;
1708 char const *ptr = stack->ptr;
1709 cf_stack_frame_t *frame = &stack->frame[stack->depth];
1710 CONF_SECTION *parent = frame->current;
1711 char *buff[4];
1712 int values = 0;
1713
1714 /*
1715 * Short names are nicer.
1716 */
1717 buff[1] = stack->buff[1];
1718 buff[2] = stack->buff[2];
1719 buff[3] = stack->buff[3];
1720
1721 /*
1722 * subrequest { ... } is allowed.
1723 */
1724 fr_skip_whitespace(ptr);
1725 if (*ptr == '{') {
1726 ptr++;
1727 goto alloc_section;
1728 }
1729
1730 /*
1731 * Get the name of the Packet-Type.
1732 */
1733 if (cf_get_token(parent, &ptr, &token, buff[1], stack->bufsize,
1734 frame->filename, frame->lineno) < 0) {
1735 return NULL;
1736 }
1737
1738 mod = buff[1];
1739
1740 /*
1741 * subrequest Access-Request { ... } is allowed.
1742 */
1743 if (*ptr == '{') {
1744 ptr++;
1745 goto alloc_section;
1746 }
1747
1748 /*
1749 * subrequest Access-Request &foo { ... }
1750 */
1751 if (cf_get_token(parent, &ptr, &token, buff[2], stack->bufsize,
1752 frame->filename, frame->lineno) < 0) {
1753 return NULL;
1754 }
1755
1756 if (token != T_BARE_WORD) {
1757 ERROR("%s[%d]: The second argument to 'subrequest' must be an attribute reference",
1758 frame->filename, frame->lineno);
1759 return NULL;
1760 }
1761 values++;
1762
1763 if (*ptr == '{') {
1764 ptr++;
1765 goto alloc_section;
1766 }
1767
1768 /*
1769 * subrequest Access-Request &foo &bar { ... }
1770 */
1771 if (cf_get_token(parent, &ptr, &token, buff[3], stack->bufsize,
1772 frame->filename, frame->lineno) < 0) {
1773 return NULL;
1774 }
1775
1776 if (token != T_BARE_WORD) {
1777 ERROR("%s[%d]: The third argument to 'subrequest' must be an attribute reference",
1778 frame->filename, frame->lineno);
1779 return NULL;
1780 }
1781 values++;
1782
1783 if (*ptr != '{') {
1784 ERROR("%s[%d]: Expecting section start brace '{' in 'subrequest' definition",
1785 frame->filename, frame->lineno);
1786 return NULL;
1787 }
1788 ptr++;
1789
1790 /*
1791 * Allocate the section
1792 */
1793alloc_section:
1794 css = cf_section_alloc(parent, parent, "subrequest", mod);
1795 if (!css) {
1796 ERROR("%s[%d]: Failed allocating memory for section",
1797 frame->filename, frame->lineno);
1798 return NULL;
1799 }
1800 cf_filename_set(css, frame->filename);
1801 cf_lineno_set(css, frame->lineno);
1802 if (mod) css->name2_quote = T_BARE_WORD;
1803
1804 css->argc = values;
1805 if (values) {
1806 int i;
1807
1808 css->argv = talloc_array(css, char const *, values);
1809 css->argv_quote = talloc_array(css, fr_token_t, values);
1810
1811 for (i = 0; i < values; i++) {
1812 css->argv[i] = talloc_typed_strdup(css->argv, buff[2 + i]);
1813 css->argv_quote[i] = T_BARE_WORD;
1814 }
1815 }
1816
1817 stack->ptr = ptr;
1818
1819 css->allow_locals = true;
1820 css->unlang = CF_UNLANG_ALLOW;
1821 return cf_section_to_item(css);
1822}
1823
1825{
1826 CONF_SECTION *css;
1827 int argc = 0;
1828 char const *ptr = stack->ptr;
1829 cf_stack_frame_t *frame = &stack->frame[stack->depth];
1830 CONF_SECTION *parent = frame->current;
1831 char *name2 = NULL;
1832 char *argv[RLM_MODULE_NUMCODES];
1833
1834 while (true) {
1835 char const *p;
1836 size_t len;
1837
1838 fr_skip_whitespace(ptr);
1839
1840 /*
1841 * We have an open bracket, it's the end of the "catch" statement.
1842 */
1843 if (*ptr == '{') {
1844 ptr++;
1845 break;
1846 }
1847
1848 /*
1849 * The arguments have to be short, unquoted words.
1850 */
1851 p = ptr;
1852 while (isalpha((uint8_t) *ptr)) ptr++;
1853
1854 len = ptr - p;
1855 if (len > 16) {
1856 ERROR("%s[%d]: Invalid syntax for 'catch' - unknown rcode '%s'",
1857 frame->filename, frame->lineno, p);
1858 return NULL;
1859 }
1860
1861 if ((*ptr != '{') && !isspace((uint8_t) *ptr)) {
1862 ERROR("%s[%d]: Invalid syntax for 'catch' - unexpected text at '%s'",
1863 frame->filename, frame->lineno, ptr);
1864 return NULL;
1865 }
1866
1867 if (!name2) {
1868 name2 = talloc_strndup(NULL, p, len);
1869 continue;
1870 }
1871
1872 if (argc > RLM_MODULE_NUMCODES) {
1873 ERROR("%s[%d]: Invalid syntax for 'catch' - too many arguments at'%s'",
1874 frame->filename, frame->lineno, ptr);
1875 return NULL;
1876 }
1877
1878 argv[argc++] = talloc_strndup(name2, p, len);
1879 }
1880
1881 css = cf_section_alloc(parent, parent, "catch", name2);
1882 if (!css) {
1883 talloc_free(name2);
1884 ERROR("%s[%d]: Failed allocating memory for section",
1885 frame->filename, frame->lineno);
1886 return NULL;
1887 }
1888 cf_filename_set(css, frame->filename);
1889 cf_lineno_set(css, frame->lineno);
1890 css->name2_quote = T_BARE_WORD;
1891 css->unlang = CF_UNLANG_ALLOW;
1892
1893 css->argc = argc;
1894 if (argc) {
1895 int i;
1896
1897 css->argv = talloc_array(css, char const *, argc + 1);
1898 css->argv_quote = talloc_array(css, fr_token_t, argc);
1899 css->argc = argc;
1900
1901 for (i = 0; i < argc; i++) {
1902 css->argv[i] = talloc_typed_strdup(css->argv, argv[i]);
1903 css->argv_quote[i] = T_BARE_WORD;
1904 }
1905
1906 css->argv[argc] = NULL;
1907 }
1908 talloc_free(name2);
1909
1910 stack->ptr = ptr;
1911
1912 return cf_section_to_item(css);
1913}
1914
1915static int parse_error(cf_stack_t *stack, char const *ptr, char const *message)
1916{
1917 char *spaces, *text;
1918 cf_stack_frame_t *frame = &stack->frame[stack->depth];
1919
1920 if (!ptr) ptr = stack->ptr;
1921
1922 /*
1923 * We must pass a _negative_ offset to this function.
1924 */
1925 fr_canonicalize_error(NULL, &spaces, &text, stack->ptr - ptr, stack->ptr);
1926
1927 ERROR("%s[%d]: %s", frame->filename, frame->lineno, text);
1928 ERROR("%s[%d]: %s^ - %s", frame->filename, frame->lineno, spaces, message);
1929
1931 talloc_free(text);
1932 return -1;
1933}
1934
1935static int parse_type_name(cf_stack_t *stack, char const **ptr_p, char const *type_ptr, fr_type_t *type_p)
1936{
1938 fr_token_t token;
1939 char const *ptr = *ptr_p;
1940 char const *ptr2;
1941
1942 /*
1943 * Parse an explicit type.
1944 */
1946 switch (type) {
1947 default:
1948 break;
1949
1950 case FR_TYPE_NULL:
1951 case FR_TYPE_VOID:
1952 case FR_TYPE_VALUE_BOX:
1953 case FR_TYPE_MAX:
1954 (void) parse_error(stack, type_ptr, "Unknown or invalid variable type in 'foreach'");
1955 return -1;
1956 }
1957
1958 fr_skip_whitespace(ptr);
1959 ptr2 = ptr;
1960
1961 /*
1962 * Parse the variable name. @todo - allow '-' in names.
1963 */
1964 token = gettoken(&ptr, stack->buff[2], stack->bufsize, false);
1965 if (token != T_BARE_WORD) {
1966 (void) parse_error(stack, ptr2, "Invalid variable name for key in 'foreach'");
1967 return -1;
1968 }
1969 fr_skip_whitespace(ptr);
1970
1971 *ptr_p = ptr;
1972 *type_p = type;
1973
1974 return 0;
1975}
1976
1977/*
1978 * foreach &User-Name { - old and deprecated
1979 *
1980 * foreach value (...) { - automatically define variable
1981 *
1982 * foreach string value ( ...) { - data type for variable
1983 *
1984 * foreach string key, type value (..) { - key is "string", value is as above
1985 */
1987{
1988 fr_token_t token;
1990 CONF_SECTION *css;
1991 char const *ptr = stack->ptr, *ptr2, *type_ptr;
1992 cf_stack_frame_t *frame = &stack->frame[stack->depth];
1993 CONF_SECTION *parent = frame->current;
1994
1995 css = cf_section_alloc(parent, parent, "foreach", NULL);
1996 if (!css) {
1997 ERROR("%s[%d]: Failed allocating memory for section",
1998 frame->filename, frame->lineno);
1999 return NULL;
2000 }
2001
2002 cf_filename_set(css, frame->filename);
2003 cf_lineno_set(css, frame->lineno);
2004 css->name2_quote = T_BARE_WORD;
2005 css->unlang = CF_UNLANG_ALLOW;
2006 css->allow_locals = true;
2007
2008 /*
2009 * Get the first argument to "foreach". For backwards
2010 * compatibility, it could be an attribute reference.
2011 */
2012 type_ptr = ptr;
2013 if (cf_get_token(parent, &ptr, &token, stack->buff[1], stack->bufsize,
2014 frame->filename, frame->lineno) < 0) {
2015 return NULL;
2016 }
2017
2018 if (token != T_BARE_WORD) {
2019 invalid_argument:
2020 (void) parse_error(stack, type_ptr, "Unexpected argument to 'foreach'");
2021 return NULL;
2022 }
2023
2024 fr_skip_whitespace(ptr);
2025
2026 /*
2027 * foreach foo { ...
2028 *
2029 * Deprecated and don't use.
2030 */
2031 if (*ptr == '{') {
2032 css->name2 = talloc_typed_strdup(css, stack->buff[1]);
2033
2034 ptr++;
2035 stack->ptr = ptr;
2036
2037 cf_log_warn(css, "Using deprecated syntax. Please use new the new 'foreach' syntax.");
2038 return cf_section_to_item(css);
2039 }
2040
2041 fr_skip_whitespace(ptr);
2042
2043 /*
2044 * foreach value (...) {
2045 */
2046 if (*ptr == '(') {
2048 strcpy(stack->buff[2], stack->buff[1]); /* so that we can parse expression in buff[1] */
2049 goto alloc_argc_2;
2050 }
2051
2052 /*
2053 * on input, type name is in stack->buff[1]
2054 * on output, variable name is in stack->buff[2]
2055 */
2056 if (parse_type_name(stack, &ptr, type_ptr, &type) < 0) return NULL;
2057
2058 /*
2059 * if we now have an expression block, then just have variable type / name.
2060 */
2061 if (*ptr == '(') goto alloc_argc_2;
2062
2063 /*
2064 * There's a comma. the first "type name" is for the key. We skip the comma, and parse the
2065 * second "type name" as being for the value.
2066 *
2067 * foreach type key, type value (...)
2068 */
2069 if (*ptr == ',') {
2070 /*
2071 * We have 4 arguments, [var-type, var-name, key-type, key-name]
2072 *
2073 * We don't really care about key-type, but we might care later.
2074 */
2075 css->argc = 4;
2076 css->argv = talloc_array(css, char const *, css->argc);
2077 css->argv_quote = talloc_array(css, fr_token_t, css->argc);
2078
2079 css->argv[2] = fr_type_to_str(type);
2080 css->argv_quote[2] = T_BARE_WORD;
2081
2082 css->argv[3] = talloc_typed_strdup(css->argv, stack->buff[2]);
2083 css->argv_quote[3] = T_BARE_WORD;
2084
2085 ptr++;
2086 fr_skip_whitespace(ptr);
2087 type_ptr = ptr;
2088
2089 /*
2090 * Now parse "type value"
2091 */
2092 token = gettoken(&ptr, stack->buff[1], stack->bufsize, false);
2093 if (token != T_BARE_WORD) goto invalid_argument;
2094
2095 if (parse_type_name(stack, &ptr, type_ptr, &type) < 0) return NULL;
2096
2097 if (!fr_type_is_leaf(type)) {
2098 (void) parse_error(stack, type_ptr, "Invalid data type for 'key' variable");
2099 return NULL;
2100 }
2101 }
2102
2103 /*
2104 * The thing to loop over must now be in an expression block.
2105 */
2106 if (*ptr != '(') {
2107 (void) parse_error(stack, ptr, "Expected (...) after 'foreach' variable definition");
2108 return NULL;
2109 }
2110
2111 goto parse_expression;
2112
2113alloc_argc_2:
2114 css->argc = 2;
2115 css->argv = talloc_array(css, char const *, css->argc);
2116 css->argv_quote = talloc_array(css, fr_token_t, css->argc);
2117
2118
2119parse_expression:
2120 /*
2121 * "(" whitespace EXPRESSION whitespace ")"
2122 */
2123 ptr++;
2124 fr_skip_whitespace(ptr);
2125 ptr2 = ptr;
2126
2127 if (cf_get_token(parent, &ptr, &token, stack->buff[1], stack->bufsize,
2128 frame->filename, frame->lineno) < 0) {
2129 return NULL;
2130 }
2131
2132 /*
2133 * We can do &foo[*] or %func(...), but not "...".
2134 */
2135 if (token != T_BARE_WORD) {
2136 (void) parse_error(stack, ptr2, "Invalid reference in 'foreach'");
2137 return NULL;
2138 }
2139
2140 fr_skip_whitespace(ptr);
2141 if (*ptr != ')') {
2142 (void) parse_error(stack, ptr, "Missing ')' in 'foreach'");
2143 return NULL;
2144 }
2145 ptr++;
2146 fr_skip_whitespace(ptr);
2147
2148 if (*ptr != '{') {
2149 (void) parse_error(stack, ptr, "Expected '{' in 'foreach'");
2150 return NULL;
2151 }
2152
2153 css->name2 = talloc_typed_strdup(css, stack->buff[1]);
2154
2155 /*
2156 * Add in the extra arguments
2157 */
2158 css->argv[0] = fr_type_to_str(type);
2159 css->argv_quote[0] = T_BARE_WORD;
2160
2161 css->argv[1] = talloc_typed_strdup(css->argv, stack->buff[2]);
2162 css->argv_quote[1] = T_BARE_WORD;
2163
2164 ptr++;
2165 stack->ptr = ptr;
2166
2167 return cf_section_to_item(css);
2168}
2169
2170
2171static int add_pair(CONF_SECTION *parent, char const *attr, char const *value,
2172 fr_token_t name1_token, fr_token_t op_token, fr_token_t value_token,
2173 char *buff, char const *filename, int lineno)
2174{
2175 CONF_DATA const *cd;
2176 conf_parser_t *rule;
2177 CONF_PAIR *cp;
2178 bool pass2 = false;
2179
2180 /*
2181 * If we have the value, expand any configuration
2182 * variables in it.
2183 */
2184 if (value && *value) {
2185 bool soft_fail;
2186 char const *expanded;
2187
2188 expanded = cf_expand_variables(filename, lineno, parent, buff, talloc_array_length(buff), value, -1, &soft_fail);
2189 if (expanded) {
2190 value = expanded;
2191
2192 } else if (!soft_fail) {
2193 return -1;
2194
2195 } else {
2196 /*
2197 * References an item which doesn't exist,
2198 * or which is already marked up as being
2199 * expanded in pass2. Wait for pass2 to
2200 * do the expansions.
2201 *
2202 * Leave the input value alone.
2203 */
2204 pass2 = true;
2205 }
2206 }
2207
2208 cp = cf_pair_alloc(parent, attr, value, op_token, name1_token, value_token);
2209 if (!cp) return -1;
2210 cf_filename_set(cp, filename);
2211 cf_lineno_set(cp, lineno);
2212 cp->pass2 = pass2;
2213
2215 if (!cd) return 0;
2216
2217 rule = cf_data_value(cd);
2218 if (!rule->on_read) return 0;
2219
2220 /*
2221 * Do the on_read callback after adding the value.
2222 */
2223 return rule->on_read(parent, NULL, NULL, cf_pair_to_item(cp), rule);
2224}
2225
2226/*
2227 * switch (cast) foo {
2228 */
2230{
2231 size_t match_len;
2233 fr_token_t name2_quote = T_BARE_WORD;
2234 CONF_SECTION *css;
2235 char const *ptr = stack->ptr;
2236 cf_stack_frame_t *frame = &stack->frame[stack->depth];
2237 CONF_SECTION *parent = frame->current;
2238
2239 fr_skip_whitespace(ptr);
2240 if (*ptr == '(') {
2241 char const *start;
2242
2243 ptr++;
2244 start = ptr;
2245
2246 while (isalpha(*ptr)) ptr++;
2247
2248 if (*ptr != ')') {
2249 ERROR("%s[%d]: Missing ')' in cast",
2250 frame->filename, frame->lineno);
2251 return NULL;
2252 }
2253
2255 start, ptr - start, FR_TYPE_MAX);
2256 if (type == FR_TYPE_MAX) {
2257 ERROR("%s[%d]: Unknown data type '%.*s' in cast",
2258 frame->filename, frame->lineno, (int) (ptr - start), start);
2259 return NULL;
2260 }
2261
2262 if (!fr_type_is_leaf(type)) {
2263 ERROR("%s[%d]: Invalid data type '%.*s' in cast",
2264 frame->filename, frame->lineno, (int) (ptr - start), start);
2265 return NULL;
2266 }
2267
2268 ptr++;
2269 fr_skip_whitespace(ptr);
2270 }
2271
2272 /*
2273 * Get the argument to the switch statement
2274 */
2275 if (cf_get_token(parent, &ptr, &name2_quote, stack->buff[1], stack->bufsize,
2276 frame->filename, frame->lineno) < 0) {
2277 return NULL;
2278 }
2279
2280 css = cf_section_alloc(parent, parent, "switch", NULL);
2281 if (!css) {
2282 ERROR("%s[%d]: Failed allocating memory for section",
2283 frame->filename, frame->lineno);
2284 return NULL;
2285 }
2286
2287 cf_filename_set(css, frame->filename);
2288 cf_lineno_set(css, frame->lineno);
2289 css->name2_quote = name2_quote;
2290 css->unlang = CF_UNLANG_ALLOW;
2291 css->allow_locals = true;
2292
2293 fr_skip_whitespace(ptr);
2294
2295 if (*ptr != '{') {
2296 (void) parse_error(stack, ptr, "Expected '{' in 'switch'");
2297 return NULL;
2298 }
2299
2300 css->name2 = talloc_typed_strdup(css, stack->buff[1]);
2301
2302 /*
2303 * Add in the extra argument.
2304 */
2305 if (type != FR_TYPE_NULL) {
2306 css->argc = 1;
2307 css->argv = talloc_array(css, char const *, css->argc);
2308 css->argv_quote = talloc_array(css, fr_token_t, css->argc);
2309
2310 css->argv[0] = fr_type_to_str(type);
2311 css->argv_quote[0] = T_BARE_WORD;
2312 }
2313
2314 ptr++;
2315 stack->ptr = ptr;
2316
2317 return cf_section_to_item(css);
2318}
2319
2320
2322 { L("catch"), (void *) process_catch },
2323 { L("elsif"), (void *) process_if },
2324 { L("foreach"), (void *) process_foreach },
2325 { L("if"), (void *) process_if },
2326 { L("map"), (void *) process_map },
2327 { L("subrequest"), (void *) process_subrequest },
2328 { L("switch"), (void *) process_switch }
2329};
2331
2332typedef CONF_ITEM *(*cf_process_func_t)(cf_stack_t *);
2333
2335{
2336 fr_token_t name1_token, name2_token, value_token, op_token;
2337 char const *value;
2338 CONF_SECTION *css;
2339 char const *ptr = stack->ptr;
2340 char const *ptr2;
2341 cf_stack_frame_t *frame = &stack->frame[stack->depth];
2342 CONF_SECTION *parent = frame->current;
2343 char *buff[4];
2344 cf_process_func_t process;
2345
2346 /*
2347 * Short names are nicer.
2348 */
2349 buff[0] = stack->buff[0];
2350 buff[1] = stack->buff[1];
2351 buff[2] = stack->buff[2];
2352 buff[3] = stack->buff[3];
2353
2354 fr_assert(parent != NULL);
2355
2356 /*
2357 * Catch end of a subsection.
2358 *
2359 * frame->current is the new thing we just created.
2360 * frame->parent is the parent of the current frame
2361 * frame->at_reference is the original frame->current, before the @reference
2362 * parent is the parent we started with when we started this section.
2363 */
2364 if (*ptr == '}') {
2365 /*
2366 * No pushed braces means that we're already in
2367 * the parent section which loaded this file. We
2368 * cannot go back up another level.
2369 *
2370 * This limitation means that we cannot put half
2371 * of a CONF_SECTION in one file, and then the
2372 * second half in another file. That's fine.
2373 */
2374 if (frame->braces == 0) {
2375 return parse_error(stack, ptr, "Too many closing braces");
2376 }
2377
2378 /*
2379 * Reset the current and parent to the original
2380 * section, before we were parsing the
2381 * @reference.
2382 */
2383 if (frame->at_reference) {
2384 frame->current = frame->parent = frame->at_reference;
2385 frame->at_reference = NULL;
2386
2387 } else {
2388 /*
2389 * Go back up one section, because we can.
2390 */
2391 frame->current = frame->parent = cf_item_to_section(frame->current->item.parent);
2392 }
2393
2394 fr_assert(frame->braces > 0);
2395 frame->braces--;
2396
2397 /*
2398 * Merge the template into the existing
2399 * section. parent uses more memory, but
2400 * means that templates now work with
2401 * sub-sections, etc.
2402 */
2403 if (!cf_template_merge(parent, parent->template)) return -1;
2404
2405 ptr++;
2406 stack->ptr = ptr;
2407 return 1;
2408 }
2409
2410 /*
2411 * Found nothing to get excited over. It MUST be
2412 * a key word.
2413 */
2414 ptr2 = ptr;
2415 switch (parent->unlang) {
2416 default:
2417 /*
2418 * The LHS is a bare word / keyword in normal configuration file syntax.
2419 */
2420 name1_token = gettoken(&ptr, buff[1], stack->bufsize, false);
2421 if (name1_token == T_EOL) return 0;
2422
2423 if (name1_token == T_INVALID) {
2424 return parse_error(stack, ptr2, fr_strerror());
2425 }
2426
2427 if (name1_token != T_BARE_WORD) {
2428 return parse_error(stack, ptr2, "Invalid location for quoted string");
2429 }
2430
2431 fr_skip_whitespace(ptr);
2432 break;
2433
2434 case CF_UNLANG_ALLOW:
2435 case CF_UNLANG_EDIT:
2437 /*
2438 * The LHS can be an xlat expansion, attribute reference, etc.
2439 */
2440 if (cf_get_token(parent, &ptr, &name1_token, buff[1], stack->bufsize,
2441 frame->filename, frame->lineno) < 0) {
2442 return -1;
2443 }
2444 break;
2445 }
2446
2447 /*
2448 * Check if the thing we just parsed is an unlang keyword.
2449 */
2450 if ((name1_token == T_BARE_WORD) && isalpha((uint8_t) *buff[1])) {
2452 if (process) {
2453 CONF_ITEM *ci;
2454
2455 /*
2456 * Disallow keywords outside of unlang sections.
2457 *
2458 * We don't strictly need to do this with the more state-oriented parser, but
2459 * people keep putting unlang into random places in the configuration files,
2460 * which is wrong.
2461 */
2462 if (parent->unlang != CF_UNLANG_ALLOW) {
2463 return parse_error(stack, ptr2, "Invalid location for unlang keyword");
2464 }
2465
2466 stack->ptr = ptr;
2467 ci = process(stack);
2468 if (!ci) return -1;
2469
2470 ptr = stack->ptr;
2471 if (cf_item_is_section(ci)) {
2472 parent->allow_locals = false;
2473 css = cf_item_to_section(ci);
2474 goto add_section;
2475 }
2476
2477 /*
2478 * Else the item is a pair, and the call to process() it already added it to the
2479 * current section.
2480 */
2481 goto added_pair;
2482 }
2483
2484 /*
2485 * The next token isn't text, so we ignore it.
2486 */
2487 if (!isalnum((int) *ptr)) goto check_for_eol;
2488 }
2489
2490 /*
2491 * See if this thing is a variable definition.
2492 */
2493 if ((name1_token == T_BARE_WORD) && parent->allow_locals) {
2495 char const *ptr3;
2496
2498 if (type == FR_TYPE_NULL) {
2499 parent->allow_locals = false;
2500 goto check_for_eol;
2501 }
2502
2503 if (type == FR_TYPE_TLV) goto parse_name2;
2504
2505 /*
2506 * group {
2507 *
2508 * is a section.
2509 */
2510 if (type == FR_TYPE_GROUP) {
2511 fr_skip_whitespace(ptr);
2512 if (*ptr == '{') {
2513 ptr++;
2514 value = NULL;
2515 name2_token = T_BARE_WORD;
2516 goto alloc_section;
2517 }
2518
2519 } else if (!fr_type_is_leaf(type)) {
2520 /*
2521 * Other structural types are allowed.
2522 */
2523 return parse_error(stack, ptr2, "Invalid data type for local variable. Must be 'tlv' or else a non-structrul type");
2524 }
2525
2526 /*
2527 * We don't have an operator, so set it to a magic value.
2528 */
2529 op_token = T_OP_CMP_TRUE;
2530
2531 /*
2532 * Parse the name of the local variable, and use it as the "value" for the CONF_PAIR.
2533 */
2534 ptr3 = ptr;
2535 if (cf_get_token(parent, &ptr, &value_token, buff[2], stack->bufsize,
2536 frame->filename, frame->lineno) < 0) {
2537 return -1;
2538 }
2539
2540 if (value_token != T_BARE_WORD) {
2541 return parse_error(stack, ptr3, "Invalid name");
2542 }
2543
2544 value = buff[2];
2545
2546 /*
2547 * Non-structural things must be variable definitions.
2548 */
2549 if (fr_type_is_leaf(type)) goto alloc_pair;
2550
2551 /*
2552 * Parse: group foo
2553 * vs group foo { ...
2554 */
2555 fr_skip_whitespace(ptr);
2556
2557 if (*ptr != '{') goto alloc_pair;
2558
2559 ptr++;
2560 name2_token = T_BARE_WORD;
2561 goto alloc_section;
2562 }
2563
2564 /*
2565 * We've parsed the LHS thing. The RHS might be empty, or an operator, or another word, or an
2566 * open bracket.
2567 */
2568check_for_eol:
2569 if (!*ptr || (*ptr == '#') || (*ptr == ',') || (*ptr == ';') || (*ptr == '}')) {
2570 /*
2571 * Only unlang sections can have module references.
2572 *
2573 * We also allow bare words in edit lists, where the RHS is a list of values.
2574 *
2575 * @todo - detail "suppress" requires bare words :(
2576 */
2577 parent->allow_locals = false;
2578 value_token = T_INVALID;
2579 op_token = T_OP_EQ;
2580 value = NULL;
2581 goto alloc_pair;
2582 }
2583
2584 /*
2585 * A common pattern is: name { ...}
2586 * Check for it and skip ahead.
2587 */
2588 if (*ptr == '{') {
2589 ptr++;
2590 name2_token = T_INVALID;
2591 value = NULL;
2592 goto alloc_section;
2593 }
2594
2595 /*
2596 * Parse the thing after the first word. It can be an operator, or the second name for a section.
2597 */
2598 ptr2 = ptr;
2599 switch (parent->unlang) {
2600 default:
2601 /*
2602 * Configuration sections can only have '=' after the
2603 * first word, OR a second word which is the second name
2604 * of a configuration section.
2605 */
2606 if (*ptr == '=') goto operator;
2607
2608 /*
2609 * Section name2 can only be alphanumeric or UTF-8.
2610 */
2611 parse_name2:
2612 if (!(isalpha((uint8_t) *ptr) || isdigit((uint8_t) *ptr) || (*(uint8_t const *) ptr >= 0x80))) {
2613 /*
2614 * Maybe they missed a closing brace somewhere?
2615 */
2616 name2_token = gettoken(&ptr, buff[2], stack->bufsize, false); /* can't be EOL */
2617 if (fr_assignment_op[name2_token]) {
2618 return parse_error(stack, ptr2, "Unexpected operator, was expecting a configuration section. Is there a missing '}' somewhere?");
2619 }
2620
2621 return parse_error(stack, ptr2, "Invalid second name for configuration section");
2622 }
2623
2624 name2_token = gettoken(&ptr, buff[2], stack->bufsize, false); /* can't be EOL */
2625 if (name1_token == T_INVALID) {
2626 return parse_error(stack, ptr2, fr_strerror());
2627 }
2628
2629 if (name1_token != T_BARE_WORD) {
2630 return parse_error(stack, ptr2, "Unexpected quoted string after section name");
2631 }
2632
2633 fr_skip_whitespace(ptr);
2634
2635 if (*ptr != '{') {
2636 return parse_error(stack, ptr, "Missing '{' for configuration section");
2637 }
2638
2639 ptr++;
2640 value = buff[2];
2641 goto alloc_section;
2642
2644 /*
2645 * The next thing MUST be an operator. We don't support nested attributes in "update" or
2646 * "map" sections.
2647 */
2648 goto operator;
2649
2650 case CF_UNLANG_EDIT:
2651 /*
2652 * The next thing MUST be an operator. Edit sections always do operations, even on
2653 * lists. i.e. there is no second name section when editing a list.
2654 */
2655 goto operator;
2656
2657 case CF_UNLANG_ALLOW:
2658 /*
2659 * 'case ::foo' is allowed. For generality, we just expect that the second argument to
2660 * 'case' is not an operator.
2661 */
2662 if ((strcmp(buff[1], "case") == 0) ||
2663 (strcmp(buff[1], "limit") == 0) ||
2664 (strcmp(buff[1], "timeout") == 0)) {
2665 break;
2666 }
2667
2668 /*
2669 * It's not a string, bare word, or attribute reference. It must be an operator.
2670 */
2671 if (!((*ptr == '"') || (*ptr == '`') || (*ptr == '\'') || ((*ptr == '&') && (ptr[1] != '=')) ||
2672 ((*((uint8_t const *) ptr) & 0x80) != 0) || isalpha((uint8_t) *ptr) || isdigit((uint8_t) *ptr))) {
2673 goto operator;
2674 }
2675 break;
2676 }
2677
2678 /*
2679 * The second name could be a bare word, xlat expansion, string etc.
2680 */
2681 if (cf_get_token(parent, &ptr, &name2_token, buff[2], stack->bufsize,
2682 frame->filename, frame->lineno) < 0) {
2683 return -1;
2684 }
2685
2686 if (*ptr != '{') {
2687 return parse_error(stack, ptr, "Expected '{'");
2688 }
2689 ptr++;
2690 value = buff[2];
2691
2692alloc_section:
2693 parent->allow_locals = false;
2694
2695 /*
2696 * @policy foo { ...}
2697 *
2698 * Means "add foo to the policy section". And if
2699 * policy{} doesn't exist, create it, and then mark up
2700 * policy{} with a flag "we need to merge it", so that
2701 * when we read the actual policy{}, we merge the
2702 * contents together, instead of creating a second
2703 * policy{}.
2704 *
2705 * @todo - allow for '.' in @.ref Or at least test it. :(
2706 *
2707 * @todo - allow for two section names @ref foo bar {...}
2708 *
2709 * @todo - maybe we can use this to overload things in
2710 * virtual servers, and in modules?
2711 */
2712 if (buff[1][0] == '@') {
2713 CONF_ITEM *ci;
2714 CONF_SECTION *root;
2715 char const *name = &buff[1][1];
2716
2717 if (!value) {
2718 ERROR("%s[%d]: Missing section name for reference", frame->filename, frame->lineno);
2719 return -1;
2720 }
2721
2722 root = cf_root(parent);
2723
2724 ci = cf_reference_item(root, parent, name);
2725 if (!ci) {
2726 if (name[1] == '.') {
2727 PERROR("%s[%d]: Failed finding reference \"%s\"", frame->filename, frame->lineno, name);
2728 return -1;
2729 }
2730
2731 css = cf_section_alloc(root, root, name, NULL);
2732 if (!css) goto oom;
2733
2734 cf_filename_set(css, frame->filename);
2735 cf_lineno_set(css, frame->lineno);
2736 css->name2_quote = name2_token;
2737 css->unlang = CF_UNLANG_NONE;
2738 css->allow_locals = false;
2739 css->at_reference = true;
2740 parent = css;
2741
2742 /*
2743 * Copy this code from below. :(
2744 */
2745 if (cf_item_to_section(parent->item.parent) == root) {
2746 if (strcmp(css->name1, "server") == 0) css->unlang = CF_UNLANG_SERVER;
2747 if (strcmp(css->name1, "policy") == 0) css->unlang = CF_UNLANG_POLICY;
2748 if (strcmp(css->name1, "modules") == 0) css->unlang = CF_UNLANG_MODULES;
2749 if (strcmp(css->name1, "templates") == 0) css->unlang = CF_UNLANG_CAN_HAVE_UPDATE;
2750 }
2751
2752 } else {
2753 if (!cf_item_is_section(ci)) {
2754 ERROR("%s[%d]: Reference \"%s\" is not a section", frame->filename, frame->lineno, name);
2755 return -1;
2756 }
2757
2758 /*
2759 * Set the new parent and ensure we're
2760 * not creating a duplicate section.
2761 */
2763 css = cf_section_find(parent, value, NULL);
2764 if (css) {
2765 ERROR("%s[%d]: Reference \"%s\" already contains a \"%s\" section at %s[%d]",
2766 frame->filename, frame->lineno, name, value,
2767 css->item.filename, css->item.lineno);
2768 return -1;
2769 }
2770 }
2771
2772 /*
2773 * We're processing a section. The @reference is
2774 * OUTSIDE of this section.
2775 */
2776 fr_assert(frame->current == frame->parent);
2777 frame->at_reference = frame->parent;
2778 name2_token = T_BARE_WORD;
2779
2780 css = cf_section_alloc(parent, parent, value, NULL);
2781 } else {
2782 /*
2783 * Check if there's already an auto-created
2784 * section of this name. If so, just use that
2785 * section instead of allocating a new one.
2786 */
2787 css = cf_section_find(parent, buff[1], value);
2788 if (css && css->at_reference) {
2789 css->at_reference = false;
2790 } else {
2792 }
2793 }
2794
2795 if (!css) {
2796 oom:
2797 ERROR("%s[%d]: Failed allocating memory for section",
2798 frame->filename, frame->lineno);
2799 return -1;
2800 }
2801
2802 cf_filename_set(css, frame->filename);
2803 cf_lineno_set(css, frame->lineno);
2804 css->name2_quote = name2_token;
2805 css->unlang = CF_UNLANG_NONE;
2806 css->allow_locals = false;
2807
2808 /*
2809 * Only a few top-level sections allow "unlang"
2810 * statements. And for those, "unlang"
2811 * statements are only allowed in child
2812 * subsection.
2813 */
2814 switch (parent->unlang) {
2815 case CF_UNLANG_NONE:
2816 if (!parent->item.parent) {
2817 if (strcmp(css->name1, "server") == 0) css->unlang = CF_UNLANG_SERVER;
2818 if (strcmp(css->name1, "policy") == 0) css->unlang = CF_UNLANG_POLICY;
2819 if (strcmp(css->name1, "modules") == 0) css->unlang = CF_UNLANG_MODULES;
2820 if (strcmp(css->name1, "templates") == 0) css->unlang = CF_UNLANG_CAN_HAVE_UPDATE;
2821
2822 } else if ((cf_item_to_section(parent->item.parent)->unlang == CF_UNLANG_MODULES) &&
2823 (strcmp(css->name1, "update") == 0)) {
2824 /*
2825 * Module configuration can contain "update" statements.
2826 */
2828 css->allow_locals = false;
2829 }
2830 break;
2831
2832 /*
2833 * It's a policy section - allow unlang inside of child sections.
2834 */
2835 case CF_UNLANG_POLICY:
2836 css->unlang = CF_UNLANG_ALLOW;
2837 css->allow_locals = true;
2838 break;
2839
2840 /*
2841 * A virtual server has processing sections, but only a limited number of them.
2842 * Rather than trying to autoload them and glue the interpreter into the conf
2843 * file parser, we just hack it.
2844 */
2845 case CF_UNLANG_SERVER:
2846 // git grep SECTION_NAME src/process/ src/lib/server/process.h | sed 's/.*SECTION_NAME("//;s/",.*//' | sort -u
2848 css->unlang = CF_UNLANG_ALLOW;
2849 css->allow_locals = true;
2850 break;
2851 }
2852
2853 /*
2854 * Allow local variables, but no unlang statements.
2855 */
2856 if (strcmp(css->name1, "dictionary") == 0) {
2858 css->allow_locals = true;
2859 break;
2860 }
2861
2862 /*
2863 * ldap sync has "update" a few levels down.
2864 */
2865 if (strcmp(css->name1, "listen") == 0) {
2867 }
2868 break;
2869
2870 /*
2871 * Virtual modules in the "modules" section can have unlang.
2872 */
2873 case CF_UNLANG_MODULES:
2874 if ((strcmp(css->name1, "group") == 0) ||
2875 (strcmp(css->name1, "load-balance") == 0) ||
2876 (strcmp(css->name1, "redundant") == 0) ||
2877 (strcmp(css->name1, "redundant-load-balance") == 0)) {
2878 css->unlang = CF_UNLANG_ALLOW;
2879 css->allow_locals = true;
2880 } else {
2882 }
2883 break;
2884
2885 case CF_UNLANG_EDIT:
2886 /*
2887 * Edit sections can only have children which are edit sections.
2888 */
2889 css->unlang = CF_UNLANG_EDIT;
2890 break;
2891
2892 case CF_UNLANG_ALLOW:
2893 /*
2894 * If we are doing list assignment, then don't allow local variables. The children are
2895 * also then all edit sections, and not unlang statements.
2896 *
2897 * If we're not doing list assignment, then name2 has to be a bare word, string, etc.
2898 */
2899 css->allow_locals = !fr_list_assignment_op[name2_token];
2900 if (css->allow_locals) {
2901 /*
2902 * @todo - tighten this up for "actions" sections, and module rcode
2903 * over-rides.
2904 *
2905 * Perhaps the best way to do that is to change the syntax for module
2906 * over-rides, so that the parser doesn't have to guess. :(
2907 */
2908 css->unlang = CF_UNLANG_ALLOW;
2909 } else {
2910 css->unlang = CF_UNLANG_EDIT;
2911 }
2912 break;
2913
2914 /*
2915 * We can (maybe?) do nested assignments inside of an old-style "update" or "map" section
2916 */
2919 break;
2920
2923 css->allow_locals = true;
2924 break;
2925
2927 if (strcmp(css->name1, "update") == 0) {
2929 } else {
2931 }
2932 break;
2933 }
2934
2935add_section:
2936 /*
2937 * The current section is now the child section.
2938 */
2939 frame->current = css;
2940 frame->braces++;
2941 css = NULL;
2942 stack->ptr = ptr;
2943 return 1;
2944
2945
2946 /*
2947 * If we're not parsing a section, then the next
2948 * token MUST be an operator.
2949 */
2950operator:
2951 ptr2 = ptr;
2952 name2_token = gettoken(&ptr, buff[2], stack->bufsize, false);
2953 switch (name2_token) {
2954 case T_OP_ADD_EQ:
2955 case T_OP_SUB_EQ:
2956 case T_OP_AND_EQ:
2957 case T_OP_OR_EQ:
2958 case T_OP_NE:
2959 case T_OP_RSHIFT_EQ:
2960 case T_OP_GE:
2961 case T_OP_GT:
2962 case T_OP_LSHIFT_EQ:
2963 case T_OP_LE:
2964 case T_OP_LT:
2965 case T_OP_CMP_EQ:
2966 case T_OP_CMP_FALSE:
2967 case T_OP_SET:
2968 case T_OP_PREPEND:
2969 /*
2970 * Allow more operators in unlang statements, edit sections, and old-style "update" sections.
2971 */
2972 if ((parent->unlang != CF_UNLANG_ALLOW) && (parent->unlang != CF_UNLANG_EDIT) && (parent->unlang != CF_UNLANG_ASSIGNMENT)) {
2973 return parse_error(stack, ptr2, "Invalid operator for assignment");
2974 }
2976
2977 case T_OP_EQ:
2978 /*
2979 * Configuration variables can only use =
2980 */
2981 fr_skip_whitespace(ptr);
2982 op_token = name2_token;
2983 break;
2984
2985 default:
2986 return parse_error(stack, ptr2, "Syntax error, the input should be an assignment operator");
2987 }
2988
2989 /*
2990 * MUST have something after the operator.
2991 */
2992 if (!*ptr || (*ptr == '#') || (*ptr == ',') || (*ptr == ';')) {
2993 return parse_error(stack, ptr, "Missing value after operator");
2994 }
2995
2996 /*
2997 * foo = { ... } for nested groups.
2998 *
2999 * As a special case, we allow sub-sections after '=', etc.
3000 *
3001 * This syntax is only for inside of "update"
3002 * sections, and for attributes of type "group".
3003 * But the parser isn't (yet) smart enough to
3004 * know about that context. So we just silently
3005 * allow it everywhere.
3006 */
3007 if (*ptr == '{') {
3008 if ((parent->unlang != CF_UNLANG_ALLOW) && (parent->unlang != CF_UNLANG_EDIT)) {
3009 return parse_error(stack, ptr, "Invalid location for nested attribute assignment");
3010 }
3011
3012 if (!fr_list_assignment_op[name2_token]) {
3013 return parse_error(stack, ptr, "Invalid assignment operator for list");
3014 }
3015
3016 /*
3017 * Now that we've peeked ahead to
3018 * see the open brace, parse it
3019 * for real.
3020 */
3021 ptr++;
3022
3023 /*
3024 * Leave name2_token as the
3025 * operator (as a hack). But
3026 * note that there's no actual
3027 * name2. We'll deal with that
3028 * situation later.
3029 */
3030 value = NULL;
3031 goto alloc_section;
3032 }
3033
3034 fr_skip_whitespace(ptr);
3035
3036 /*
3037 * Parse the value for a CONF_PAIR.
3038 *
3039 * If it's unlang or an edit section, the RHS can be an expression.
3040 */
3041 if ((parent->unlang == CF_UNLANG_ALLOW) || (parent->unlang == CF_UNLANG_EDIT)) {
3042 bool eol;
3043 ssize_t slen;
3044
3045 ptr2 = ptr;
3046
3047 /*
3048 * If the RHS is an expression (foo) or function %foo(), then mark it up as an expression.
3049 */
3050 if ((*ptr == '(') || (*ptr == '%')) {
3051 /* nothing */
3052
3053 } else if (cf_get_token(parent, &ptr2, &value_token, buff[2], stack->bufsize,
3054 frame->filename, frame->lineno) == 0) {
3055 /*
3056 * We have one token (bare word), followed by EOL. It's just a token.
3057 */
3058 fr_skip_whitespace(ptr2);
3059 if (terminal_end_line[(uint8_t) *ptr2]) {
3060 parent->allow_locals = false;
3061 ptr = ptr2;
3062 value = buff[2];
3063 goto alloc_pair;
3064 }
3065 } /* else it looks like an expression */
3066
3067 /*
3068 * Parse the text as an expression.
3069 *
3070 * Note that unlike conditions, expressions MUST use \ at the EOL for continuation.
3071 * If we automatically read past EOL, as with:
3072 *
3073 * &foo := (bar -
3074 * baz)
3075 *
3076 * That works, mostly. Until the user forgets to put the trailing ')', and then
3077 * the parse is bad enough that it tries to read to EOF, or to some other random
3078 * parse error.
3079 *
3080 * So the simplest way to avoid utter craziness is to simply require a signal which
3081 * says "yes, I intended to put this over multiple lines".
3082 */
3083 slen = fr_skip_condition(ptr, NULL, terminal_end_line, &eol);
3084 if (slen < 0) {
3085 return parse_error(stack, ptr + (-slen), fr_strerror());
3086 }
3087
3088 /*
3089 * We parsed until the end of the string, but the condition still needs more data.
3090 */
3091 if (eol) {
3092 return parse_error(stack, ptr + slen, "Expression is unfinished at end of line");
3093 }
3094
3095 /*
3096 * Keep a copy of the entire RHS.
3097 */
3098 memcpy(buff[2], ptr, slen);
3099 buff[2][slen] = '\0';
3100
3101 value = buff[2];
3102
3103 /*
3104 * Mark it up as an expression
3105 *
3106 * @todo - we should really just call cf_data_add() to add a flag, but this is good for
3107 * now. See map_afrom_cp()
3108 */
3109 value_token = T_HASH;
3110
3111 /*
3112 * Skip terminal characters
3113 */
3114 ptr += slen;
3115 if ((*ptr == ',') || (*ptr == ';')) ptr++;
3116
3117#if 0
3118 } else if ((parent->unlang != CF_UNLANG_ASSIGNMENT) &&
3119 ((*ptr == '`') || (*ptr == '%') || (*ptr == '('))) {
3120 /*
3121 * Config sections can't use backticks, xlat expansions, or expressions.
3122 *
3123 * Except module configurations can have key = %{...}
3124 */
3125 return parse_error(stack, ptr, "Invalid value for assignment in configuration file");
3126#endif
3127
3128 } else {
3129 if (cf_get_token(parent, &ptr, &value_token, buff[2], stack->bufsize,
3130 frame->filename, frame->lineno) < 0) {
3131 return -1;
3132 }
3133 value = buff[2];
3134 }
3135
3136 /*
3137 * We have an attribute assignment, which means that we no longer allow local variables to be
3138 * defined.
3139 */
3140 parent->allow_locals = false;
3141
3142alloc_pair:
3143 if (add_pair(parent, buff[1], value, name1_token, op_token, value_token, buff[3], frame->filename, frame->lineno) < 0) return -1;
3144
3145added_pair:
3146 fr_skip_whitespace(ptr);
3147
3148 /*
3149 * Skip semicolon if we see it after a
3150 * CONF_PAIR. Also allow comma for
3151 * backwards compatibility with secret
3152 * things in v3.
3153 */
3154 if ((*ptr == ';') || (*ptr == ',')) {
3155 ptr++;
3156 stack->ptr = ptr;
3157 return 1;
3158 }
3159
3160 /*
3161 * Closing brace is allowed after a CONF_PAIR
3162 * definition.
3163 */
3164 if (*ptr == '}') {
3165 stack->ptr = ptr;
3166 return 1;
3167 }
3168
3169 /*
3170 * Anything OTHER than EOL or comment is a syntax
3171 * error.
3172 */
3173 if (*ptr && (*ptr != '#')) {
3174 return parse_error(stack, ptr, "Unexpected text after configuration item");
3175 }
3176
3177 /*
3178 * Since we're at EOL or comment, just drop the
3179 * text, and go read another line of text.
3180 */
3181 return 0;
3182}
3183
3184
3186{
3187 cf_stack_frame_t *frame = &stack->frame[stack->depth];
3188 CONF_SECTION *parent = frame->current;
3189 cf_file_heap_t *h;
3190
3191 h = fr_heap_pop(&frame->heap);
3192 if (!h) {
3193 /*
3194 * Done reading the directory entry. Close it, and go
3195 * back up a stack frame.
3196 */
3197 talloc_free(frame->directory);
3198 stack->depth--;
3199 return 1;
3200 }
3201
3202 /*
3203 * Push the next filename onto the stack.
3204 */
3205 stack->depth++;
3206 frame = &stack->frame[stack->depth];
3207 memset(frame, 0, sizeof(*frame));
3208
3209 frame->type = CF_STACK_FILE;
3210 frame->fp = NULL;
3211 frame->parent = parent;
3212 frame->current = parent;
3213 frame->filename = h->filename;
3214 frame->lineno = 0;
3215 frame->from_dir = true;
3216 return 1;
3217}
3218
3219
3221
3222void cf_md5_init(void)
3223{
3225}
3226
3227
3228static void cf_md5_update(char const *p)
3229{
3230 if (!cf_md5_ctx) return;
3231
3232 fr_md5_update(cf_md5_ctx, (uint8_t const *)p, strlen(p));
3233}
3234
3236{
3237 if (!cf_md5_ctx) {
3238 memset(digest, 0, MD5_DIGEST_LENGTH);
3239 return;
3240 }
3241
3242 fr_md5_final(digest, cf_md5_ctx);
3244 cf_md5_ctx = NULL;
3245}
3246
3248{
3249 bool at_eof, has_spaces;
3250 size_t len;
3251 char const *ptr;
3252 cf_stack_frame_t *frame = &stack->frame[stack->depth];
3253
3254read_more:
3255 has_spaces = false;
3256
3257read_continuation:
3258 /*
3259 * Get data, and remember if we are at EOF.
3260 */
3261 at_eof = (fgets(stack->fill, stack->bufsize - (stack->fill - stack->buff[0]), frame->fp) == NULL);
3262 cf_md5_update(stack->fill);
3263 frame->lineno++;
3264
3265 /*
3266 * We read the entire 8k worth of data: complain.
3267 * Note that we don't care if the last character
3268 * is \n: it's still forbidden. This means that
3269 * the maximum allowed length of text is 8k-1, which
3270 * should be plenty.
3271 */
3272 len = strlen(stack->fill);
3273 if ((stack->fill + len + 1) >= (stack->buff[0] + stack->bufsize)) {
3274 ERROR("%s[%d]: Line too long", frame->filename, frame->lineno);
3275 return -1;
3276 }
3277
3278 /*
3279 * Suppress leading whitespace after a
3280 * continuation line.
3281 */
3282 if (has_spaces) {
3283 ptr = stack->fill;
3284 fr_skip_whitespace(ptr);
3285
3286 if (ptr > stack->fill) {
3287 memmove(stack->fill, ptr, len - (ptr - stack->fill));
3288 len -= (ptr - stack->fill);
3289 }
3290 }
3291
3292 /*
3293 * Skip blank lines when we're at the start of
3294 * the read buffer.
3295 */
3296 if (stack->fill == stack->buff[0]) {
3297 if (at_eof) return 0;
3298
3299 ptr = stack->buff[0];
3300 fr_skip_whitespace(ptr);
3301
3302 if (!*ptr || (*ptr == '#')) goto read_more;
3303
3304 } else if (at_eof || (len == 0)) {
3305 ERROR("%s[%d]: Continuation at EOF is illegal", frame->filename, frame->lineno);
3306 return -1;
3307 }
3308
3309 /*
3310 * See if there's a continuation.
3311 */
3312 while ((len > 0) &&
3313 ((stack->fill[len - 1] == '\n') || (stack->fill[len - 1] == '\r'))) {
3314 len--;
3315 stack->fill[len] = '\0';
3316 }
3317
3318 if ((len > 0) && (stack->fill[len - 1] == '\\')) {
3319 /*
3320 * Check for "suppress spaces" magic.
3321 */
3322 if (!has_spaces && (len > 2) && (stack->fill[len - 2] == '"')) {
3323 has_spaces = true;
3324 }
3325
3326 stack->fill[len - 1] = '\0';
3327 stack->fill += len - 1;
3328 goto read_continuation;
3329 }
3330
3331 ptr = stack->fill;
3332
3333 /*
3334 * We now have one full line of text in the input
3335 * buffer, without continuations.
3336 */
3337 fr_skip_whitespace(ptr);
3338
3339 /*
3340 * Nothing left, or just a comment. Go read
3341 * another line of text.
3342 */
3343 if (!*ptr || (*ptr == '#')) goto read_more;
3344
3345 return 1;
3346}
3347
3348
3349/*
3350 * Read a configuration file or files.
3351 */
3353{
3355 char const *ptr;
3356
3357 cf_stack_frame_t *frame;
3358 int rcode;
3359
3360do_frame:
3361 frame = &stack->frame[stack->depth];
3362 parent = frame->current; /* add items here */
3363
3364 switch (frame->type) {
3365#ifdef HAVE_GLOB_H
3366 case CF_STACK_GLOB:
3367 if (frame->gl_current == frame->glob.gl_pathc) {
3368 globfree(&frame->glob);
3369 goto pop_stack;
3370 }
3371
3372 /*
3373 * Process the filename as an include.
3374 */
3375 if (process_include(stack, parent, frame->glob.gl_pathv[frame->gl_current++], frame->required, false) < 0) return -1;
3376
3377 /*
3378 * Run the correct frame. If the file is NOT
3379 * required, then the call to process_include()
3380 * may return 0, and we just process the next
3381 * glob. Otherwise, the call to
3382 * process_include() may return a directory or a
3383 * filename. Go handle that.
3384 */
3385 goto do_frame;
3386#endif
3387
3388#ifdef HAVE_DIRENT_H
3389 case CF_STACK_DIR:
3390 rcode = frame_readdir(stack);
3391 if (rcode == 0) goto do_frame;
3392 if (rcode < 0) return -1;
3393
3394 /*
3395 * Reset which frame we're looking at.
3396 */
3397 frame = &stack->frame[stack->depth];
3398 fr_assert(frame->type == CF_STACK_FILE);
3399 break;
3400#endif
3401
3402 case CF_STACK_FILE:
3403 break;
3404 }
3405
3406#ifndef NDEBUG
3407 /*
3408 * One last sanity check.
3409 */
3410 if (frame->type != CF_STACK_FILE) {
3411 cf_log_err(frame->current, "%s: Internal sanity check failed", __FUNCTION__);
3412 goto pop_stack;
3413 }
3414#endif
3415
3416 /*
3417 * Open the new file if necessary. It either came from
3418 * the first call to the function, or was pushed onto the
3419 * stack by another function.
3420 */
3421 if (!frame->fp) {
3422 rcode = cf_file_open(frame->parent, frame->filename, frame->from_dir, &frame->fp);
3423 if (rcode < 0) return -1;
3424
3425 /*
3426 * Ignore this file
3427 */
3428 if (rcode == 1) {
3429 cf_log_warn(frame->current, "Ignoring file %s - it was already read",
3430 frame->filename);
3431 goto pop_stack;
3432 }
3433 }
3434
3435 /*
3436 * Read, checking for line continuations ('\\' at EOL)
3437 */
3438 for (;;) {
3439 /*
3440 * Fill the buffers with data.
3441 */
3442 stack->fill = stack->buff[0];
3443 rcode = cf_file_fill(stack);
3444 if (rcode < 0) return -1;
3445 if (rcode == 0) break;
3446
3447 /*
3448 * The text here MUST be at the start of a line,
3449 * OR have only whitespace in front of it.
3450 */
3451 ptr = stack->buff[0];
3452 fr_skip_whitespace(ptr);
3453
3454 if (*ptr == '$') {
3455 /*
3456 * Allow for $INCLUDE files
3457 */
3458 if (strncasecmp(ptr, "$INCLUDE", 8) == 0) {
3459 ptr += 8;
3460
3461 if (process_include(stack, parent, ptr, true, true) < 0) return -1;
3462 goto do_frame;
3463 }
3464
3465 if (strncasecmp(ptr, "$-INCLUDE", 9) == 0) {
3466 ptr += 9;
3467
3468 rcode = process_include(stack, parent, ptr, false, true);
3469 if (rcode < 0) return -1;
3470 if (rcode == 0) continue;
3471 goto do_frame;
3472 }
3473
3474 /*
3475 * Allow for $TEMPLATE things
3476 */
3477 if (strncasecmp(ptr, "$TEMPLATE", 9) == 0) {
3478 ptr += 9;
3479 fr_skip_whitespace(ptr);
3480
3481 stack->ptr = ptr;
3482 if (process_template(stack) < 0) return -1;
3483 continue;
3484 }
3485
3486 return parse_error(stack, ptr, "Unknown $... keyword");
3487 }
3488
3489 /*
3490 * All of the file handling code is done. Parse the input.
3491 */
3492 do {
3493 fr_skip_whitespace(ptr);
3494 if (!*ptr || (*ptr == '#')) break;
3495
3496 stack->ptr = ptr;
3497 rcode = parse_input(stack);
3498 ptr = stack->ptr;
3499
3500 if (rcode < 0) return -1;
3501 parent = frame->current;
3502 } while (rcode == 1);
3503 }
3504
3505 fr_assert(frame->fp != NULL);
3506
3507 /*
3508 * See if EOF was unexpected.
3509 */
3510 if (feof(frame->fp) && (parent != frame->parent)) {
3511 ERROR("%s[%d]: EOF reached without closing brace for section %s starting at line %d",
3513 return -1;
3514 }
3515
3516 fclose(frame->fp);
3517 frame->fp = NULL;
3518
3519pop_stack:
3520 /*
3521 * More things to read, go read them.
3522 */
3523 if (stack->depth > 0) {
3524 stack->depth--;
3525 goto do_frame;
3526 }
3527
3528 return 0;
3529}
3530
3532{
3533 cf_stack_frame_t *frame = &stack->frame[stack->depth];
3534
3535 while (stack->depth >= 0) {
3536 switch (frame->type) {
3537 case CF_STACK_FILE:
3538 if (frame->fp) fclose(frame->fp);
3539 frame->fp = NULL;
3540 break;
3541
3542#ifdef HAVE_DIRENT_H
3543 case CF_STACK_DIR:
3544 talloc_free(frame->directory);
3545 break;
3546#endif
3547
3548#ifdef HAVE_GLOB_H
3549 case CF_STACK_GLOB:
3550 globfree(&frame->glob);
3551 break;
3552#endif
3553 }
3554
3555 frame--;
3556 stack->depth--;
3557 }
3558
3559 talloc_free(stack->buff);
3560}
3561
3562/*
3563 * Bootstrap a config file.
3564 */
3565int cf_file_read(CONF_SECTION *cs, char const *filename)
3566{
3567 int i;
3568 char *p;
3569 CONF_PAIR *cp;
3570 fr_rb_tree_t *tree;
3572 cf_stack_frame_t *frame;
3573
3574 cp = cf_pair_alloc(cs, "confdir", filename, T_OP_EQ, T_BARE_WORD, T_SINGLE_QUOTED_STRING);
3575 if (!cp) return -1;
3576
3577 p = strrchr(cp->value, FR_DIR_SEP);
3578 if (p) *p = '\0';
3579
3580 MEM(tree = fr_rb_inline_talloc_alloc(cs, cf_file_t, node, _inode_cmp, NULL));
3581
3582 cf_data_add(cs, tree, "filename", false);
3583
3584#ifndef NDEBUG
3585 memset(&stack, 0, sizeof(stack));
3586#endif
3587
3588 /*
3589 * Allocate temporary buffers on the heap (so we don't use *all* the stack space)
3590 */
3591 stack.buff = talloc_array(cs, char *, 4);
3592 for (i = 0; i < 4; i++) MEM(stack.buff[i] = talloc_array(stack.buff, char, 8192));
3593
3594 stack.depth = 0;
3595 stack.bufsize = 8192;
3596 frame = &stack.frame[stack.depth];
3597
3598 memset(frame, 0, sizeof(*frame));
3599 frame->parent = frame->current = cs;
3600
3601 frame->type = CF_STACK_FILE;
3602 frame->filename = talloc_strdup(frame->parent, filename);
3603 cs->item.filename = frame->filename;
3604
3605 if (cf_file_include(&stack) < 0) {
3607 return -1;
3608 }
3609
3610 talloc_free(stack.buff);
3611
3612 /*
3613 * Now that we've read the file, go back through it and
3614 * expand the variables.
3615 */
3616 if (cf_section_pass2(cs) < 0) {
3617 cf_log_err(cs, "Parsing config items failed");
3618 return -1;
3619 }
3620
3621 return 0;
3622}
3623
3625{
3626 talloc_free(cs);
3627}
3628
3629static char const parse_tabs[] = " ";
3630
3631static ssize_t cf_string_write(FILE *fp, char const *string, size_t len, fr_token_t t)
3632{
3633 size_t outlen;
3634 char c;
3635 char buffer[2048];
3636
3637 switch (t) {
3638 default:
3639 c = '\0';
3640 break;
3641
3643 c = '"';
3644 break;
3645
3647 c = '\'';
3648 break;
3649
3651 c = '`';
3652 break;
3653 }
3654
3655 if (c) fprintf(fp, "%c", c);
3656
3657 outlen = fr_snprint(buffer, sizeof(buffer), string, len, c);
3658 fwrite(buffer, outlen, 1, fp);
3659
3660 if (c) fprintf(fp, "%c", c);
3661 return 1;
3662}
3663
3664static int cf_pair_write(FILE *fp, CONF_PAIR *cp)
3665{
3666 if (!cp->value) {
3667 fprintf(fp, "%s\n", cp->attr);
3668 return 0;
3669 }
3670
3671 cf_string_write(fp, cp->attr, strlen(cp->attr), cp->lhs_quote);
3672 fprintf(fp, " %s ", fr_table_str_by_value(fr_tokens_table, cp->op, "<INVALID>"));
3673 cf_string_write(fp, cp->value, strlen(cp->value), cp->rhs_quote);
3674 fprintf(fp, "\n");
3675
3676 return 1; /* FIXME */
3677}
3678
3679
3680int cf_section_write(FILE *fp, CONF_SECTION *cs, int depth)
3681{
3682 if (!fp || !cs) return -1;
3683
3684 /*
3685 * Print the section name1, etc.
3686 */
3687 fwrite(parse_tabs, depth, 1, fp);
3688 cf_string_write(fp, cs->name1, strlen(cs->name1), T_BARE_WORD);
3689
3690 /*
3691 * FIXME: check for "if" or "elsif". And if so, print
3692 * out the parsed condition, instead of the input text
3693 *
3694 * cf_data_find(cs, CF_DATA_TYPE_UNLANG, "if");
3695 */
3696 if (cs->name2) {
3697 fputs(" ", fp);
3698
3699#if 0
3700 c = cf_data_value(cf_data_find(cs, fr_cond_t, NULL));
3701 if (c) {
3702 char buffer[1024];
3703
3704 cond_print(&FR_SBUFF_OUT(buffer, sizeof(buffer)), c);
3705 fprintf(fp, "(%s)", buffer);
3706 } else
3707#endif
3708 cf_string_write(fp, cs->name2, strlen(cs->name2), cs->name2_quote);
3709 }
3710
3711 fputs(" {\n", fp);
3712
3713 /*
3714 * Loop over the children. Either recursing, or opening
3715 * a new file.
3716 */
3717 cf_item_foreach(&cs->item, ci) {
3718 switch (ci->type) {
3719 case CONF_ITEM_SECTION:
3721 break;
3722
3723 case CONF_ITEM_PAIR:
3724 /*
3725 * Ignore internal things.
3726 */
3727 if (!ci->filename || (ci->filename[0] == '<')) break;
3728
3729 fwrite(parse_tabs, depth + 1, 1, fp);
3731 break;
3732
3733 default:
3734 break;
3735 }
3736 }
3737
3738 fwrite(parse_tabs, depth, 1, fp);
3739 fputs("}\n\n", fp);
3740
3741 return 1;
3742}
3743
3744
3746 CONF_SECTION const *outer_cs,
3747 char const *ptr)
3748{
3749 CONF_PAIR *cp;
3750 CONF_SECTION *next;
3751 CONF_SECTION const *cs = outer_cs;
3752 char name[8192], *p;
3753 char const *name2;
3754
3755 if (!ptr || (!parent_cs && !outer_cs)) {
3756 fr_strerror_const("Invalid argument");
3757 return NULL;
3758 }
3759
3760 if (!*ptr) {
3761 fr_strerror_const("Empty string is invalid");
3762 return NULL;
3763 }
3764
3765 strlcpy(name, ptr, sizeof(name));
3766
3767 p = name;
3768
3769 /*
3770 * ".foo" means "foo from the current section"
3771 */
3772 if (*p == '.') {
3773 p++;
3774
3775 /*
3776 * Just '.' means the current section
3777 */
3778 if (*p == '\0') return cf_section_to_item(cs); /* const issues */
3779
3780 /*
3781 * ..foo means "foo from the section
3782 * enclosing this section" (etc.)
3783 */
3784 while (*p == '.') {
3785 if (cs->item.parent) cs = cf_item_to_section(cs->item.parent);
3786
3787 /*
3788 * .. means the section
3789 * enclosing this section
3790 */
3791 if (!*++p) return cf_section_to_item(cs); /* const issues */
3792 }
3793
3794 /*
3795 * "foo.bar.baz" means "from the given root"
3796 */
3797 } else if (strchr(p, '.') != NULL) {
3798 if (!parent_cs) {
3799 missing_parent:
3800 fr_strerror_const("Missing parent configuration section");
3801 return NULL;
3802 }
3803 cs = parent_cs;
3804
3805 /*
3806 * "foo" could be from the current section, either as a
3807 * section or as a pair.
3808 *
3809 * If that isn't found, search from the given root.
3810 */
3811 } else {
3812 next = cf_section_find(cs, p, NULL);
3813 if (!next && cs->template) next = cf_section_find(cs->template, p, NULL);
3814 if (next) return &(next->item);
3815
3816 cp = cf_pair_find(cs, p);
3817 if (!cp && cs->template) cp = cf_pair_find(cs->template, p);
3818 if (cp) return &(cp->item);
3819
3820 if (!parent_cs) goto missing_parent;
3821 cs = parent_cs;
3822 }
3823
3824 /*
3825 * Chop the string into pieces, and look up the pieces.
3826 */
3827 while (*p) {
3828 char *n1, *n2, *q;
3829
3830 n1 = p;
3831 n2 = NULL;
3832 q = p;
3833
3834 fr_assert(*q);
3835
3836 /*
3837 * Look for a terminating '.' or '[', to get name1 and possibly name2.
3838 */
3839 while (*q != '\0') {
3840 /*
3841 * foo.bar -> return "foo"
3842 */
3843 if (*q == '.') {
3844 *q++ = '\0'; /* leave 'q' after the '.' */
3845 break;
3846 }
3847
3848 /*
3849 * name1 is anything up to '[' or EOS.
3850 */
3851 if (*q != '[') {
3852 q++;
3853 continue;
3854 }
3855
3856 /*
3857 * Found "name1[", look for "name2]" or "name2]."
3858 */
3859 *q++ = '\0';
3860 n2 = q;
3861
3862 while (*q != '\0') {
3863 if (*q == '[') {
3864 fr_strerror_const("Invalid reference, '[' cannot be used inside of a '[...]' block");
3865 return NULL;
3866 }
3867
3868 if (*q != ']') {
3869 q++;
3870 continue;
3871 }
3872
3873 /*
3874 * We've found the trailing ']'
3875 */
3876 *q++ = '\0';
3877
3878 /*
3879 * "name2]"
3880 */
3881 if (!*q) break;
3882
3883 /*
3884 * Must be "name2]."
3885 */
3886 if (*q++ == '.') break;
3887
3888 fr_strerror_const("Invalid reference, ']' is not followed by '.'");
3889 return NULL;
3890 }
3891
3892 if (n2) break;
3893
3894 /*
3895 * "name1[name2", but not "name1[name2]"
3896 */
3897 fr_strerror_printf("Invalid reference after '%s', missing close ']'", n2);
3898 return NULL;
3899 }
3900 p = q; /* get it ready for the next round */
3901
3902 /*
3903 * End of the string. The thing could be a section with
3904 * two names, a section with one name, or a pair.
3905 *
3906 * And if we don't find the thing we're looking for here,
3907 * check the template section.
3908 */
3909 if (!*p) {
3910 /*
3911 * Two names, must be a section.
3912 */
3913 if (n2) {
3914 next = cf_section_find(cs, n1, n2);
3915 if (!next && cs->template) next = cf_section_find(cs->template, n1, n2);
3916 if (next) return &(next->item);
3917
3918 fail:
3919 name2 = cf_section_name2(cs);
3920 fr_strerror_printf("Parent section %s%s%s { ... } does not contain a %s %s { ... } configuration section",
3921 cf_section_name1(cs),
3922 name2 ? " " : "", name2 ? name2 : "",
3923 n1, n2);
3924 return NULL;
3925 }
3926
3927 /*
3928 * One name, the final thing can be a section or a pair.
3929 */
3930 next = cf_section_find(cs, n1, NULL);
3931 if (!next && cs->template) next = cf_section_find(cs->template, n1, NULL);
3932
3933 if (next) return &(next->item);
3934
3935 cp = cf_pair_find(cs, n1);
3936 if (!cp && cs->template) cp = cf_pair_find(cs->template, n1);
3937 if (cp) return &(cp->item);
3938
3939 name2 = cf_section_name2(cs);
3940 fr_strerror_printf("Parent section %s%s%s { ... } does not contain a %s configuration item",
3941 cf_section_name1(cs),
3942 name2 ? " " : "", name2 ? name2 : "",
3943 n1);
3944 return NULL;
3945 }
3946
3947 /*
3948 * There's more to the string. The thing we're looking
3949 * for MUST be a configuration section.
3950 */
3951 next = cf_section_find(cs, n1, n2);
3952 if (!next && cs->template) next = cf_section_find(cs->template, n1, n2);
3953 if (next) {
3954 cs = next;
3955 continue;
3956 }
3957
3958 if (n2) goto fail;
3959
3960 name2 = cf_section_name2(cs);
3961 fr_strerror_printf("Parent section %s%s%s { ... } does not contain a %s { ... } configuration section",
3962 cf_section_name1(cs),
3963 name2 ? " " : "", name2 ? name2 : "",
3964 n1);
3965 return NULL;
3966 }
3967
3968 /*
3969 * We've fallen off of the end of the string. This should not have happened!
3970 */
3971 fr_strerror_const("Cannot parse reference");
3972 return NULL;
3973}
3974
3975/*
3976 * Only for unit_test_map
3977 */
3979{
3981 fr_assert(!cs->item.parent);
3982
3983 cs->unlang = CF_UNLANG_ALLOW;
3984}
static int const char char buffer[256]
Definition acutest.h:576
int const char * file
Definition acutest.h:702
strcpy(log_entry->msg, buffer)
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define RCSID(id)
Definition build.h:485
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:324
#define CMP_RETURN(_a, _b, _field)
Return if the comparison is not 0 (is unequal)
Definition build.h:121
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition build.h:112
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
CONF_SECTION * current
sub-section we're reading
Definition cf_file.c:139
cf_stack_file_t type
Definition cf_file.c:112
static bool cf_template_merge(CONF_SECTION *cs, CONF_SECTION const *template)
Definition cf_file.c:478
static int parse_input(cf_stack_t *stack)
Definition cf_file.c:2334
char * fill
where we start filling the buffer from
Definition cf_file.c:157
void cf_section_set_unlang(CONF_SECTION *cs)
Definition cf_file.c:3978
conf_property
Definition cf_file.c:70
@ CONF_PROPERTY_NAME
Definition cf_file.c:72
@ CONF_PROPERTY_INSTANCE
Definition cf_file.c:73
@ CONF_PROPERTY_INVALID
Definition cf_file.c:71
static int unlang_keywords_len
Definition cf_file.c:2330
static fr_table_num_sorted_t const server_unlang_section[]
Definition cf_file.c:82
bool check_config
Definition cf_file.c:66
fr_heap_index_t heap_id
Definition cf_file.c:1083
static int frame_readdir(cf_stack_t *stack)
Definition cf_file.c:3185
static CONF_ITEM * process_map(cf_stack_t *stack)
Definition cf_file.c:1615
static CONF_ITEM * process_foreach(cf_stack_t *stack)
Definition cf_file.c:1986
static int add_pair(CONF_SECTION *parent, char const *attr, char const *value, fr_token_t name1_token, fr_token_t op_token, fr_token_t value_token, char *buff, char const *filename, int lineno)
Definition cf_file.c:2171
void cf_md5_init(void)
Definition cf_file.c:3222
#define RULES_VERIFY(_cs, _rules)
Definition cf_file.c:180
static int parse_type_name(cf_stack_t *stack, char const **ptr_p, char const *type_ptr, fr_type_t *type_p)
Definition cf_file.c:1935
static void cf_stack_cleanup(cf_stack_t *stack)
Definition cf_file.c:3531
static int cf_tmpl_rules_verify(CONF_SECTION *cs, tmpl_rules_t const *rules)
Definition cf_file.c:162
int depth
stack depth
Definition cf_file.c:155
char const * cf_expand_variables(char const *cf, int lineno, CONF_SECTION *outer_cs, char *output, size_t outsize, char const *input, ssize_t inlen, bool *soft_fail)
Definition cf_file.c:188
cf_file_check_err_t cf_file_check_unix_perm(char const *filename, UNUSED void *uctx)
Check if file exists, and is a socket.
Definition cf_file.c:811
static int cf_file_open(CONF_SECTION *cs, char const *filename, bool from_dir, FILE **fp_p)
Definition cf_file.c:563
static fr_md5_ctx_t * cf_md5_ctx
Definition cf_file.c:3220
static int8_t filename_cmp(void const *one, void const *two)
Definition cf_file.c:1086
int cf_section_write(FILE *fp, CONF_SECTION *cs, int depth)
Definition cf_file.c:3680
int lineno
line in that filename
Definition cf_file.c:115
int cf_file_read(CONF_SECTION *cs, char const *filename)
Definition cf_file.c:3565
cf_stack_file_t
Definition cf_file.c:100
@ CF_STACK_FILE
Definition cf_file.c:101
static const bool terminal_end_section[UINT8_MAX+1]
Definition cf_file.c:1432
static size_t conf_property_name_len
Definition cf_file.c:80
static fr_table_num_sorted_t const conf_property_name[]
Definition cf_file.c:76
int cf_section_pass2(CONF_SECTION *cs)
Definition cf_file.c:969
bool from_dir
this file was read from $include foo/
Definition cf_file.c:143
static ssize_t cf_string_write(FILE *fp, char const *string, size_t len, fr_token_t t)
Definition cf_file.c:3631
static char const parse_tabs[]
Definition cf_file.c:3629
cf_file_check_err_t cf_file_check(CONF_PAIR *cp, bool check_perms)
Do some checks on the file as an "input" file.
Definition cf_file.c:907
static int cf_file_include(cf_stack_t *stack)
Definition cf_file.c:3352
static uid_t conf_check_uid
Definition cf_file.c:67
static gid_t conf_check_gid
Definition cf_file.c:68
CONF_ITEM *(* cf_process_func_t)(cf_stack_t *)
Definition cf_file.c:2332
static int cf_get_token(CONF_SECTION *parent, char const **ptr_p, fr_token_t *token, char *buffer, size_t buflen, char const *filename, int lineno)
Definition cf_file.c:1032
static int8_t _inode_cmp(void const *one, void const *two)
Definition cf_file.c:554
CONF_SECTION * at_reference
was this thing an @foo ?
Definition cf_file.c:140
cf_file_check_err_t cf_file_check_open_read(char const *filename, void *uctx)
Callback for cf_file_check to open a file and check permissions.
Definition cf_file.c:857
static int cf_file_fill(cf_stack_t *stack)
Definition cf_file.c:3247
char const * ptr
current parse pointer
Definition cf_file.c:156
cf_file_check_err_t cf_file_check_effective(char const *filename, cf_file_check_err_t(*cb)(char const *filename, void *uctx), void *uctx)
Perform an operation with the effect/group set to conf_check_gid and conf_check_uid.
Definition cf_file.c:686
CONF_ITEM * cf_reference_item(CONF_SECTION const *parent_cs, CONF_SECTION const *outer_cs, char const *ptr)
Definition cf_file.c:3745
char const * filename
Definition cf_file.c:1082
static CONF_ITEM * process_subrequest(cf_stack_t *stack)
Definition cf_file.c:1703
void cf_file_check_set_uid_gid(uid_t uid, gid_t gid)
Set the euid/egid used when performing file checks.
Definition cf_file.c:671
enum conf_property CONF_PROPERTY
static fr_table_ptr_sorted_t unlang_keywords[]
Definition cf_file.c:2321
char ** buff
buffers for reading / parsing
Definition cf_file.c:153
CONF_SECTION * parent
which started this file
Definition cf_file.c:138
static int process_template(cf_stack_t *stack)
Definition cf_file.c:1375
size_t bufsize
size of the buffers
Definition cf_file.c:154
static int cf_pair_write(FILE *fp, CONF_PAIR *cp)
Definition cf_file.c:3664
static size_t server_unlang_section_len
Definition cf_file.c:98
void cf_md5_final(uint8_t *digest)
Definition cf_file.c:3235
static int process_include(cf_stack_t *stack, CONF_SECTION *parent, char const *ptr, bool required, bool relative)
Definition cf_file.c:1097
static CONF_ITEM * process_if(cf_stack_t *stack)
Definition cf_file.c:1448
static void cf_md5_update(char const *p)
Definition cf_file.c:3228
void cf_file_free(CONF_SECTION *cs)
Definition cf_file.c:3624
static char const * cf_local_file(char const *base, char const *filename, char *buffer, size_t bufsize)
Definition cf_file.c:1003
#define MAX_STACK
Definition cf_file.c:110
static CONF_ITEM * process_catch(cf_stack_t *stack)
Definition cf_file.c:1824
static const bool terminal_end_line[UINT8_MAX+1]
Definition cf_file.c:1436
cf_file_check_err_t cf_file_check_unix_connect(char const *filename, UNUSED void *uctx)
Check if we can connect to a unix socket.
Definition cf_file.c:738
char const * filename
filename we're reading
Definition cf_file.c:114
static CONF_ITEM * process_switch(cf_stack_t *stack)
Definition cf_file.c:2229
static int parse_error(cf_stack_t *stack, char const *ptr, char const *message)
Definition cf_file.c:1915
cf_file_check_err_t
Results of file checks.
Definition cf_file.h:45
@ CF_FILE_OK
File checks passed.
Definition cf_file.h:46
@ CF_FILE_NO_PERMISSION
Requested permissions not set.
Definition cf_file.h:47
@ CF_FILE_NO_UNIX_SOCKET
File is not a unix socket.
Definition cf_file.h:49
@ CF_FILE_OTHER_ERROR
Other error occurred checking permissions.
Definition cf_file.h:50
@ CF_FILE_NO_EXIST
File does not exist.
Definition cf_file.h:48
cf_parse_t on_read
Function to call as the item is being read, just after it has been allocated and initialized.
Definition cf_parse.h:619
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:599
CONF_SECTION * cs
CONF_SECTION associated with the file.
Definition cf_priv.h:139
CONF_ITEM item
Common set of fields.
Definition cf_priv.h:102
CONF_ITEM * parent
Parent.
Definition cf_priv.h:56
fr_token_t name2_quote
The type of quoting around name2.
Definition cf_priv.h:107
char const * name2
Second name token. Given foo bar {} would be bar.
Definition cf_priv.h:105
bool allow_locals
allow local variables
Definition cf_priv.h:116
int argc
number of additional arguments
Definition cf_priv.h:109
char const * attr
Attribute name.
Definition cf_priv.h:73
fr_token_t rhs_quote
Value Quoting style T_(DOUBLE|SINGLE|BACK)_QUOTE_STRING or T_BARE_WORD.
Definition cf_priv.h:78
char const * value
Attribute value.
Definition cf_priv.h:74
#define cf_item_foreach(_ci, _iter)
Iterate over the contents of a list.
Definition cf_priv.h:150
cf_unlang_t unlang
Definition cf_priv.h:115
char const * name1
First name token. Given foo bar {} would be foo.
Definition cf_priv.h:104
@ CF_UNLANG_MODULES
this section is in "modules", allow unlang 2 down
Definition cf_priv.h:91
@ CF_UNLANG_NONE
no unlang
Definition cf_priv.h:87
@ CF_UNLANG_CAN_HAVE_UPDATE
can have "update"
Definition cf_priv.h:95
@ CF_UNLANG_ALLOW
allow unlang in this section
Definition cf_priv.h:88
@ CF_UNLANG_POLICY
this section is a policy, allow unlang 2 down
Definition cf_priv.h:90
@ CF_UNLANG_ASSIGNMENT
only assignments inside of map / update
Definition cf_priv.h:93
@ CF_UNLANG_EDIT
only edit commands
Definition cf_priv.h:92
@ CF_UNLANG_DICTIONARY
only local variable definitions
Definition cf_priv.h:94
@ CF_UNLANG_SERVER
this section is a virtual server, allow unlang 2 down
Definition cf_priv.h:89
fr_token_t * argv_quote
Definition cf_priv.h:111
fr_token_t op
Operator e.g. =, :=.
Definition cf_priv.h:76
CONF_ITEM item
Common set of fields.
Definition cf_priv.h:71
bool pass2
do expansion in pass2.
Definition cf_priv.h:80
char const * filename
The file the config item was parsed from.
Definition cf_priv.h:64
struct stat buf
stat about the file
Definition cf_priv.h:140
bool at_reference
this thing was created from an ...
Definition cf_priv.h:117
CONF_SECTION * template
Definition cf_priv.h:119
char const * filename
name of the file
Definition cf_priv.h:138
@ CONF_ITEM_PAIR
Definition cf_priv.h:41
@ CONF_ITEM_SECTION
Definition cf_priv.h:42
char const ** argv
additional arguments
Definition cf_priv.h:110
fr_token_t lhs_quote
Name quoting style T_(DOUBLE|SINGLE|BACK)_QUOTE_STRING or T_BARE_WORD.
Definition cf_priv.h:77
int lineno
The line number the config item began on.
Definition cf_priv.h:63
CONF_ITEM_TYPE type
Whether the config item is a config_pair, conf_section or cf_data.
Definition cf_priv.h:61
Internal data that is associated with a configuration section.
Definition cf_priv.h:125
Common header for all CONF_* types.
Definition cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
CONF_PAIR * cf_pair_dup(CONF_SECTION *parent, CONF_PAIR *cp, bool copy_meta)
Duplicate a CONF_PAIR.
Definition cf_util.c:1320
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1184
void * cf_data_value(CONF_DATA const *cd)
Return the user assigned value of CONF_DATA.
Definition cf_util.c:1762
CONF_ITEM * cf_section_to_item(CONF_SECTION const *cs)
Cast a CONF_SECTION to a CONF_ITEM.
Definition cf_util.c:737
CONF_PAIR * cf_pair_alloc(CONF_SECTION *parent, char const *attr, char const *value, fr_token_t op, fr_token_t lhs_quote, fr_token_t rhs_quote)
Allocate a CONF_PAIR.
Definition cf_util.c:1278
char const * cf_section_name1(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
Definition cf_util.c:1170
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
Definition cf_util.c:1027
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
Definition cf_util.c:1438
bool cf_item_is_section(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_SECTION.
Definition cf_util.c:617
CONF_SECTION * cf_section_dup(TALLOC_CTX *ctx, CONF_SECTION *parent, CONF_SECTION const *cs, char const *name1, char const *name2, bool copy_meta)
Duplicate a configuration section.
Definition cf_util.c:927
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:663
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1593
CONF_ITEM * cf_pair_to_item(CONF_PAIR const *cp)
Cast a CONF_PAIR to a CONF_ITEM.
Definition cf_util.c:721
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
#define cf_lineno(_cf)
Definition cf_util.h:104
#define cf_data_add(_cf, _data, _name, _free)
Definition cf_util.h:255
#define cf_data_find(_cf, _type, _name)
Definition cf_util.h:244
#define cf_lineno_set(_ci, _lineno)
Definition cf_util.h:131
#define cf_root(_cf)
Definition cf_util.h:98
#define cf_parent(_cf)
Definition cf_util.h:101
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
Definition cf_util.h:367
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:296
#define cf_section_find_parent(_cf, _name1, _name2)
Definition cf_util.h:175
#define cf_section_alloc(_ctx, _parent, _name1, _name2)
Definition cf_util.h:140
#define CF_TO_ITEM(_cf)
Auto cast from the input type to CONF_ITEM (which is the base type)
Definition cf_util.h:65
#define cf_filename_set(_ci, _filename)
Definition cf_util.h:128
#define cf_log_warn(_cf, _fmt,...)
Definition cf_util.h:290
#define cf_log_debug(_cf, _fmt,...)
Definition cf_util.h:292
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:148
#define MEM(x)
Definition debug.h:36
static char const * spaces
Definition dependency.c:360
#define ERROR(fmt,...)
Definition dhcpclient.c:41
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition dict_util.c:2475
fr_dict_t const * fr_dict_internal(void)
Definition dict_util.c:4728
Test enumeration values.
Definition dict_test.h:92
int fr_dirfd(int *dirfd, char const **filename, char const *pathname)
From a pathname, return fd and filename needed for *at() functions.
Definition file.c:412
int fr_heap_insert(fr_heap_t **hp, void *data)
Insert a new element into the heap.
Definition heap.c:146
void * fr_heap_pop(fr_heap_t **hp)
Remove a node from the heap.
Definition heap.c:322
unsigned int fr_heap_index_t
Definition heap.h:80
#define fr_heap_alloc(_ctx, _cmp, _type, _field, _init)
Creates a heap that can be used with non-talloced elements.
Definition heap.h:100
#define FR_HEAP_INDEX_INVALID
Definition heap.h:83
The main heap structure.
Definition heap.h:66
#define PERROR(_fmt,...)
Definition log.h:228
talloc_free(reap)
void fr_canonicalize_error(TALLOC_CTX *ctx, char **sp, char **text, ssize_t slen, char const *fmt)
Canonicalize error strings, removing tabs, and generate spaces for error marker.
Definition log.c:87
static char * stack[MAX_STACK]
Definition radmin.c:159
fr_md5_ctx_alloc_t fr_md5_ctx_alloc
Definition md5.c:440
fr_md5_ctx_free_t fr_md5_ctx_free
Definition md5.c:441
fr_md5_update_t fr_md5_update
Definition md5.c:442
fr_md5_final_t fr_md5_final
Definition md5.c:443
void fr_md5_ctx_t
Definition md5.h:28
#define MD5_DIGEST_LENGTH
fr_type_t
@ FR_TYPE_TLV
Contains nested attributes.
@ FR_TYPE_MAX
Number of defined data types.
@ FR_TYPE_NULL
Invalid (uninitialised) attribute type.
@ FR_TYPE_VALUE_BOX
A boxed value.
@ FR_TYPE_VOID
User data.
@ FR_TYPE_GROUP
A grouping of other attributes.
long int ssize_t
unsigned char uint8_t
#define UINT8_MAX
static uint8_t depth(fr_minmax_heap_index_t i)
Definition minmax_heap.c:83
int strncasecmp(char *s1, char *s2, int n)
Definition missing.c:36
void fr_perm_file_error(int num)
Write a file access error to the fr_strerror buffer, including euid/egid.
Definition perm.c:531
size_t fr_snprint(char *out, size_t outlen, char const *in, ssize_t inlen, char quote)
Escape any non printable or non-UTF8 characters in the input string.
Definition print.c:227
#define fr_assert(_expr)
Definition rad_assert.h:38
#define DEBUG2(fmt,...)
Definition radclient.h:43
#define WARN(fmt,...)
Definition radclient.h:47
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition rb.c:577
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition rb.c:626
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition rb.h:246
The main red black tree structure.
Definition rb.h:73
@ RLM_MODULE_NUMCODES
How many valid return codes there are.
Definition rcode.h:53
fr_dict_attr_t const * request_attr_request
Definition request.c:43
static char const * name
#define FR_SBUFF_OUT(_start, _len_or_end)
bool at_runtime
Produce an ephemeral/runtime tmpl.
Definition tmpl.h:344
ssize_t tmpl_preparse(char const **out, size_t *outlen, char const *in, size_t inlen, fr_token_t *type))
Preparse a string in preparation for passing it to tmpl_afrom_substr()
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:335
struct tmpl_rules_s tmpl_rules_t
Definition tmpl.h:233
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:332
static char buff[sizeof("18446744073709551615")+3]
Definition size_tests.c:41
ssize_t fr_skip_condition(char const *start, char const *end, bool const terminal[static UINT8_MAX+1], bool *eol)
Skip a conditional expression.
Definition skip.c:286
ssize_t fr_skip_xlat(char const *start, char const *end)
Skip an xlat expression.
Definition skip.c:217
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
Definition skip.h:37
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
fr_aka_sim_id_type_t type
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
uint8_t allow_foreign
Allow arguments not found in dict_def.
Definition tmpl.h:312
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define fr_table_value_by_longest_prefix(_match_len, _table, _name, _name_len, _def)
Find the longest string match using a sorted or ordered table.
Definition table.h:732
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
Definition table.h:653
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
An element in a lexicographically sorted array of name to num mappings.
Definition table.h:49
An element in a lexicographically sorted array of name to ptr mappings.
Definition table.h:65
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition talloc.c:467
static int talloc_const_free(void const *ptr)
Free const'd memory.
Definition talloc.h:229
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:294
const bool fr_assignment_op[T_TOKEN_LAST]
Definition token.c:169
const bool fr_list_assignment_op[T_TOKEN_LAST]
Definition token.c:186
fr_token_t gettoken(char const **ptr, char *buf, int buflen, bool unescape)
Definition token.c:469
fr_table_num_ordered_t const fr_tokens_table[]
Definition token.c:34
const bool fr_str_tok[T_TOKEN_LAST]
Definition token.c:232
int getword(char const **ptr, char *buf, int buflen, bool unescape)
Definition token.c:460
enum fr_token fr_token_t
@ T_OP_SUB_EQ
Definition token.h:70
@ T_INVALID
Definition token.h:39
@ T_EOL
Definition token.h:40
@ T_SINGLE_QUOTED_STRING
Definition token.h:122
@ T_OP_AND_EQ
Definition token.h:74
@ T_OP_CMP_TRUE
Definition token.h:104
@ T_BARE_WORD
Definition token.h:120
@ T_OP_EQ
Definition token.h:83
@ T_BACK_QUOTED_STRING
Definition token.h:123
@ T_HASH
Definition token.h:119
@ T_OP_SET
Definition token.h:84
@ T_OP_NE
Definition token.h:97
@ T_OP_ADD_EQ
Definition token.h:69
@ T_OP_CMP_FALSE
Definition token.h:105
@ T_OP_LSHIFT_EQ
Definition token.h:77
@ T_OP_RSHIFT_EQ
Definition token.h:76
@ T_DOUBLE_QUOTED_STRING
Definition token.h:121
@ T_OP_CMP_EQ
Definition token.h:106
@ T_OP_LE
Definition token.h:100
@ T_OP_GE
Definition token.h:98
@ T_OP_GT
Definition token.h:99
@ T_OP_OR_EQ
Definition token.h:73
@ T_OP_LT
Definition token.h:101
@ T_OP_PREPEND
Definition token.h:85
close(uq->fd)
static fr_slen_t parent
Definition pair.h:841
char const * fr_strerror(void)
Get the last library error.
Definition strerror.c:553
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:576
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const(_msg)
Definition strerror.h:223
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
Definition types.c:31
#define fr_type_is_leaf(_x)
Definition types.h:391
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
Definition types.h:452
static size_t char fr_sbuff_t size_t inlen
Definition value.h:1023
static size_t char ** out
Definition value.h:1023
#define FR_VALUE_BOX_SAFE_FOR_ANY
Definition value.h:173
fr_dict_t const * virtual_server_dict_by_child_ci(CONF_ITEM const *ci)
Return the namespace for a given virtual server specified by a CONF_ITEM within the virtual server.