From: Alan T. DeKok Date: Wed, 8 Dec 2021 15:20:01 +0000 (-0500) Subject: compile edit sections, too X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0cbd112ada2008c33437bcd55889adabcefe3f3a;p=thirdparty%2Ffreeradius-server.git compile edit sections, too unfortunately this can't be tested much until we have the new function which adds VPs as necessary. --- diff --git a/src/lib/unlang/compile.c b/src/lib/unlang/compile.c index ab598c6771..83a87a66c9 100644 --- a/src/lib/unlang/compile.c +++ b/src/lib/unlang/compile.c @@ -1528,12 +1528,204 @@ static unlang_t *compile_filter(unlang_t *parent, unlang_compile_t *unlang_ctx, return c; } -/** Compile one edit. +/** Validate and fixup a map that's part of an edit section. + * + * @param map to validate. + * @param ctx data to pass to fixup function (currently unused). + * @return 0 if valid else -1. + * + * @todo - this is only called for CONF_PAIR maps, not for + * CONF_SECTION. So when we parse nested maps, there's no validation + * done of the CONF_SECTION. In order to fix this, we need to have + * map_afrom_cs() call the validation function for the CONF_SECTION + * *before* recursing. + */ +static int unlang_fixup_edit(map_t *map, void *ctx) +{ + CONF_PAIR *cp = cf_item_to_pair(map->ci); + fr_dict_attr_t const *da; + fr_dict_attr_t const *parent = NULL; + + if (map->op != T_OP_EQ) { + cf_log_err(cp, "When creating an 'in-place list', the attributes can only be specified with with \"=\" operator"); + return -1; + } + + if (!map->parent) { + parent = *(fr_dict_attr_t **) ctx; + + } else if (tmpl_is_attr(map->parent->lhs)) { + parent = tmpl_da(map->parent->lhs); + } + + /* + * Anal-retentive checks. + */ + if (DEBUG_ENABLED3) { + if (tmpl_is_attr(map->lhs) && (map->lhs->name[0] != '&')) { + cf_log_warn(cp, "Please change attribute reference to '&%s %s ...'", + map->lhs->name, fr_table_str_by_value(fr_tokens_table, map->op, "")); + } + + if (tmpl_is_attr(map->rhs) && (map->rhs->name[0] != '&')) { + cf_log_warn(cp, "Please change attribute reference to '... %s &%s'", + fr_table_str_by_value(fr_tokens_table, map->op, ""), map->rhs->name); + } + } + + switch (map->lhs->type) { + case TMPL_TYPE_ATTR: + da = tmpl_da(map->lhs); + if (!da->flags.internal && parent && (parent->type != FR_TYPE_GROUP) && + (da->parent != parent)) { + cf_log_err(cp, "Invalid location for %s - it is not a child of %s", + da->name, parent->name); + return -1; + } + break; + + case TMPL_TYPE_XLAT_UNRESOLVED: + case TMPL_TYPE_XLAT: + break; + + default: + cf_log_err(map->ci, "Left side of map must be an attribute " + "or an xlat (that expands to an attribute), not a %s", + fr_table_str_by_value(tmpl_type_table, map->lhs->type, "")); + return -1; + } + + fr_assert(map->rhs); + + switch (map->rhs->type) { + case TMPL_TYPE_UNRESOLVED: + case TMPL_TYPE_XLAT_UNRESOLVED: + case TMPL_TYPE_XLAT: + case TMPL_TYPE_ATTR: + case TMPL_TYPE_EXEC: + break; + + default: + cf_log_err(map->ci, "Right side of map must be an attribute, literal, xlat or exec"); + return -1; + } + + return 0; +} + +/** Compile one edit section. * * Edits which are adjacent to one another are automatically merged * into one larger edit transaction. */ -static unlang_t *compile_edit(unlang_t *parent, unlang_compile_t *unlang_ctx, unlang_t **prev, CONF_PAIR *cp) +static unlang_t *compile_edit_section(unlang_t *parent, unlang_compile_t *unlang_ctx, unlang_t **prev, CONF_SECTION *cs) +{ + unlang_edit_t *edit, *edit_free; + unlang_t *c, *out = UNLANG_IGNORE; + map_t *map; + char const *name; + fr_token_t op; + ssize_t slen; + fr_dict_attr_t const *parent_da; + + tmpl_rules_t t_rules; + + name = cf_section_name2(cs); + if (name) { + cf_log_err(cs, "Unexpected text for editing list %s.", cf_section_name1(cs)); + return NULL; + } + op = cf_section_name2_quote(cs); + if ((op == T_INVALID) || !fr_assignment_op[op]) { + cf_log_err(cs, "Invalid operator '%s' for editing list %s.", name, cf_section_name1(cs)); + return NULL; + } + + /* + * We allow unknown attributes here. + */ + t_rules = *(unlang_ctx->rules); + t_rules.allow_unknown = true; + t_rules.list_as_attr = true; + RULES_VERIFY(&t_rules); + + c = *prev; + edit = edit_free = NULL; + + if (c && (c->type == UNLANG_TYPE_EDIT)) { + edit = unlang_generic_to_edit(c); + + } else { + edit = talloc_zero(parent, unlang_edit_t); + if (!edit) return NULL; + + c = out = unlang_edit_to_generic(edit); + c->parent = parent; + c->next = NULL; + c->name = cf_section_name1(cs); + c->debug_name = c->name; + c->type = UNLANG_TYPE_EDIT; + + fr_map_list_init(&edit->maps); + edit_free = edit; + + compile_action_defaults(c, unlang_ctx); + } + + /* + * Allocate the map and initialize it. + */ + MEM(map = talloc_zero(parent, map_t)); + map->op = op; + map->ci = cf_section_to_item(cs); + fr_map_list_init(&map->child); + + name = cf_section_name1(cs); + + slen = tmpl_afrom_attr_str(map, NULL, &map->lhs, name, &t_rules); + if (slen <= 0) { + cf_log_err(cs, "Failed parsing list reference %s", name); + fail: + talloc_free(edit_free); + return NULL; + } + + /* + * If the DA isn't structural, then it can't have children. + */ + parent_da = tmpl_da(map->lhs); + if (!fr_type_is_structural(parent_da->type)) { + cf_log_err(cs, "Only structural data types can be assigned a list"); + goto fail; + } + + if (map_afrom_cs(map, &map->child, cs, &t_rules, &t_rules, unlang_fixup_edit, &parent_da, 256) < 0) { + goto fail; + } + + /* + * Do basic sanity checks and resolving. + */ + if (!pass2_fixup_map(map, unlang_ctx->rules, NULL)) goto fail; + + /* + * Check operators, and ensure that the RHS has been + * resolved. + */ +// if (unlang_fixup_update(map, NULL) < 0) goto fail; + + fr_dlist_insert_tail(&edit->maps, map); + + *prev = c; + return out; +} + +/** Compile one edit pair + * + * Edits which are adjacent to one another are automatically merged + * into one larger edit transaction. + */ +static unlang_t *compile_edit_pair(unlang_t *parent, unlang_compile_t *unlang_ctx, unlang_t **prev, CONF_PAIR *cp) { unlang_edit_t *edit, *edit_free; unlang_t *c, *out = UNLANG_IGNORE; @@ -1943,8 +2135,6 @@ static unlang_t *compile_children(unlang_group_t *g, unlang_compile_t *unlang_ct char const *name = NULL; CONF_SECTION *subcs = cf_item_to_section(ci); - edit = NULL; /* no longer doing implicit merging of edits */ - /* * Skip precompiled blocks. This is * mainly for policies. @@ -1958,14 +2148,20 @@ static unlang_t *compile_children(unlang_group_t *g, unlang_compile_t *unlang_ct name = cf_section_name1(subcs); /* - * In-line attribute editing is not supported. + * In-line attribute editing. */ if (*name == '&') { - cf_log_err(subcs, "Please use 'update' sections to add / modify / delete attributes"); - talloc_free(c); - return NULL; + single = compile_edit_section(c, unlang_ctx, &edit, subcs); + if (!single) { + talloc_free(c); + return NULL; + } + + goto add_child; } + edit = NULL; /* no longer doing implicit merging of edits */ + if (strcmp(name, "actions") == 0) { if (!compile_action_subsection(c, g->cs, subcs)) { talloc_free(c); @@ -2025,7 +2221,7 @@ static unlang_t *compile_children(unlang_group_t *g, unlang_compile_t *unlang_ct * In-line attribute editing is not supported. */ if (*attr == '&') { - single = compile_edit(c, unlang_ctx, &edit, cp); + single = compile_edit_pair(c, unlang_ctx, &edit, cp); if (!single) { talloc_free(c); return NULL;