]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
allow expressions in %{...} xlats
authorAlan T. DeKok <aland@freeradius.org>
Sun, 1 Oct 2023 12:54:20 +0000 (08:54 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Sun, 1 Oct 2023 13:16:18 +0000 (09:16 -0400)
src/lib/unlang/xlat_eval.c
src/lib/unlang/xlat_tokenize.c
src/tests/keywords/expand-expr [new file with mode: 0644]
src/tests/unit/xlat/base.txt

index fc138c698c6d8bbc2d5c54404a983420036fbaee..e2edaf0dabf142561b92214f6b2442750ee4de3c 100644 (file)
@@ -1197,7 +1197,7 @@ xlat_action_t xlat_frame_eval(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_exp_head_
 
                                if (tmpl_eval_pair(ctx, &result, request, node->vpt) < 0) goto fail;
 
-                       } else if (tmpl_is_exec(node->vpt)) { /* exec only */
+                       } else if (tmpl_is_exec(node->vpt) || tmpl_is_xlat(node->vpt)) {
                                xlat_exec_rctx_t *rctx;
 
                                /*
index d3b92f00455333dbe73371aa0f5f41e3013ef092..996330707140be2cdc7193b7815dd58f6f11e91b 100644 (file)
@@ -906,6 +906,13 @@ done:
        return 0;
 }
 
+static bool const tmpl_attr_allowed_chars[UINT8_MAX + 1] = {
+       SBUFF_CHAR_CLASS_ALPHA_NUM,
+       ['-'] = true, ['/'] = true, ['_'] = true,                       // fr_dict_attr_allowed_chars
+       ['.'] = true, ['*'] = true, ['#'] = true,
+       ['['] = true, [']'] = true,                                     // tmpls and attribute arrays
+};
+
 int xlat_tokenize_expansion(xlat_exp_head_t *head, fr_sbuff_t *in,
                            tmpl_rules_t const *t_rules)
 {
@@ -962,6 +969,71 @@ int xlat_tokenize_expansion(xlat_exp_head_t *head, fr_sbuff_t *in,
        }
 #endif /* HAVE_REGEX */
 
+       /*
+        *      See if it's an old-style function name.
+        */
+       fr_sbuff_marker(&s_m, in);
+       len = fr_sbuff_adv_past_allowed(in, SIZE_MAX, xlat_func_chars, NULL);
+       if (fr_sbuff_is_char(in, ':')) {
+               if (!len) goto missing_function;
+               goto check_for_attr;
+       }
+
+       /*
+        *      See if it's an attribute reference, with possible array stuff.
+        */
+       len += fr_sbuff_adv_past_allowed(in, SIZE_MAX, tmpl_attr_allowed_chars, NULL);
+       if (fr_sbuff_is_char(in, '}')) {
+               if (!len) goto empty_disallowed;
+               goto check_for_attr;
+       }
+
+       if (!fr_sbuff_extend(in)) {
+               fr_strerror_const("Missing closing brace");
+               fr_sbuff_marker_release(&s_m);
+               return -1;
+       }
+
+       /*
+        *      It must be an expression.
+        *
+        *      We wrap the xlat in a tmpl, so that the result is just a value, and not wrapped in another
+        *      XLAT_GROUP, which turns into a wrapper of FR_TYPE_GROUP in the value-box.
+        */
+       {
+               int ret;
+               char *fmt;
+               xlat_exp_t *node;
+               xlat_exp_head_t *child;
+
+               fr_sbuff_set(in, &s_m);         /* backtrack to the start of the expression */
+
+               MEM(node = xlat_exp_alloc(head, XLAT_TMPL, NULL, 0));
+               MEM(node->vpt = tmpl_alloc(node, TMPL_TYPE_XLAT, T_BARE_WORD, "", 1));
+
+               ret = xlat_tokenize_expression(node->vpt, &child, in, &attr_p_rules, t_rules);
+               if (ret <= 0) {
+                       talloc_free(node);
+                       return ret;
+               }
+
+               if (!fr_sbuff_next_if_char(in, '}')) {
+                       fr_strerror_const("Missing closing brace");
+                       return -1;
+               }
+
+               MEM(fmt = talloc_bstrndup(node, fr_sbuff_current(&s_m), fr_sbuff_behind(&s_m)));
+               xlat_exp_set_name_buffer_shallow(node, fmt);
+               tmpl_set_name_shallow(node->vpt, T_BARE_WORD, fmt, fr_sbuff_behind(&s_m));
+
+               tmpl_set_xlat(node->vpt, child);
+               xlat_exp_insert_tail(head, node);
+               return ret;
+       }
+
+check_for_attr:
+       fr_sbuff_set(in, &s_m);         /* backtrack */
+
        /*
         *      %{Attr-Name}
         *      %{Attr-Name[#]}
@@ -993,10 +1065,12 @@ int xlat_tokenize_expansion(xlat_exp_head_t *head, fr_sbuff_t *in,
        if (len == 0) {
                switch (hint) {
                case '}':
+               empty_disallowed:
                        fr_strerror_const("Empty expression is invalid");
                        return -1;
 
                case ':':
+               missing_function:
                        fr_strerror_const("Missing expansion function");
                        return -1;
 
diff --git a/src/tests/keywords/expand-expr b/src/tests/keywords/expand-expr
new file mode 100644 (file)
index 0000000..6f4d2f3
--- /dev/null
@@ -0,0 +1,20 @@
+uint32 foo
+uint32 bar
+uint32 baz
+uint32 none
+
+&foo = 1
+&bar = 2
+
+&baz := %{&foo + &bar}
+
+if !(&baz == 3) {
+       test_fail
+}
+
+&baz := %{&none || &foo}
+if !(&baz == &foo) {
+       test_fail
+}
+
+success
index 63706b75d662f4ff0fe388214428cf9642f547a4..4bbe2bc0d7b70b618a89e370661f052e988544d6 100644 (file)
@@ -237,19 +237,19 @@ xlat %{[baz}
 match ERROR offset 3: Missing attribute name
 
 xlat %{ }
-match ERROR offset 3: Invalid char ' ' in expression
+match ERROR offset 4: No operand found.  Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value
 
 xlat %{\t}
-match ERROR offset 3: Invalid attribute name
+match ERROR offset 3: No operand found.  Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value
 
 xlat %{\n}
-match ERROR offset 3: Invalid attribute name
+match ERROR offset 3: No operand found.  Expected &ref, literal, 'quoted literal', "%{expansion}", or enum value
 
 xlat %{foo }
-match ERROR offset 6: Invalid char ' ' in expression
+match ERROR offset 7: Unexpected text - attribute names must prefixed with '&'
 
 xlat %{foo bar}
-match ERROR offset 6: Invalid char ' ' in expression
+match ERROR offset 7: Invalid operator
 
 xlat %{test:
 match ERROR offset 8: Missing closing brace
@@ -294,8 +294,12 @@ xlat_argv /bin/sh "foo bar" "%{User-Name} %{Filter-Id}"
 match [0]{ /bin/sh }, [1]{ foo bar }, [2]{ %{User-Name} %{Filter-Id} }
 
 # and errors
+#
+#  @todo - we should really be unescaping the input in the sbuff, OR passing in the terminals
+#  to xlat_tokenize_expansion()
+#
 xlat_argv /bin/sh "foo bar" "%{User-Name} %{Filter-Id"
-match ERROR offset 45: Missing closing brace
+match ERROR offset 44: Unexpected text after enum value.  Expected operator
 
 # and text immediately after a variable expansion
 xlat_argv echo hello %{Tmp-String-0}:1234 world