From: Alan T. DeKok Date: Thu, 24 Nov 2022 14:22:09 +0000 (-0500) Subject: update parser to allow local variable definitions X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=18ee73c1322840ac5d504a2d77dca459b99096d9;p=thirdparty%2Ffreeradius-server.git update parser to allow local variable definitions and don't allow unlang statements inside of list assignments like &request += { ... } This was previously parsed, and then would result in some weird load-time error. Forbidding it here makes the errors a bit clearer. --- diff --git a/src/lib/server/cf_file.c b/src/lib/server/cf_file.c index f1f0f62ce5f..53773c3f04d 100644 --- a/src/lib/server/cf_file.c +++ b/src/lib/server/cf_file.c @@ -1529,7 +1529,7 @@ static CONF_ITEM *process_if(cf_stack_t *stack) stack->ptr = ptr; - cs->allow_unlang = true; + cs->allow_unlang = cs->allow_locals = true; return cf_section_to_item(cs); } @@ -1743,7 +1743,7 @@ alloc_section: stack->ptr = ptr; frame->special = css; - css->allow_unlang = true; + css->allow_unlang = css->allow_locals = true; return cf_section_to_item(css); } @@ -1979,7 +1979,10 @@ static int parse_input(cf_stack_t *stack) frame->filename, frame->lineno, buff[1]); return -1; } - } else { + + } else if (name1_token == T_BARE_WORD) { + fr_type_t type; + process = (cf_process_func_t) fr_table_value_by_str(unlang_keywords, buff[1], NULL); if (process) { CONF_ITEM *ci; @@ -1989,6 +1992,7 @@ static int parse_input(cf_stack_t *stack) ptr = stack->ptr; if (!ci) return -1; if (cf_item_is_section(ci)) { + parent->allow_locals = false; css = cf_item_to_section(ci); goto add_section; } @@ -1998,16 +2002,43 @@ static int parse_input(cf_stack_t *stack) */ goto added_pair; } + + /* + * The next token is an assignment operator, so we ignore it. + */ + if (!isalnum((int) *ptr)) goto check_for_eol; + + /* + * Else check for a typedef. + */ + type = fr_table_value_by_str(fr_type_table, buff[1], FR_TYPE_NULL); + if (type == FR_TYPE_NULL) { + parent->allow_locals = false; + goto check_for_eol; + } + + if (!parent->allow_locals) { + ERROR("%s[%d]: Parse error: Invalid location for variable definition", + frame->filename, frame->lineno); + return -1; + } + + /* + * We don't have an operastor, so set it to a magic value. + */ + op_token = T_OP_CMP_TRUE; + goto parse_name; } /* * parent single word is done. Create a CONF_PAIR. */ +check_for_eol: if (!*ptr || (*ptr == '#') || (*ptr == ',') || (*ptr == ';') || (*ptr == '}')) { value_token = T_INVALID; op_token = T_OP_EQ; value = NULL; - goto do_set; + goto alloc_pair; } /* @@ -2082,8 +2113,15 @@ static int parse_input(cf_stack_t *stack) if (strcmp(css->name1, "server") == 0) css->allow_unlang = 2; if (strcmp(css->name1, "policy") == 0) css->allow_unlang = 2; - } else if (parent->allow_unlang) { - css->allow_unlang = 1; + } else if ((parent->allow_unlang == 2) && (strcmp(css->name1, "listen") == 0)) { /* hacks for listeners */ + css->allow_unlang = css->allow_locals = false; + + } else { + /* + * Allow unlang if the parent allows it, but don't allow + * unlang in list assignment sections. + */ + css->allow_unlang = css->allow_locals = parent->allow_unlang && !fr_list_assignment_op[name2_token]; } add_section: @@ -2207,6 +2245,7 @@ static int parse_input(cf_stack_t *stack) goto alloc_section; } +parse_name: /* * Parse the value for a CONF_PAIR. */ @@ -2219,11 +2258,12 @@ static int parse_input(cf_stack_t *stack) /* * Add parent CONF_PAIR to our CONF_SECTION */ -do_set: +alloc_pair: if (add_pair(parent, buff[1], value, name1_token, op_token, value_token, buff[3], frame->filename, frame->lineno) < 0) return -1; added_pair: fr_skip_whitespace(ptr); + parent->allow_locals = false; /* * Skip semicolon if we see it after a diff --git a/src/lib/server/cf_priv.h b/src/lib/server/cf_priv.h index aa2f2ef8477..a99bf7c676f 100644 --- a/src/lib/server/cf_priv.h +++ b/src/lib/server/cf_priv.h @@ -102,6 +102,7 @@ struct cf_section { int depth; int allow_unlang; //!< depth at which we allow unlang bool attr; //!< is this thing an attribute definition? + bool allow_locals; //!< allow local variables CONF_SECTION *template; }; diff --git a/src/lib/unlang/compile.c b/src/lib/unlang/compile.c index cb41ef2e07b..187a64542ab 100644 --- a/src/lib/unlang/compile.c +++ b/src/lib/unlang/compile.c @@ -2018,6 +2018,16 @@ static unlang_t *compile_edit_pair(unlang_t *parent, unlang_compile_t *unlang_ct return out; } +/** Compile a variable definition. + * + * Definitions which are adjacent to one another are automatically merged + * into one larger variable definition. + */ +static unlang_t *compile_variable(UNUSED unlang_t *parent, UNUSED unlang_compile_t *unlang_ctx, UNUSED unlang_t **prev, UNUSED CONF_PAIR *cp) +{ + return UNLANG_IGNORE; +} + /* * Compile action && rcode for later use. */ @@ -2360,6 +2370,7 @@ static unlang_t *compile_children(unlang_group_t *g, unlang_compile_t *unlang_ct bool was_if = false; char const *skip_else = NULL; unlang_t *edit = NULL; + unlang_t *var = NULL; c = unlang_group_to_generic(g); @@ -2377,6 +2388,8 @@ static unlang_t *compile_children(unlang_group_t *g, unlang_compile_t *unlang_ct char const *name = NULL; CONF_SECTION *subcs = cf_item_to_section(ci); + var = NULL; + /* * Skip precompiled blocks. This is * mainly for policies. @@ -2466,6 +2479,7 @@ static unlang_t *compile_children(unlang_group_t *g, unlang_compile_t *unlang_ct } goto add_child; + } else if (cf_item_is_pair(ci)) { char const *attr; CONF_PAIR *cp = cf_item_to_pair(ci); @@ -2487,6 +2501,16 @@ static unlang_t *compile_children(unlang_group_t *g, unlang_compile_t *unlang_ct edit = NULL; /* no longer doing implicit merging of edits */ + /* + * Variable definition. + */ + if (cf_pair_operator(cp) == T_OP_CMP_TRUE) { + single = compile_variable(c, unlang_ctx, &var, cp); + goto check_single; + } + + var = NULL; + /* * Bare "foo = bar" is disallowed. */ @@ -2502,6 +2526,7 @@ static unlang_t *compile_children(unlang_group_t *g, unlang_compile_t *unlang_ct * etc." */ single = compile_item(c, unlang_ctx, ci); + check_single: if (!single) { cf_log_err(ci, "Invalid keyword \"%s\".", attr); talloc_free(c);