From: Alan T. DeKok Date: Fri, 8 Jan 2021 22:41:17 +0000 (-0500) Subject: use fr_type_promote() in condition tokenizer X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0ba2e2b17bd2d154131a03ea8e769e00c4609651;p=thirdparty%2Ffreeradius-server.git use fr_type_promote() in condition tokenizer and update one test: we can cast a "string" to "ipaddr" for comparisons --- diff --git a/src/lib/server/cond_tokenize.c b/src/lib/server/cond_tokenize.c index 2691b27c5d2..8dd7e09d368 100644 --- a/src/lib/server/cond_tokenize.c +++ b/src/lib/server/cond_tokenize.c @@ -139,6 +139,121 @@ ssize_t cond_print(fr_sbuff_t *out, fr_cond_t const *in) return fr_sbuff_set(out, &our_out); } +/** Promote the types in a FOO OP BAR comparison. + * + */ +static int cond_promote_types(fr_cond_t *c) +{ + fr_type_t lhs_type, rhs_type; + fr_type_t cast_type; + +#ifdef HAVE_REGEX + /* + * We don't do casting on regexes. + */ + if ((c->data.map->op == T_OP_REG_EQ) || (c->data.map->op == T_OP_REG_NE)) return 0; +#endif + + /* + * There's a cast, remember it. + */ + if (c->data.map->lhs->cast != FR_TYPE_INVALID) { + lhs_type = c->data.map->lhs->cast; + + /* + * Two explicit casts MUST be the same, otherwise + * it's an error. + * + * We only do type promotion when at least one + * data type is implicitly specified. + */ + if (c->data.map->rhs->cast != FR_TYPE_INVALID) { + if (c->data.map->rhs->cast != lhs_type) { + fr_strerror_const("Incompatible casts"); + return -1; + } + return 0; + } + + } else if (tmpl_is_data(c->data.map->lhs)) { + /* + * Choose the data type which was parsed. + */ + lhs_type = tmpl_value_type(c->data.map->lhs); + + } else if (tmpl_is_attr(c->data.map->lhs)) { + /* + * Choose the attribute type which was parsed. + */ + lhs_type = tmpl_da(c->data.map->lhs)->type; + + } else { + lhs_type = FR_TYPE_INVALID; + } + + if (c->data.map->rhs->cast != FR_TYPE_INVALID) { + rhs_type = c->data.map->rhs->cast; + + } else if (tmpl_is_data(c->data.map->rhs)) { + rhs_type = tmpl_value_type(c->data.map->rhs); + + } else if (tmpl_is_attr(c->data.map->rhs)) { + rhs_type = tmpl_da(c->data.map->rhs)->type; + + } else { + rhs_type = FR_TYPE_INVALID; + + /* + * Both sides are have unresolved issues. Leave + * them alone... + */ + if (lhs_type == FR_TYPE_INVALID) return 0; + } + + /* + * Both types are identical. Ensure that LHS / RHS are + * cast as appropriate. + */ + if (lhs_type == rhs_type) { + cast_type = lhs_type; + goto set_types; + } + + /* + * Only one side has a known data type. Cast the other + * side to it. + * + * Note that we don't check the return code for + * tmpl_cast_set(). If one side is an unresolved + * attribute, then the cast will fail. Which is fine, + * because we will just check it again after the pass2 + * fixups. + */ + if ((lhs_type != FR_TYPE_INVALID) && (rhs_type == FR_TYPE_INVALID)) { + (void) tmpl_cast_set(c->data.map->rhs, lhs_type); + return 0; + } + + if ((rhs_type != FR_TYPE_INVALID) && (lhs_type == FR_TYPE_INVALID)) { + (void) tmpl_cast_set(c->data.map->lhs, rhs_type); + return 0; + } + + cast_type = fr_type_promote(lhs_type, rhs_type); + fr_assert(cast_type != FR_TYPE_INVALID); + + /* + * Cast both sides to the promoted type. If the tmpl + * already has a data type, then the cast will just do + * nothing. + */ +set_types: + (void) tmpl_cast_set(c->data.map->lhs, cast_type); + (void) tmpl_cast_set(c->data.map->rhs, cast_type); + + return 0; +} + static bool cond_type_check(fr_cond_t *c, fr_type_t lhs_type) { @@ -1554,6 +1669,16 @@ static ssize_t cond_tokenize(TALLOC_CTX *ctx, fr_cond_t **out, fr_sbuff_adv_past_whitespace(&our_in, SIZE_MAX, NULL); + /* + * Promote the data types to the appropriate + * values. + */ + if (cond_promote_types(c) < 0) { + fr_sbuff_set(&our_in, our_in.start); + goto error; + + } + /* * Check cast type. We can have the RHS * a string if the LHS has a cast. But diff --git a/src/tests/unit/condition/base.txt b/src/tests/unit/condition/base.txt index b45c8163591..e93b418848f 100644 --- a/src/tests/unit/condition/base.txt +++ b/src/tests/unit/condition/base.txt @@ -409,8 +409,11 @@ match &User-Name == "foo" condition "%{md4: 1 + 1}" < &NAS-Port match &NAS-Port > "%{md4: 1 + 1}" +# +# The string gets parsed as an IP address. +# condition &Filter-Id == &Framed-IP-Address -match ERROR offset 0: Cannot compare attributes of type 'string' and 'ipaddr' +match &Filter-Id == &Framed-IP-Address condition 127.0.0.1 == &Filter-Id match ERROR offset 0: Cannot compare types 'ipaddr' (cast) and 'string' (attr)