From: Arran Cudbard-Bell Date: Tue, 22 Nov 2022 02:04:45 +0000 (-0800) Subject: Add support for unspecified attributes X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7ab6b81885ca32c92ed6ec2f1afcb068eb6e34f8;p=thirdparty%2Ffreeradius-server.git Add support for unspecified attributes --- diff --git a/src/lib/server/tmpl.h b/src/lib/server/tmpl.h index 856e10b46f0..b41e67ae416 100644 --- a/src/lib/server/tmpl.h +++ b/src/lib/server/tmpl.h @@ -384,6 +384,8 @@ struct tmpl_res_rules_s { typedef enum { TMPL_ATTR_TYPE_NORMAL = 0, //!< Normal, resolved, attribute ref. + TMPL_ATTR_TYPE_UNSPEC, //!< No attribute was specified as this level + ///< only a filter. TMPL_ATTR_TYPE_UNKNOWN, //!< We have an attribute number but ///< it doesn't match anything in the ///< dictionary, or isn't a child of diff --git a/src/lib/server/tmpl_dcursor.c b/src/lib/server/tmpl_dcursor.c index 62bb1e9bfca..a7467f6386b 100644 --- a/src/lib/server/tmpl_dcursor.c +++ b/src/lib/server/tmpl_dcursor.c @@ -100,8 +100,20 @@ void _tmpl_cursor_pair_init(TALLOC_CTX *list_ctx, fr_pair_list_t *list, tmpl_att .ar = ar, .list_ctx = list_ctx }; - fr_pair_dcursor_iter_init(&ns->cursor, list, _tmpl_cursor_child_next, ns); + /* + * Iterates over attributes of a specific type + */ + if (tmpl_is_list(cc->vpt) || ar_is_normal(ar)) { + fr_pair_dcursor_iter_init(&ns->cursor, list, _tmpl_cursor_child_next, ns); + /* + * Iterates over all attributes at this level + */ + } else if (ar_is_unspecified(ar)) { + fr_dcursor_init(&ns->cursor, fr_pair_list_to_dlist(list)); + } else { + fr_assert_msg(0, "Invalid attr reference type"); + } tmpl_cursor_nested_push(cc, ns); } diff --git a/src/lib/server/tmpl_tokenize.c b/src/lib/server/tmpl_tokenize.c index fb0083b0bc2..7ab992fa476 100644 --- a/src/lib/server/tmpl_tokenize.c +++ b/src/lib/server/tmpl_tokenize.c @@ -131,6 +131,7 @@ size_t tmpl_type_table_len = NUM_ELEMENTS(tmpl_type_table); */ static fr_table_num_ordered_t const attr_table[] = { { L("normal"), TMPL_ATTR_TYPE_NORMAL }, + { L("unspecified"), TMPL_ATTR_TYPE_UNSPEC }, { L("unknown"), TMPL_ATTR_TYPE_UNKNOWN }, { L("unresolved"), TMPL_ATTR_TYPE_UNRESOLVED } }; @@ -208,6 +209,7 @@ void tmpl_attr_ref_debug(const tmpl_attr_t *ar, int i) switch (ar->type) { case TMPL_ATTR_TYPE_NORMAL: + case TMPL_ATTR_TYPE_UNSPEC: case TMPL_ATTR_TYPE_UNKNOWN: if (!ar->da) { FR_FAULT_LOG("\t[%u] %s null%s%s%s", @@ -1034,6 +1036,9 @@ int tmpl_attr_copy(tmpl_t *dst, tmpl_t const *src) dst_ar->ar_da = src_ar->ar_da; break; + case TMPL_ATTR_TYPE_UNSPEC: /* Nothing to copy */ + break; + case TMPL_ATTR_TYPE_UNKNOWN: dst_ar->ar_unknown = fr_dict_unknown_afrom_da(dst_ar, src_ar->ar_unknown); break; @@ -1392,6 +1397,40 @@ static fr_slen_t tmpl_attr_parse_filter(tmpl_attr_error_t *err, tmpl_attr_t *ar, FR_SBUFF_SET_RETURN(name, &our_name); } +static inline CC_HINT(nonnull(3,4)) +fr_slen_t tmpl_attr_ref_from_unspecified_substr(tmpl_attr_t *ar, tmpl_attr_error_t *err, + tmpl_t *vpt, + fr_sbuff_t *name, tmpl_attr_rules_t const *t_rules) +{ + fr_slen_t slen; + + *ar = (tmpl_attr_t){ + .ar_num = NUM_UNSPEC, /* May be changed by tmpl_attr_parse_filter */ + .ar_type = TMPL_ATTR_TYPE_UNSPEC, + }; + + slen = tmpl_attr_parse_filter(err, ar, name, t_rules); + if (slen < 0) { + talloc_free(ar); + return slen; + /* + * No filters and no previous elements is the equivalent of '&' + * which is not allowed. + * + * &[] is allowed as this lets us perform filtering operations + * at the root. + */ + } else if ((slen == 0) && (tmpl_attr_num_elements(vpt) == 0)) { + fr_strerror_const("Invalid attribute name"); + if (err) *err = TMPL_ATTR_ERROR_INVALID_NAME; + return -1; + } + + tmpl_attr_insert(vpt, ar); + + return slen; +} + /** Parse an unresolved attribute, i.e. one which can't be found in the current dictionary * * This function calls itself recursively to process additional OID @@ -1415,77 +1454,71 @@ fr_slen_t tmpl_attr_ref_afrom_unresolved_substr(TALLOC_CTX *ctx, tmpl_attr_error fr_dict_attr_t const *parent, fr_dict_attr_t const *namespace, fr_sbuff_t *name, tmpl_attr_rules_t const *t_rules) { - tmpl_attr_t *ar = NULL; - int ret; + tmpl_attr_t *ar = NULL, *ar_curr; + fr_sbuff_t our_name = FR_SBUFF(name); + fr_slen_t slen; char *unresolved; - size_t len; /* - * Input too short - */ - if (!fr_sbuff_extend(name)) { - fr_strerror_const("Missing attribute reference"); - if (err) *err = TMPL_ATTR_ERROR_INVALID_NAME; - return -1; - } - - /* - * Mark the tmpl up as an unresolved attribute reference - * the attribute reference will be resolved later. + * Point we free from if something goes wrong. */ - vpt->type = TMPL_TYPE_ATTR_UNRESOLVED; + ar_curr = tmpl_attr_list_tail(tmpl_attr(vpt)); + for (;;) { + MEM(ar = talloc(ctx, tmpl_attr_t)); + /* + * Copy out a string of allowed dictionary chars to form + * the unresolved attribute name. + * + * This will be resolved later (outside of this function). + */ + slen = fr_sbuff_out_abstrncpy_allowed(ar, &unresolved, + &our_name, FR_DICT_ATTR_MAX_NAME_LEN + 1, + fr_dict_attr_allowed_chars); + if (slen == 0) { + slen = tmpl_attr_ref_from_unspecified_substr(ar, err, vpt, &our_name, t_rules); + if (slen < 0) { + fr_sbuff_advance(&our_name, +slen); + error: + talloc_free(ar); + tmpl_attr_list_talloc_free_to_tail(tmpl_attr(vpt), ar_curr); + return -1; + } + return fr_sbuff_set(name, &our_name); + } else if (slen > FR_DICT_ATTR_MAX_NAME_LEN) { + fr_strerror_const("Attribute name is too long"); + if (err) *err = TMPL_ATTR_ERROR_INVALID_NAME; + goto error; + } - MEM(ar = talloc(ctx, tmpl_attr_t)); - /* - * Copy out a string of allowed dictionary chars to form - * the unresolved attribute name. - * - * This will be resolved later (outside of this function). - */ - len = fr_sbuff_out_abstrncpy_allowed(ar, &unresolved, - name, FR_DICT_ATTR_MAX_NAME_LEN + 1, - fr_dict_attr_allowed_chars); - if (len == 0) { - fr_strerror_const("Invalid attribute name"); - if (err) *err = TMPL_ATTR_ERROR_INVALID_NAME; - error: - talloc_free(ar); - return -1; - } - if (len > FR_DICT_ATTR_MAX_NAME_LEN) { - fr_strerror_const("Attribute name is too long"); - if (err) *err = TMPL_ATTR_ERROR_INVALID_NAME; - goto error; - } + *ar = (tmpl_attr_t){ + .ar_num = NUM_UNSPEC, + .ar_type = TMPL_ATTR_TYPE_UNRESOLVED, + .ar_unresolved = unresolved, + .ar_unresolved_namespace = namespace, + .ar_parent = parent + }; - *ar = (tmpl_attr_t){ - .ar_num = NUM_UNSPEC, - .ar_type = TMPL_ATTR_TYPE_UNRESOLVED, - .ar_unresolved = unresolved, - .ar_unresolved_namespace = namespace, - .ar_parent = parent, - }; + if (tmpl_attr_parse_filter(err, ar, &our_name, t_rules) < 0) goto error; - if (tmpl_attr_parse_filter(err, ar, name, t_rules) < 0) goto error; + /* + * Insert the ar into the list of attribute references + */ + tmpl_attr_insert(vpt, ar); - /* - * Insert the ar into the list of attribute references - */ - tmpl_attr_insert(vpt, ar); + /* + * Once one OID component is created as unresolved all + * future OID components are also unresolved. + */ + if (!fr_sbuff_next_if_char(&our_name, '.')) break; + } /* - * Once one OID component is created as unresolved all - * future OID components are also unresolved. + * Mark the tmpl up as an unresolved attribute reference + * the attribute reference will be resolved later. */ - if (fr_sbuff_next_if_char(name, '.')) { - ret = tmpl_attr_ref_afrom_unresolved_substr(ctx, err, vpt, NULL, NULL, name, t_rules); - if (ret < 0) { - tmpl_attr_list_talloc_free_tail(&vpt->data.attribute.ar); /* Remove and free ar */ - return -1; - } - } + vpt->type = TMPL_TYPE_ATTR_UNRESOLVED; - return 0; + return fr_sbuff_set(name, &our_name); } /** Parse an attribute reference, either an OID or attribute name @@ -2188,10 +2221,10 @@ ssize_t tmpl_afrom_attr_substr(TALLOC_CTX *ctx, tmpl_attr_error_t *err, /* * Suppress useless casts. */ - if (tmpl_attr_tail_da(vpt)->type == tmpl_rules_cast(vpt)) { + if (tmpl_attr_tail_is_normal(vpt) && (tmpl_attr_tail_da(vpt)->type == tmpl_rules_cast(vpt))) { vpt->rules.cast = FR_TYPE_NULL; } - + /* * Ensure that the list is set correctly, so that * the returned vpt just doesn't just match the @@ -3689,6 +3722,7 @@ static inline CC_HINT(always_inline) int tmpl_attr_resolve(tmpl_t *vpt, tmpl_res while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) { switch (ar->type) { case TMPL_ATTR_TYPE_NORMAL: + case TMPL_ATTR_TYPE_UNSPEC: continue; /* Don't need to resolve */ case TMPL_ATTR_TYPE_UNKNOWN: @@ -4042,6 +4076,8 @@ static void attr_to_raw(tmpl_t *vpt, tmpl_attr_t *ref) ref->type = TMPL_ATTR_TYPE_UNKNOWN; } break; + case TMPL_ATTR_TYPE_UNSPEC: /* noop */ + break; case TMPL_ATTR_TYPE_UNKNOWN: ref->ar_unknown->type = FR_TYPE_OCTETS; @@ -4095,6 +4131,7 @@ int tmpl_attr_unknown_add(tmpl_t *vpt) switch (ar->type) { case TMPL_ATTR_TYPE_NORMAL: /* Skip */ + case TMPL_ATTR_TYPE_UNSPEC: continue; case TMPL_ATTR_TYPE_UNRESOLVED: /* Shouldn't have been called */ @@ -4344,6 +4381,7 @@ fr_slen_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t if (!tmpl_is_list(vpt) && (ar = tmpl_attr_list_tail(tmpl_attr(vpt)))) { switch (ar->type) { case TMPL_ATTR_TYPE_NORMAL: + case TMPL_ATTR_TYPE_UNSPEC: case TMPL_ATTR_TYPE_UNKNOWN: if (ar->ar_da->flags.is_raw) FR_SBUFF_IN_STRCPY_LITERAL_RETURN(&our_out, "raw."); break; @@ -4360,6 +4398,9 @@ fr_slen_t tmpl_attr_print(fr_sbuff_t *out, tmpl_t const *vpt, tmpl_attr_prefix_t ar = NULL; while ((ar = tmpl_attr_list_next(tmpl_attr(vpt), ar))) { if (!tmpl_is_list(vpt)) switch(ar->type) { + case TMPL_ATTR_TYPE_UNSPEC: + break; + case TMPL_ATTR_TYPE_NORMAL: case TMPL_ATTR_TYPE_UNKNOWN: { @@ -4708,6 +4749,27 @@ void tmpl_attr_verify(char const *file, int line, tmpl_t const *vpt) file, line); break; + case TMPL_ATTR_TYPE_UNSPEC: + if (seen_unknown) { + tmpl_attr_debug(vpt); + fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: " + "TMPL_TYPE_ATTR unspecified attribute " + "occurred after unknown attribute %s " + "in attr ref list", + file, line, + ar->unknown.da->name); + } + if (seen_unresolved) { + tmpl_attr_debug(vpt); + fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%u]: " + "TMPL_TYPE_ATTR unspecified attribute " + "occurred after unresolved attribute \"%s\"" + "in attr ref list", + file, line, + ar->ar_unresolved); + } + break; + case TMPL_ATTR_TYPE_UNRESOLVED: seen_unresolved = ar; fr_fatal_assert_msg(ar->ar_unresolved_namespace,