From: Arran Cudbard-Bell Date: Tue, 25 Jan 2022 13:37:16 +0000 (-0600) Subject: Process casts during tokenization for single and double quoted strings X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=84822038cae3abac87ef58f827ea7f7f2c4730d4;p=thirdparty%2Ffreeradius-server.git Process casts during tokenization for single and double quoted strings --- diff --git a/src/lib/server/tmpl_tokenize.c b/src/lib/server/tmpl_tokenize.c index 60568ffe461..de102dc2175 100644 --- a/src/lib/server/tmpl_tokenize.c +++ b/src/lib/server/tmpl_tokenize.c @@ -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; diff --git a/src/lib/unlang/xlat.h b/src/lib/unlang/xlat.h index 0518a1d463c..c049bbd6ce3 100644 --- a/src/lib/unlang/xlat.h +++ b/src/lib/unlang/xlat.h @@ -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); diff --git a/src/lib/unlang/xlat_eval.c b/src/lib/unlang/xlat_eval.c index bee412f7568..ea6008bbc8d 100644 --- a/src/lib/unlang/xlat_eval.c +++ b/src/lib/unlang/xlat_eval.c @@ -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, diff --git a/src/lib/unlang/xlat_tokenize.c b/src/lib/unlang/xlat_tokenize.c index e60581b674a..f96f2aa3386 100644 --- a/src/lib/unlang/xlat_tokenize.c +++ b/src/lib/unlang/xlat_tokenize.c @@ -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; } diff --git a/src/tests/unit/condition/base.txt b/src/tests/unit/condition/base.txt index abd7ab8ef16..9c58934d1fe 100644 --- a/src/tests/unit/condition/base.txt +++ b/src/tests/unit/condition/base.txt @@ -409,7 +409,7 @@ match &User-Name == "foo" # This used to be expr, but expr isn't a builtin, so it failed... condition "%{md4: 1 + 1}" < &NAS-Port -match &NAS-Port > "%{md4: 1 + 1}" +match &NAS-Port > "%{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 index 00000000000..945a16509f6 --- /dev/null +++ b/src/tests/unit/condition/casts.txt @@ -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 == 192.168.0.1 +match &User-Name == 192.168.0.1 + +# Forcefully cast LHS bareword +condition 192.168.0.1 == &User-Name +match &User-Name == 192.168.0.1 + +# Forcefully cast RHS single quotes +#condition &Framed-IP-Address == '192.168.0.1' +#match &Framed-IP-Address == '192.168.0.1' + +# Forcefully cast LHS single quotes +#condition '192.168.0.1' == &Framed-IP-Address +#match '192.168.0.1' == &Framed-IP-Address + +# Forcefully cast RHS double quotes +#condition &User-Name == "192.168.0.1" +#match &User-Name == "192.168.0.1" + +# Forcefully cast LHS single quotes +#condition "192.168.0.1" == &User-Name +#match "192.168.0.1" == &User-Name + +count +match 6