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