]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Process casts during tokenization for single and double quoted strings
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 25 Jan 2022 13:37:16 +0000 (07:37 -0600)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Tue, 25 Jan 2022 13:37:16 +0000 (07:37 -0600)
src/lib/server/tmpl_tokenize.c
src/lib/unlang/xlat.h
src/lib/unlang/xlat_eval.c
src/lib/unlang/xlat_tokenize.c
src/tests/unit/condition/base.txt
src/tests/unit/condition/casts.txt [new file with mode: 0644]

index 60568ffe461765456f93c02973c327f96d4d8a27..de102dc2175afb03b4a6ba0e05d12a859ae9fa3d 100644 (file)
@@ -2084,6 +2084,56 @@ ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err,
        return slen;
 }
 
+/** Create TMPL_TYPE_DATA from a string
+ *
+ * @param[in] ctx              to allocate tmpl to.
+ * @param[out] out             where to write tmpl.
+ * @param[in] in               sbuff to parse.
+ * @param[in] quote            surrounding the operand to parse.
+ * @param[in] d_rules          specifying the cast and any enumeration values.
+ * @param[in] allow_enum       Whether parsing the value as an enum should be allowed.
+ * @param[in] p_rules          formatting rules.
+ * @return
+ *     - <0 on error
+ *     - >=0 on success.
+ */
+static fr_slen_t tmpl_afrom_value_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in,
+                                        fr_token_t quote,
+                                        tmpl_data_rules_t const *d_rules, bool allow_enum,
+                                        fr_sbuff_parse_rules_t const *p_rules)
+{
+       fr_sbuff_t      our_in = FR_SBUFF(in);
+       fr_value_box_t  tmp, *actual;
+       tmpl_t          *vpt;
+       fr_slen_t       slen;
+
+       if (!fr_type_is_leaf(d_rules->cast)) {
+               fr_strerror_printf("%s is not a valid cast type",
+                                  fr_type_to_str(d_rules->cast));
+               return 0;
+       }
+
+       vpt = tmpl_alloc_null(ctx);
+       slen = fr_value_box_from_substr(vpt, &tmp,
+                                       d_rules->cast, allow_enum ? d_rules->enumv : NULL,
+                                       &our_in, p_rules, false);
+       if (slen < 0) {
+               talloc_free(vpt);
+               return slen;
+       }
+
+       tmpl_init(vpt, TMPL_TYPE_DATA, quote, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in));
+
+       actual = tmpl_value(vpt);
+       fr_value_box_copy_shallow(NULL, actual, &tmp);
+
+       *out = vpt;
+
+       TMPL_VERIFY(vpt);
+
+       return fr_sbuff_set(in, &our_in);
+}
+
 /** Parse a truth value
  *
  * @param[in] ctx      to allocate tmpl to.
@@ -2608,35 +2658,8 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                /*
                 *      Deal with explicit casts...
                 */
