31RCSID(
"$Id: 1c7c969f1ab8835230d2fcb9e161cb2b692f94c9 $")
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>
59#include <freeradius-devel/server/main_config.h>
78 {
L(
"accounting"),
true },
80 {
L(
"authenticate"),
true },
82 {
L(
"decode"),
true },
84 {
L(
"encode"),
true },
86 {
L(
"establish"),
true },
88 {
L(
"finally"),
true },
94 {
L(
"verify"),
true },
108#define MAX_STACK (32)
164 char **p_p,
char const **ptr_p,
char *output,
size_t outsize,
181 p = strrchr(
name,
'/');
195 next = strchr(ptr,
'}');
198 ERROR(
"%s[%d]: File expansion missing }",
207 if ((next - ptr) >= (
name + PATH_MAX - p)) {
208 ERROR(
"%s[%d]: File name is too large",
213 memcpy(p, ptr, next - ptr);
214 p[next - ptr] =
'\0';
216 fd = open(
name, O_RDONLY);
218 ERROR(
"%s[%d]: Failed opening %s: %s",
219 cf, lineno,
name, strerror(errno));
224 room = (output + outsize) - p;
230 len = read(fd, p, room);
232 ERROR(
"%s[%d]: Failed reading %s: %s",
233 cf, lineno,
name, strerror(errno));
240 ERROR(
"%s[%d]: Failed reading %s: the file is empty",
249 if ((
size_t)
len >= room) {
250 ERROR(
"%s[%d]: Too much data in %s: did not read the entire file",
259 char *q, *end = p +
len;
273 for (q = p; q < end; q++) {
274 if (*q >=
' ')
break;
280 ERROR(
"%s[%d]: Too much data in %s: expected one line of text, found multiple lines in the file",
302 char *output,
size_t outsize,
303 char const *input,
ssize_t inlen,
bool *soft_fail,
bool soft_fail_env)
306 char const *end, *next, *ptr;
310 if (soft_fail) *soft_fail =
false;
319 size_t want = inlen >= 0 ? (
size_t)inlen : strlen(input);
320 if (want >= outsize) want = outsize - 1;
321 memcpy(output, input, want);
347 while (*ptr && (!end || (ptr < end))) {
351 if ((*ptr ==
'$') && (ptr[1] ==
'{')) {
359 ERROR(
"%s[%d]: Failed parsing variable expansion '%s''",
371 if ((
size_t)
len >=
sizeof(
name)) {
372 ERROR(
"%s[%d]: Reference string is too large",
386 if (
name[0] ==
'/') {
387 int fd = open(
name, O_RDONLY);
391 ERROR(
"%s[%d]: Reference \"${%s}\" failed opening file - %s", cf, lineno,
name,
fr_syserror(errno));
395 if (fstat(fd, &buf) < 0) {
398 ERROR(
"%s[%d]: Reference \"${%s}\" failed reading file - %s", cf, lineno,
name,
fr_syserror(errno));
402 if (buf.st_size >= ((output + outsize) - p)) {
404 ERROR(
"%s[%d]: Reference \"${%s}\" file is too large (%zu >= %zu)", cf, lineno,
name,
405 (
size_t) buf.st_size, (
size_t) ((output + outsize) - p));
409 len = read(fd, p, (output + outsize) - p - 1);
410 if (
len < 0)
goto fail_fd;
419 q = strchr(
name,
':');
426 if (soft_fail) *soft_fail =
true;
427 PERROR(
"%s[%d]: Failed finding reference \"${%s}\"", cf, lineno,
name);
441 ERROR(
"%s[%d]: Can only reference properties of sections", cf, lineno);
456 ERROR(
"%s[%d]: Invalid property '%s'", cf, lineno, q);
461 if ((p + flen) >= (output + outsize))
goto too_long;
480 if (soft_fail) *soft_fail =
true;
482 ERROR(
"%s[%d]: Reference \"%s\" points to a variable which has not been expanded.",
488 ERROR(
"%s[%d]: Reference \"%s\" has no value",
493 if (p + strlen(cp->
value) >= output + outsize) {
494 ERROR(
"%s[%d]: Reference \"%s\" is too long",
511 for (ci_p = &outer_cs->
item; ci_p != NULL; ci_p = ci_p->
parent) {
513 ERROR(
"%s[%d]: Cannot reference different item in same section", cf, lineno);
527 ERROR(
"%s[%d]: Failed copying reference %s", cf, lineno,
name);
537 ERROR(
"%s[%d]: Reference \"%s\" type is invalid", cf, lineno, input);
540 }
else if (strncmp(ptr,
"$ENV{", 5) == 0) {
550 next = strchr(ptr,
'}');
553 ERROR(
"%s[%d]: Environment variable expansion missing }",
562 if ((
size_t) (next - ptr) >=
sizeof(
name)) {
563 ERROR(
"%s[%d]: Environment variable name is too large",
568 memcpy(
name, ptr, next - ptr);
569 name[next - ptr] =
'\0';
577 if (!soft_fail_env) {
578 ERROR(
"%s[%d]: Invalid reference to missing environment variable $ENV{%s}",
587 if (p + strlen(env) >= output + outsize) {
588 ERROR(
"%s[%d]: Reference \"%s\" is too long",
597 }
else if (strncmp(ptr,
"$VALUE{", 7) == 0) {
600 if (
cf_expand_file(cf, lineno,
name, &p, &ptr, output, outsize,
false) < 0)
return NULL;
602 }
else if (strncmp(ptr,
"$FILE{", 6) == 0) {
605 if (
cf_expand_file(cf, lineno,
name, &p, &ptr, output, outsize,
true) < 0)
return NULL;
615 if (p >= (output + outsize)) {
617 ERROR(
"%s[%d]: Reference \"%s\" is too long",
633 if (!cs || !
template)
return true;
659 if (!cp2)
return false;
691 if (!subcs2)
return false;
713 return CMP(a->
buf.st_ino, b->buf.st_ino);
744 if (
fr_dirfd(&my_fd, &r, filename) < 0) {
745 ERROR(
"Failed to open directory containing %s", filename);
749 if (fstatat(my_fd, r, &my_file.
buf, 0) < 0) {
750 if (my_fd != AT_FDCWD) close(my_fd);
766 if (my_fd != AT_FDCWD) close(my_fd);
770 fd = openat(my_fd, r, O_RDONLY, 0);
771 if (my_fd != AT_FDCWD) close(my_fd);
772 if (fd < 0)
goto error;
773 fp = fdopen(fd,
"r");
775 fp = fopen(filename,
"r");
790 file->from_dir = from_dir;
792 if (fstat(fileno(fp), &
file->buf) == 0) {
794 if ((
file->buf.st_mode & S_IWOTH) != 0) {
795 ERROR(
"Configuration file %s is globally writable. "
796 "Refusing to start due to insecure configuration.", filename);
847 uid_t euid = (uid_t) -1;
848 gid_t egid = (gid_t) -1;
872 ret = cb(filename, uctx);
874 if (seteuid(euid) < 0) {
881 if (setegid(egid) < 0) {
907 struct sockaddr_un addr = { .sun_family = AF_UNIX };
916 strlcpy(addr.sun_path, filename,
sizeof(addr.sun_path));
918 fd = socket(AF_UNIX, SOCK_STREAM, 0);
924 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
931 if (connect(fd, (
struct sockaddr *)&addr,
sizeof(addr)) < 0) {
981 if (stat(filename, &buf) < 0) {
1000 if (!S_ISSOCK(buf.st_mode)) {
1028 fd = open(filename, O_RDONLY);
1039 if (fd >= 0) close(fd);
1041 switch (our_errno) {
1060 if (
file && fstat(fd, &
file->buf) < 0)
goto error;
1098 if (stat(filename, &
file->buf) < 0) {
1100 cf_log_perr(cp,
"Unable to open file \"%s\"", filename);
1120 if ((
file->buf.st_mode & S_IWOTH) != 0) {
1121 cf_log_perr(cp,
"Configuration file %s is globally writable. "
1122 "Refusing to start due to insecure configuration.", filename);
1160 if (!
value)
return -1;
1163 ERROR(
"%s[%d]: Empty filename in $INCLUDE", ci->filename, ci->lineno);
1182 char *
buffer,
size_t bufsize)
1189 p = strrchr(
buffer, FR_DIR_SEP);
1190 if (!p)
return filename;
1195 dirsize = (p -
buffer) + 1;
1197 if ((dirsize + strlen(filename)) >= bufsize) {
1201 strlcpy(p + 1, filename, bufsize - dirsize);
1211 char const *filename,
int lineno)
1213 char const *ptr = *ptr_p;
1231 ERROR(
"%s[%d]: %s", filename, lineno, text);
1239 if ((
size_t) slen >= buflen) {
1240 ERROR(
"%s[%d]: Name is too long", filename, lineno);
1293 bool do_glob =
false;
1296 char p1 =
'\0', p2 =
'\0';
1302 ERROR(
"%s[%d]: Parse error: Invalid location for $INCLUDE",
1313 while (*ptr && !isspace((
uint8_t) *ptr)) {
1320 ERROR(
"%s[%d]: Invalid character in filename for $INCLUDE", frame->
filename, frame->
lineno);
1327 if ((*ptr ==
'*') && (p1 ==
'.') && ((p2 ==
'/') || (p2 ==
'\0'))) {
1328 ERROR(
"%s[%d]: Invalid sequence \".*\" at directory level in $INCLUDE", frame->
filename, frame->
lineno);
1332 if (do_glob && (*ptr == FR_DIR_SEP)) {
1333 ERROR(
"%s[%d]: Wildcard directory names not allowed in $INCLUDE", frame->
filename, frame->
lineno);
1336 if (do_glob && (*ptr ==
'*')) {
1341 do_glob |= (*ptr ==
'*');
1355 if ((
size_t) (ptr -
value) > 1024) {
1376 if (*
value ==
'$') relative =
false;
1380 if (!
value)
return -1;
1382 if (!FR_DIR_IS_RELATIVE(
value)) relative =
false;
1401 memset(frame, 0,
sizeof(*frame));
1403 frame->
type = CF_STACK_GLOB;
1404 frame->required = required;
1414 rcode = glob(
value, GLOB_ERR | GLOB_NOESCAPE, NULL, &frame->glob);
1428 if (frame->glob.gl_pathc != 0)
return 1;
1435 globfree(&frame->glob);
1437 if (!required)
return 0;
1439 ERROR(
"%s[%d]: Failed expanding '%s' - No matching files", frame->
filename, frame->
lineno,
1449 struct stat statbuf;
1451 if (stat(
value, &statbuf) < 0) {
1468 memset(frame, 0,
sizeof(*frame));
1489 struct stat stat_buf;
1503 dir = opendir(directory);
1505 ERROR(
"%s[%d]: Error reading directory %s: %s",
1509 if (dir) closedir(dir);
1517 ERROR(
"%s[%d]: Failed getting a descriptor for directory %s: %s", frame->
filename, frame->
lineno,
1525 if (fstat(my_fd, &stat_buf) < 0) {
1531 if ((stat_buf.st_mode & S_IWOTH) != 0) {
1532 ERROR(
"%s[%d]: Directory %s is globally writable. Refusing to start due to "
1533 "insecure configuration", frame->
filename, frame->
lineno, directory);
1549 .
type = CF_STACK_DIR,
1550 .directory = directory,
1564 while ((dp = readdir(dir)) != NULL) {
1565 char const *p, *ext = NULL;
1567 if (dp->d_name[0] ==
'.')
continue;
1572 for (p = dp->d_name; *p !=
'\0'; p++) {
1576 (*p ==
'_'))
continue;
1584 if (*p !=
'\0')
continue;
1595 WARN(
"Ignoring packaging system produced file %s%s", frame->directory, dp->d_name);
1603 frame->directory, dp->d_name);
1605 if (stat(
stack->buff[1], &stat_buf) != 0) {
1606 ERROR(
"%s[%d]: Failed checking file %s: %s",
1607 (frame - 1)->filename, (frame - 1)->lineno,
1612 if (S_ISDIR(stat_buf.st_mode)) {
1613 WARN(
"%s[%d]: Ignoring directory %s",
1614 (frame - 1)->filename, (frame - 1)->lineno,
1629 ERROR(
"%s[%d]: Error including %s: No support for directories!",
1652 ERROR(
"%s[%d]: Internal sanity check error in template reference", frame->
filename, frame->
lineno);
1665 if (templatecs)
goto check;
1671 ERROR(
"%s[%d]: Cannot find template \"%s\", as no 'templates' section exists.",
1678 PERROR(
"%s[%d]: Failed finding item \"%s\" in the 'templates' section.",
1690 for (ci = &
parent->item; ci != NULL; ci = ci->
parent) {
1691 if (ci != &templatecs->
item)
continue;
1693 ERROR(
"%s[%d]: Cannot use template \"%s\" from inside of itself",
1698 parent->template = templatecs;
1727 char const *ptr =
stack->ptr;
1768 if (slen > 0)
break;
1785 p = (
uint8_t const *) ptr + (-slen);
1791 if (*p && (*p <
' ')) {
1792 while ((*p ==
'\r') || (*p ==
'\n')) {
1834 cf_log_err(cs,
"Expected '{' instead of %s", ptr);
1845 buff[2][slen] =
'\0';
1850 buff[2][slen - 1] =
'\0';
1876 char const *
value = NULL;
1879 char const *ptr =
stack->ptr;
1896 ERROR(
"%s[%d]: Invalid syntax for 'map' - module name must not be a quoted string",
1919 ERROR(
"%s[%d]: Expecting string expansions in 'map' definition",
1925 ERROR(
"%s[%d]: Expecting section start brace '{' in 'map' definition",
1938 ERROR(
"%s[%d]: Failed allocating memory for section",
1948 css->
argv = talloc_array(css,
char const *, 1);
1963 char const *mod = NULL;
1966 char const *ptr =
stack->ptr;
2015 ERROR(
"%s[%d]: The second argument to 'subrequest' must be an attribute reference",
2035 ERROR(
"%s[%d]: The third argument to 'subrequest' must be an attribute reference",
2042 ERROR(
"%s[%d]: Expecting section start brace '{' in 'subrequest' definition",
2054 ERROR(
"%s[%d]: Failed allocating memory for section",
2066 css->
argv = talloc_array(css,
char const *, values);
2069 for (i = 0; i < values; i++) {
2086 char const *ptr =
stack->ptr;
2110 while (isalpha((
uint8_t) *ptr)) ptr++;
2114 ERROR(
"%s[%d]: Invalid syntax for 'catch' - unknown rcode '%s'",
2121 if ((*ptr !=
'{') && !isspace((
uint8_t) *ptr)) {
2122 ERROR(
"%s[%d]: Invalid syntax for 'catch' - unexpected text at '%s'",
2133 ERROR(
"%s[%d]: Invalid syntax for 'catch' - too many arguments at'%s'",
2143 ERROR(
"%s[%d]: Failed allocating memory for section",
2156 css->
argv = talloc_array(css,
char const *, argc + 1);
2160 for (i = 0; i < argc; i++) {
2165 css->
argv[argc] = NULL;
2179 if (!ptr) ptr =
stack->ptr;
2198 char const *ptr = *ptr_p;
2213 (void)
parse_error(
stack, type_ptr,
"Unknown or invalid variable type in 'foreach'");
2225 (void)
parse_error(
stack, ptr2,
"Invalid variable name for key in 'foreach'");
2250 char const *ptr =
stack->ptr, *ptr2, *type_ptr;
2256 ERROR(
"%s[%d]: Failed allocating memory for section",
2296 cf_log_warn(css,
"Using deprecated syntax. Please use the new 'foreach' syntax.");
2320 if (*ptr ==
'(')
goto alloc_argc_2;
2335 css->
argv = talloc_array(css,
char const *, css->
argc);
2357 (void)
parse_error(
stack, type_ptr,
"Invalid data type for 'key' variable");
2366 (void)
parse_error(
stack, ptr,
"Expected (...) after 'foreach' variable definition");
2370 goto parse_expression;
2374 css->
argv = talloc_array(css,
char const *, css->
argc);
2432 char *
buff,
char const *filename,
int lineno)
2445 char const *expanded;
2452 }
else if (!soft_fail) {
2495 char const *ptr =
stack->ptr;
2506 while (isalpha(*ptr)) ptr++;
2509 ERROR(
"%s[%d]: Missing ')' in cast",
2517 ERROR(
"%s[%d]: Unknown data type '%.*s' in cast",
2523 ERROR(
"%s[%d]: Invalid data type '%.*s' in cast",
2542 ERROR(
"%s[%d]: Failed allocating memory for section",
2567 css->
argv = talloc_array(css,
char const *, css->
argc);
2604 [
'0' ...
'9' ] =
true,
2605 [
'A' ...
'Z' ] =
true,
2606 [
'a' ...
'z' ] =
true,
2619 char const *ptr =
stack->ptr;
2620 char const *name1_ptr, *name2_ptr, *op_ptr;
2654 if (frame->
braces == 0) {
2695 switch (
parent->unlang) {
2701 if (name1_token ==
T_EOL)
return 0;
2708 return parse_error(
stack, name1_ptr,
"Invalid location for quoted string");
2711 if (*
buff[1] ==
'%') {
2712 return parse_error(
stack, name1_ptr,
"Cannot use functions outside of a processing section");
2715 if (*
buff[1] ==
'&') {
2716 return parse_error(
stack, name1_ptr,
"Cannot reference attributes outside of a processing section");
2737 WARN(
"%s[%d]: Using '&' is no longer necessary when referencing attributes. Please delete it.", frame->
filename, frame->
lineno);
2767 return parse_error(
stack, name1_ptr,
"Invalid location for unlang keyword");
2771 ci = process(
stack);
2776 parent->allow_locals =
false;
2791 if (!isalnum((
int) *ptr))
goto check_for_eol;
2803 parent->allow_locals =
false;
2827 return parse_error(
stack, name1_ptr,
"Invalid data type for local variable. Must be 'tlv' or else a non-structural type");
2861 if (*ptr !=
'{')
goto alloc_pair;
2873 if (!*ptr || (*ptr ==
'#') || (*ptr ==
',') || (*ptr ==
';') || (*ptr ==
'}')) {
2881 parent->allow_locals =
false;
2903 switch (
parent->unlang) {
2910 if (*ptr ==
'=')
goto operator;
2924 return parse_error(
stack, name2_ptr,
"Unexpected operator, was expecting a configuration section. Is there a missing '}' somewhere?");
2927 return parse_error(
stack, name2_ptr,
"Invalid second name for configuration section");
2936 return parse_error(
stack, name2_ptr,
"Unexpected quoted string after section name");
2946 ((strcmp(
buff[1],
"load-balance") == 0) ||
2947 (strcmp(
buff[1],
"redundant-load-balance") == 0))) {
2968 css->
argv = talloc_array(css,
char const *, 1);
3006 if ((strcmp(
buff[1],
"case") == 0) ||
3007 (strcmp(
buff[1],
"limit") == 0) ||
3008 (strcmp(
buff[1],
"load-balance") == 0) ||
3009 (strcmp(
buff[1],
"redundant-load-balance") == 0) ||
3010 (strcmp(
buff[1],
"timeout") == 0)) {
3017 if ((strcmp(
buff[1],
"default") == 0) ||
3018 (strcmp(
buff[1],
"group") == 0) ||
3019 (strcmp(
buff[1],
"transaction") == 0) ||
3020 (strcmp(
buff[1],
"try") == 0) ||
3021 (strcmp(
buff[1],
"redundant") == 0)) {
3028 if (!((*ptr ==
'"') || (*ptr ==
'`') || (*ptr ==
'\'') || ((*ptr ==
'&') && (ptr[1] !=
'=')) ||
3054 return parse_error(
stack, name1_ptr,
"Invalid location for local variable - definitions cannot be inside of an assignment");
3058 return parse_error(
stack, name1_ptr,
"Invalid location for local variable - definitions must go at the start of a section");
3061 return parse_error(
stack, name1_ptr,
"Invalid location for local variable - definitions can only go in a processing section.");
3067 parent->allow_locals =
false;
3086 if (
buff[1][0] ==
'@') {
3108 ERROR(
"%s[%d]: '@' references must refer to an absolute path, starting at the top of the configuration", frame->
filename, frame->
lineno);
3116 if (
name[1] ==
'.') {
3155 ERROR(
"%s[%d]: '@' reference \"%s\" already contains a \"%s\" section at %s[%d]",
3172 ERROR(
"%s[%d]: '@%s' references can only appear at the top level of a section",
3197 ERROR(
"%s[%d]: Failed allocating memory for section",
3204 ERROR(
"%s[%d]: Failed creating section - too many nested sections (%d)",
3221 switch (
parent->unlang) {
3223 if (!
parent->item.parent) {
3230 (strcmp(css->
name1,
"update") == 0)) {
3263 if (strcmp(css->
name1,
"dictionary") == 0) {
3272 if (strcmp(css->
name1,
"listen") == 0) {
3281 if ((strcmp(css->
name1,
"group") == 0) ||
3282 (strcmp(css->
name1,
"load-balance") == 0) ||
3283 (strcmp(css->
name1,
"redundant") == 0) ||
3284 (strcmp(css->
name1,
"redundant-load-balance") == 0)) {
3334 if (strcmp(css->
name1,
"update") == 0) {
3360 switch (name2_token) {
3395 return parse_error(
stack, op_ptr,
"Syntax error, the input should be an assignment operator");
3401 if (!*ptr || (*ptr ==
'#') || (*ptr ==
',') || (*ptr ==
';')) {
3418 return parse_error(
stack, ptr,
"Invalid location for nested attribute assignment");
3459 if ((*ptr ==
'(') || (*ptr ==
'%')) {
3469 parent->allow_locals =
false;
3501 return parse_error(
stack, ptr + slen,
"Expression is unfinished at end of line");
3507 memcpy(
buff[2], ptr, slen);
3508 buff[2][slen] =
'\0';
3524 if ((*ptr ==
',') || (*ptr ==
';')) ptr++;
3528 ((*ptr ==
'`') || (*ptr ==
'%') || (*ptr ==
'('))) {
3534 return parse_error(
stack, ptr,
"Invalid value for assignment in configuration file");
3549 parent->allow_locals =
false;
3563 if ((*ptr ==
';') || (*ptr ==
',')) {
3582 if (*ptr && (*ptr !=
'#')) {
3583 return parse_error(
stack, ptr,
"Unexpected text after configuration item");
3616 memset(frame, 0,
sizeof(*frame));
3658 bool at_eof, has_spaces;
3670 at_eof = (fgets(
stack->fill,
stack->bufsize - (
stack->fill -
stack->buff[0]), frame->fp) == NULL);
3712 if (ptr >
stack->fill) {
3736 ptr =
stack->buff[0];
3739 if (!*ptr || (*ptr ==
'#'))
goto read_more;
3741 }
else if (
len == 0) {
3755 if ((
len > 0) && (
stack->fill[
len - 1] ==
'\\')) {
3759 if (!has_spaces && (
len > 2) && (
stack->fill[
len - 2] ==
'"')) {
3765 goto read_continuation;
3784 if (!*ptr || (*ptr ==
'#')) {
3800 text = (*ptr ==
'#') ? ptr + 1 : NULL;
3806 size_t l = strlen(p);
3807 while (l > 0 && (p[l - 1] ==
'\n' || p[l - 1] ==
'\r')) {
3837 switch (frame->
type) {
3840 if (frame->gl_current == frame->glob.gl_pathc) {
3841 globfree(&frame->glob);
3864 if (rcode == 0)
goto do_frame;
3865 if (rcode < 0)
return -1;
3896 if (rcode < 0)
return -1;
3917 if (rcode < 0)
return -1;
3918 if (rcode == 0)
break;
3924 ptr =
stack->buff[0];
3944 if (rcode < 0)
return -1;
3945 if (rcode == 0)
continue;
3970 if (!*ptr || (*ptr ==
'#'))
break;
3976 if (rcode < 0)
return -1;
3978 }
while (rcode == 1);
3987 ERROR(
"%s[%d]: EOF reached without closing brace for section %s starting at line %d",
3999 if (
stack->depth > 0) {
4011 while (
stack->depth >= 0) {
4012 switch (frame->
type) {
4014 if (frame->fp) fclose(frame->fp);
4026 globfree(&frame->glob);
4053 char *confdir = NULL;
4068 p = strrchr(filename, FR_DIR_SEP);
4090 stack.buff = talloc_array(cs,
char *, 4);
4091 for (i = 0; i < 4; i++)
MEM(
stack.buff[i] = talloc_array(
stack.buff,
char, 8192));
4094 stack.bufsize = 8192;
4097 memset(frame, 0,
sizeof(*frame));
4116 cf_log_err(cs,
"Parsing config items failed");
4152 ERROR(
"Failed opening in-memory buffer for parsing: %s",
fr_syserror(errno));
4173 stack.buff = talloc_array(cs,
char *, 4);
4174 for (i = 0; i < 4; i++)
MEM(
stack.buff[i] = talloc_array(
stack.buff,
char, 8192));
4177 stack.bufsize = 8192;
4180 memset(frame, 0,
sizeof(*frame));
4207 cf_log_err(cs,
"Parsing config items failed");
4245 if (c) fprintf(fp,
"%c", c);
4248 fwrite(
buffer, outlen, 1, fp);
4250 if (c) fprintf(fp,
"%c", c);
4257 fprintf(fp,
"%s\n", cp->
attr);
4272 if (!fp || !cs)
return -1;
4295 fprintf(fp,
"(%s)",
buffer);
4327 bool prev_was_blank =
false;
4329 if (!fp || !cs)
return -1;
4341 prev_was_blank =
true;
4348 if (!ci->filename || (ci->filename[0] ==
'<'))
break;
4352 prev_was_blank =
false;
4362 if (c->
text == NULL) {
4363 if (!prev_was_blank) {
4365 prev_was_blank =
true;
4374 prev_was_blank =
false;
4393 char name[8192], *p;
4396 if (!ptr || (!parent_cs && !outer_cs)) {
4440 }
else if (strchr(p,
'.') != NULL) {
4457 if (next)
return &(next->
item);
4461 if (cp)
return &(cp->
item);
4463 if (!parent_cs)
goto missing_parent;
4482 while (*q !=
'\0') {
4505 while (*q !=
'\0') {
4507 fr_strerror_const(
"Invalid reference, '[' cannot be used inside of a '[...]' block");
4529 if (*q++ ==
'.')
break;
4564 if (next)
return &(next->
item);
4568 fr_strerror_printf(
"Parent section %s%s%s { ... } does not contain a %s %s { ... } configuration section",
4570 name2 ?
" " :
"", name2 ? name2 :
"",
4581 if (next)
return &(next->
item);
4585 if (cp)
return &(cp->
item);
4588 fr_strerror_printf(
"Parent section %s%s%s { ... } does not contain a %s configuration item",
4590 name2 ?
" " :
"", name2 ? name2 :
"",
4609 fr_strerror_printf(
"Parent section %s%s%s { ... } does not contain a %s { ... } configuration section",
4611 name2 ?
" " :
"", name2 ? name2 :
"",
static int const char char buffer[256]
strcpy(log_entry->msg, buffer)
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
#define DIAG_OFF_OPTIONAL(_x)
#define L(_str)
Helper for initialising arrays of string literals.
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
#define CMP_RETURN(_a, _b, _field)
Return if the comparison is not 0 (is unequal)
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
fr_cmp_ret_t
Result of an ordering comparison.
int at_reference_braces
braces when we found this thing
CONF_SECTION * current
sub-section we're reading
static bool cf_template_merge(CONF_SECTION *cs, CONF_SECTION const *template)
static int parse_input(cf_stack_t *stack)
int cf_section_write_children(FILE *fp, CONF_SECTION *cs, int depth)
Emit the children of a section at depth without an enclosing { ... }.
char * fill
where we start filling the buffer from
void cf_section_set_unlang(CONF_SECTION *cs)
static int unlang_keywords_len
static fr_table_num_sorted_t const server_unlang_section[]
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.
static int frame_readdir(cf_stack_t *stack)
static CONF_ITEM * process_map(cf_stack_t *stack)
static CONF_ITEM * process_foreach(cf_stack_t *stack)
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)
static int cf_expand_file(char const *cf, int lineno, char name[static PATH_MAX], char **p_p, char const **ptr_p, char *output, size_t outsize, bool raw)
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)
static int parse_type_name(cf_stack_t *stack, char const **ptr_p, char const *type_ptr, fr_type_t *type_p)
static fr_cmp_ret_t filename_cmp(void const *one, void const *two)
static const bool cf_name_char1[SBUFF_CHAR_CLASS]
Convert tokens back to a quoting character.
static void cf_stack_cleanup(cf_stack_t *stack)
cf_file_check_err_t cf_file_check_unix_perm(char const *filename, UNUSED void *uctx)
Check if file exists, and is a socket.
static const bool terminal_end_line[SBUFF_CHAR_CLASS]
static int cf_file_open(CONF_SECTION *cs, char const *filename, bool from_dir, FILE **fp_p)
int cf_file_read(CONF_SECTION *cs, char const *filename, bool root)
static fr_md5_ctx_t * cf_md5_ctx
int cf_section_write(FILE *fp, CONF_SECTION *cs, int depth)
int lineno
line in that filename
static const bool terminal_end_section[SBUFF_CHAR_CLASS]
static size_t conf_property_name_len
static fr_table_num_sorted_t const conf_property_name[]
int cf_section_pass2(CONF_SECTION *cs)
bool from_dir
this file was read from $include foo/
static ssize_t cf_string_write(FILE *fp, char const *string, size_t len, fr_token_t t)
static char const parse_tabs[]
static fr_cmp_ret_t _inode_cmp(void const *one, void const *two)
cf_file_check_err_t cf_file_check(CONF_PAIR *cp, bool check_perms)
Do some checks on the file as an "input" file.
static int cf_file_include(cf_stack_t *stack)
static uid_t conf_check_uid
static gid_t conf_check_gid
CONF_ITEM *(* cf_process_func_t)(cf_stack_t *)
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)
CONF_SECTION * at_reference
was this thing an @foo ?
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.
static int cf_file_fill(cf_stack_t *stack)
static const size_t cf_file_extensions_len
char const * ptr
current parse pointer
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.
CONF_ITEM * cf_reference_item(CONF_SECTION const *parent_cs, CONF_SECTION const *outer_cs, char const *ptr)
static CONF_ITEM * process_subrequest(cf_stack_t *stack)
void cf_file_check_set_uid_gid(uid_t uid, gid_t gid)
Set the euid/egid used when performing file checks.
enum conf_property CONF_PROPERTY
static fr_table_ptr_sorted_t unlang_keywords[]
char ** buff
buffers for reading / parsing
CONF_SECTION * parent
which started this file
static int process_template(cf_stack_t *stack)
size_t bufsize
size of the buffers
static size_t server_unlang_section_len
void cf_md5_final(uint8_t *digest)
static int process_include(cf_stack_t *stack, CONF_SECTION *parent, char const *ptr, bool required, bool relative)
static CONF_ITEM * process_if(cf_stack_t *stack)
static void cf_md5_update(char const *p)
void cf_file_free(CONF_SECTION *cs)
static char const * cf_local_file(char const *base, char const *filename, char *buffer, size_t bufsize)
static CONF_ITEM * process_catch(cf_stack_t *stack)
int cf_pair_write(FILE *fp, CONF_PAIR *cp)
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.
char const * filename
filename we're reading
static CONF_ITEM * process_switch(cf_stack_t *stack)
static int parse_error(cf_stack_t *stack, char const *ptr, char const *message)
static fr_table_num_sorted_t const cf_file_extensions[]
cf_file_check_err_t
Results of file checks.
@ CF_FILE_OK
File checks passed.
@ CF_FILE_NO_PERMISSION
Requested permissions not set.
@ CF_FILE_NO_UNIX_SOCKET
File is not a unix socket.
@ CF_FILE_OTHER_ERROR
Other error occurred checking permissions.
@ CF_FILE_NO_EXIST
File does not exist.
cf_parse_t on_read
Function to call as the item is being read, just after it has been allocated and initialized.
Defines a CONF_PAIR to C data type mapping.
CONF_SECTION * cs
CONF_SECTION associated with the file.
CONF_ITEM item
Common set of fields.
CONF_ITEM * parent
Parent.
fr_token_t name2_quote
The type of quoting around name2.
char const * name2
Second name token. Given foo bar {} would be bar.
bool allow_locals
allow local variables
int argc
number of additional arguments
char const * attr
Attribute name.
#define CONF_SECTION_MAX_DEPTH
Limit the nesting depth.
@ CONF_ITEM_COMMENT
A # ... line preserved verbatim from the input - only created when the parser is asked to keep commen...
bool referenced
Was this item referenced in the config?
fr_token_t rhs_quote
Value Quoting style T_(DOUBLE|SINGLE|BACK)_QUOTE_STRING or T_BARE_WORD.
@ CF_UNLANG_MODULES
this section is in "modules", allow unlang 2 down
@ CF_UNLANG_NONE
no unlang
@ CF_UNLANG_CAN_HAVE_UPDATE
can have "update"
@ CF_UNLANG_ALLOW
allow unlang in this section
@ CF_UNLANG_POLICY
this section is a policy, allow unlang 2 down
@ CF_UNLANG_ASSIGNMENT
only assignments inside of map / update
@ CF_UNLANG_EDIT
only edit commands
@ CF_UNLANG_DICTIONARY
only local variable definitions
@ CF_UNLANG_SERVER
this section is a virtual server, allow unlang 2 down
char const * value
Attribute value.
char const * name1
First name token. Given foo bar {} would be foo.
fr_token_t op
Operator e.g. =, :=.
CONF_ITEM item
Common set of fields.
bool pass2
do expansion in pass2.
char const * filename
The file the config item was parsed from.
struct stat buf
stat about the file
bool at_reference
this thing was created from an ...
bool _cf_expand_variables(void)
bool _cf_preserve_comments(void)
char const * filename
name of the file
char const * text
Comment text, with the leading # and any surrounding whitespace stripped.
char const ** argv
additional arguments
fr_token_t lhs_quote
Name quoting style T_(DOUBLE|SINGLE|BACK)_QUOTE_STRING or T_BARE_WORD.
int lineno
The line number the config item began on.
CONF_ITEM_TYPE type
Whether the config item is a config_pair, conf_section or cf_data.
Internal data that is associated with a configuration section.
Common header for all CONF_* types.
Configuration AVP similar to a fr_pair_t.
A section grouping multiple CONF_PAIR.
CONF_PAIR * cf_pair_dup(CONF_SECTION *parent, CONF_PAIR *cp, bool copy_meta)
Duplicate a CONF_PAIR.
char const * cf_section_name2(CONF_SECTION const *cs)
Return the second identifier of a CONF_SECTION.
void * cf_data_value(CONF_DATA const *cd)
Return the user assigned value of CONF_DATA.
CONF_ITEM * cf_section_to_item(CONF_SECTION const *cs)
Cast a CONF_SECTION to a CONF_ITEM.
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.
char const * cf_section_name1(CONF_SECTION const *cs)
Return the first identifier of a CONF_SECTION.
CONF_SECTION * cf_section_find(CONF_SECTION const *cs, char const *name1, char const *name2)
Find a CONF_SECTION with name1 and optionally name2.
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
CONF_PAIR * cf_pair_find(CONF_SECTION const *cs, char const *attr)
Search for a CONF_PAIR with a specific name.
CONF_COMMENT * cf_comment_alloc(CONF_SECTION *parent, char const *text)
Allocate a new comment item attached to parent.
CONF_COMMENT * cf_item_to_comment(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_COMMENT (asserts on type mismatch).
bool cf_item_is_section(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_SECTION.
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.
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
CONF_ITEM * cf_pair_to_item(CONF_PAIR const *cp)
Cast a CONF_PAIR to a CONF_ITEM.
#define cf_log_err(_cf, _fmt,...)
#define cf_data_add(_cf, _data, _name, _free)
#define cf_data_find(_cf, _type, _name)
#define cf_lineno_set(_ci, _lineno)
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
#define cf_log_perr(_cf, _fmt,...)
#define cf_section_alloc(_ctx, _parent, _name1, _name2)
#define CF_TO_ITEM(_cf)
Auto cast from the input type to CONF_ITEM (which is the base type)
#define cf_item_foreach(_parent, _iter)
Iterate over every child item of a CONF_SECTION (or any CONF_ITEM that has children).
#define cf_filename_set(_ci, _filename)
#define cf_log_warn(_cf, _fmt,...)
#define cf_log_debug(_cf, _fmt,...)
int fr_heap_insert(fr_heap_t **hp, void *data)
Insert a new element into the heap.
int fr_heap_pop(void **out, fr_heap_t **hp)
Remove a node from the heap.
unsigned int fr_heap_index_t
#define fr_heap_alloc(_ctx, _cmp, _type, _field, _init)
Creates a heap that can be used with non-talloced elements.
#define FR_HEAP_INDEX_INVALID
#define DEBUG_ENABLED2
True if global debug level 1-2 messages are enabled.
int fr_dirfd(int *dirfd, char const **filename, char const *pathname)
From a pathname, return fd and filename needed for *at() functions.
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.
static char * stack[MAX_STACK]
#define fr_md5_final(_out, _ctx)
Finalise the ctx, producing the digest.
#define fr_md5_ctx_alloc()
Allocation function for MD5 digest context.
#define fr_md5_update(_ctx, _in, _inlen)
Ingest plaintext into the digest.
#define fr_md5_ctx_free(_ctx)
Free function for MD5 digest ctx.
#define MD5_DIGEST_LENGTH
@ 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_GROUP
A grouping of other attributes.
static uint8_t depth(fr_minmax_heap_index_t i)
int strncasecmp(char *s1, char *s2, int n)
void fr_perm_file_error(int num)
Write a file access error to the fr_strerror buffer, including euid/egid.
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.
static const char * spaces
static fr_token_t op_token(char const *s)
int fr_rb_find(void **found, fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
int fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
#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.
The main red black tree structure.
@ RLM_MODULE_NUMCODES
How many valid return codes there are.
#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]
fr_slen_t fr_skip_xlat(char const *start, char const *end)
Skip an xlat expression.
fr_slen_t fr_skip_condition(char const *start, char const *end, bool const terminal[static SBUFF_CHAR_CLASS], bool *eol)
Skip a conditional expression.
#define fr_skip_whitespace(_p)
Skip whitespace ('\t', '\n', '\v', '\f', '\r', ' ')
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
fr_aka_sim_id_type_t type
size_t strlcpy(char *dst, char const *src, size_t siz)
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
#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.
#define fr_table_value_by_str(_table, _name, _def)
Convert a string to a value using a sorted or ordered table.
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
An element in a lexicographically sorted array of name to num mappings.
An element in a lexicographically sorted array of name to ptr mappings.
char * talloc_bstrndup(TALLOC_CTX *ctx, char const *in, size_t inlen)
Binary safe strndup function.
#define talloc_strndup(_ctx, _str, _len)
static int talloc_const_free(void const *ptr)
Free const'd memory.
#define talloc_strdup(_ctx, _str)
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
const bool fr_assignment_op[T_TOKEN_LAST]
const bool fr_list_assignment_op[T_TOKEN_LAST]
fr_token_t gettoken(char const **ptr, char *buf, int buflen, bool unescape)
fr_table_num_ordered_t const fr_tokens_table[]
const bool fr_str_tok[T_TOKEN_LAST]
int getword(char const **ptr, char *buf, int buflen, bool unescape)
char const * fr_strerror(void)
Get the last library error.
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
#define fr_strerror_const(_msg)
fr_table_num_ordered_t const fr_type_table[]
Map data types to names representing those types.
#define fr_type_is_leaf(_x)
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
static fr_type_t fr_type_from_str(char const *type)
Return the constant value representing a type.
static fr_sbuff_err_t char ** out
static fr_sbuff_err_t char size_t * len