]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
update parser to allow local variable definitions
authorAlan T. DeKok <aland@freeradius.org>
Thu, 24 Nov 2022 14:22:09 +0000 (09:22 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Thu, 24 Nov 2022 20:06:15 +0000 (15:06 -0500)
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.

src/lib/server/cf_file.c
src/lib/server/cf_priv.h
src/lib/unlang/compile.c

index f1f0f62ce5fe5d46847c34e31d275ddbf0f15cf0..53773c3f04d1660fd2ee6fc244750a383a8de31e 100644 (file)
@@ -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
index aa2f2ef84779e839a49a4d4ca53c3b5276f9e5ce..a99bf7c676ff9957ecc064ee7108f30658800644 100644 (file)
@@ -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;
 };
index cb41ef2e07b1823a5f464b41ba9479b4e19879ab..187a64542ab9f513e2b668395bcf680ba24484bf 100644 (file)
@@ -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);