-               if (!fr_type_is_null(t_rules->data.cast)) {
-                       fr_value_box_t tmp, *actual;
-
-                       if (!fr_type_is_leaf(t_rules->data.cast)) {
-                               fr_strerror_printf("%s is not a valid cast type",
-                                                  fr_type_to_str(t_rules->data.cast));
-                               return 0;
-                       }
-
-                       vpt = tmpl_alloc_null(ctx);
-                       slen = fr_value_box_from_substr(vpt, &tmp,
-                                                       t_rules->data.cast, t_rules->data.enumv,
-                                                       &our_in, p_rules, false);
-                       if (slen < 0) {
-                               talloc_free(vpt);
-                               return slen;
-                       }
-
-                       tmpl_init(vpt, TMPL_TYPE_DATA, quote, fr_sbuff_start(&our_in), fr_sbuff_used(&our_in));
-
-                       actual = tmpl_value(vpt);
-                       fr_value_box_copy_shallow(NULL, actual, &tmp);
-
-                       *out = vpt;
-
-                       TMPL_VERIFY(vpt);
-
-                       return fr_sbuff_set(in, &our_in);
-               }
+               if (!fr_type_is_null(t_rules->data.cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
+                                                                                        &t_rules->data, true, p_rules);
 
                /*
                 *      See if it's a boolean value
@@ -2727,6 +2750,16 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                return fr_sbuff_set(in, &our_in);
 
        case T_SINGLE_QUOTED_STRING:
+               /*
+                *      Single quoted strings can be
+                *      cast to a specific data type
+                *      immediately as they cannot contain
+                *      expansions.
+                */
+               if (!fr_type_is_null(t_rules->data.cast)) return tmpl_afrom_value_substr(ctx, out, in, quote,
+                                                                                        &t_rules->data, false,
+                                                                                        p_rules);
+
                vpt = tmpl_alloc_null(ctx);
                slen = fr_sbuff_out_aunescape_until(vpt, &str, &our_in, SIZE_MAX,
                                                    p_rules ? p_rules->terminals : NULL,
@@ -2739,6 +2772,7 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
        {
                xlat_exp_t      *head = NULL;
                xlat_flags_t    flags = {};
+               tmpl_type_t     type = TMPL_TYPE_XLAT;
 
                vpt = tmpl_alloc_null(ctx);
 
@@ -2751,27 +2785,42 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                if (!head) return slen;
 
                /*
-                *      If the string doesn't contain an xlat, just
-                *      convert the xlat expansion into an unescaped
-                *      literal for parsing later.
+                *      If the string doesn't contain an xlat,
+                *      and we want to cast it as a specific
+                *      type, then do the conversion now.
                 */
-               if (xlat_to_string(vpt, &str, &head)) {
-                       tmpl_init(vpt, TMPL_TYPE_UNRESOLVED, quote, fr_sbuff_start(&our_in), slen);
-                       vpt->data.unescaped = str;      /* Store the unescaped string for parsing later */
+               if (xlat_is_literal(head)) {
+                       if (!fr_type_is_null(t_rules->data.cast)) {
+                               talloc_free(vpt);               /* Also frees any nodes */
+
+                               return tmpl_afrom_value_substr(ctx, out,
+                                                              in, quote,
+                                                              &t_rules->data, false, p_rules);
+                       }
+
+                       /*
+                        *      If the string doesn't contain an xlat
+                        *      and there's no cast, we just store
+                        *      the string for conversion later.
+                        */
+                       if (xlat_to_string(vpt, &str, &head)) {
+                               xlat_exp_free(&head);           /* Free up any memory */
+
+                               tmpl_init(vpt, TMPL_TYPE_UNRESOLVED, quote, fr_sbuff_start(&our_in), slen);
+                               vpt->data.unescaped = str;      /* Store the unescaped string for parsing later */
+                               break;
+                       }
+               }
 
                /*
                 *      If the string actually contains an xlat
                 *      store the compiled xlat.
                 */
-               } else {
-                       tmpl_type_t     type = TMPL_TYPE_XLAT;
-
-                       if (flags.needs_resolving) UNRESOLVED_SET(&type);
+               if (flags.needs_resolving) UNRESOLVED_SET(&type);
 
-                       tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen);
-                       vpt->data.xlat.ex = head;
-                       vpt->data.xlat.flags = flags;
-               }
+               tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen);
+               vpt->data.xlat.ex = head;
+               vpt->data.xlat.flags = flags;
        }
                break;
 
@@ -2811,6 +2860,7 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
 
                xlat_exp_t              *head = NULL;
                xlat_flags_t            flags = {};
+               tmpl_type_t             type = TMPL_TYPE_REGEX_XLAT;
 
                vpt = tmpl_alloc_null(ctx);
 
@@ -2820,31 +2870,28 @@ ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out,
                /*
                 *      Check if the string actually contains an xlat
                 *      if it doesn't, we unfortunately still
-                *      can't compile it here, as we don't know if it
-                *      should be ephemeral or what flags should be used
+                *      can't compile the regex here, as we don't know if
+                *      it should be ephemeral or what flags should be used
                 *      during the compilation.
                 *
-                *      The caller will need to do the compilation
-                *      after we return.
+                *      The caller will need to do the compilation after we
+                *      return.
                 */
                if (xlat_to_string(vpt, &str, &head)) {
                        tmpl_init(vpt, TMPL_TYPE_REGEX_UNCOMPILED, quote, fr_sbuff_start(&our_in), slen);
                        vpt->data.unescaped = str;      /* Store the unescaped string for compilation later */
-
+                       break;
+               }
                /*
                 *      Mark the regex up as a regex-xlat which
                 *      will need expanding before evaluation, and can never
                 *      be pre-compiled.
                 */
-               } else {
-                       tmpl_type_t type = TMPL_TYPE_REGEX_XLAT;
-
-                       if (flags.needs_resolving) UNRESOLVED_SET(&type);
+               if (flags.needs_resolving) UNRESOLVED_SET(&type);
 
-                       tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen);
-                       vpt->data.xlat.ex = head;
-                       vpt->data.xlat.flags = flags;
-               }
+               tmpl_init(vpt, type, quote, fr_sbuff_start(&our_in), slen);
+               vpt->data.xlat.ex = head;
+               vpt->data.xlat.flags = flags;
        }
                break;
 
index 0518a1d463ca691c9a148209ff681c86f93e3691..c049bbd6ce3081cc2a6d164d760931aa8921e121 100644 (file)
@@ -320,7 +320,7 @@ int         xlat_validate_function_args(xlat_exp_t *node);
 
 void           xlat_debug(xlat_exp_t const *node);
 
-bool           xlat_is_value_box(xlat_exp_t const *head);
+bool           xlat_is_literal(xlat_exp_t const *head);
 
 bool           xlat_to_string(TALLOC_CTX *ctx, char **str, xlat_exp_t **head);
 
index bee412f7568070b6bf8342b6d80c27fdbaf9cd77..ea6008bbc8dd60d68cf42fe7732debc3580e1e94 100644 (file)
@@ -209,7 +209,7 @@ static inline void xlat_debug_log_expansion(request_t *request, xlat_exp_t const
         *      we print the concatenated arguments list as
         *      well as the original fmt string.
         */
-       if ((node->type == XLAT_FUNC) && !xlat_is_value_box(node->child)) {
+       if ((node->type == XLAT_FUNC) && !xlat_is_literal(node->child)) {
                RDEBUG2("      (%%%c%s:%pM%c)",
                        (node->call.func->input_type == XLAT_INPUT_ARGS) ? '(' : '{',
                        node->call.func->name, args,
index e60581b674ab54a562cd473239787128ab954532..f96f2aa3386b5e864a26ea23e312128748744da1 100644 (file)
@@ -1581,7 +1581,7 @@ ssize_t xlat_tokenize(TALLOC_CTX *ctx, xlat_exp_t **head, xlat_flags_t *flags, f
  *     - true if expansion contains only literal elements.
  *     - false if expansion contains expandable elements.
  */
-bool xlat_is_value_box(xlat_exp_t const *head)
+bool xlat_is_literal(xlat_exp_t const *head)
 {
        xlat_exp_t const *node;
 
@@ -1621,7 +1621,7 @@ bool xlat_to_string(TALLOC_CTX *ctx, char **str, xlat_exp_t **head)
         *      list until we find a non-literal.
         */
        for (node = *head; node; node = node->next) {
-               if (!xlat_is_value_box(node)) return false;
+               if (!xlat_is_literal(node)) return false;
                len += talloc_array_length(node->fmt) - 1;
        }
 
index abd7ab8ef1613d1a90dfcf6d51289aaaae1671b2..9c58934d1fed39e4c80855518e2853e98308fe65 100644 (file)
@@ -409,7 +409,7 @@ match &User-Name == "foo"
 
 # This used to be expr, but expr isn't a builtin, so it failed...
 condition <integer>"%{md4: 1 + 1}" < &NAS-Port
-match &NAS-Port > "%{md4: 1 + 1}"
+match &NAS-Port > <uint32>"%{md4: 1 + 1}"
 
 #
 #  The string gets parsed as an IP address.
diff --git a/src/tests/unit/condition/casts.txt b/src/tests/unit/condition/casts.txt
new file mode 100644 (file)
index 0000000..945a165
--- /dev/null
@@ -0,0 +1,35 @@
+#
+#  Tests for parsing conditional expressions.
+#
+#  $Id$
+#
+
+proto-dictionary radius
+tmpl-rules allow_unresolved=yes allow_unknown=yes
+
+# Forcefully cast RHS bareword
+condition &User-Name == <ipaddr>192.168.0.1
+match <ipaddr>&User-Name == 192.168.0.1
+
+# Forcefully cast LHS bareword
+condition <ipaddr>192.168.0.1 == &User-Name
+match <ipaddr>&User-Name == 192.168.0.1
+
+# Forcefully cast RHS single quotes
+#condition &Framed-IP-Address == <ipaddr>'192.168.0.1'
+#match &Framed-IP-Address == <ipaddr>'192.168.0.1'
+
+# Forcefully cast LHS single quotes
+#condition <ipaddr>'192.168.0.1' == &Framed-IP-Address
+#match <ipaddr>'192.168.0.1' == &Framed-IP-Address
+
+# Forcefully cast RHS double quotes
+#condition &User-Name == <ipaddr>"192.168.0.1"
+#match &User-Name == <ipaddr>"192.168.0.1"
+
+# Forcefully cast LHS single quotes
+#condition <ipaddr>"192.168.0.1" == &User-Name
+#match <ipaddr>"192.168.0.1" == &User-Name
+
+count
+match 6