From: Alan T. DeKok Date: Tue, 19 Aug 2025 13:37:21 +0000 (-0400) Subject: remove "update" handlers X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5a257b084a661a183cdb721a75bc91f5d098bcc0;p=thirdparty%2Ffreeradius-server.git remove "update" handlers --- diff --git a/src/lib/unlang/map.c b/src/lib/unlang/map.c index cc6bc1fef2..a25cb00f29 100644 --- a/src/lib/unlang/map.c +++ b/src/lib/unlang/map.c @@ -34,26 +34,6 @@ RCSID("$Id$") #include "map_priv.h" -typedef enum { - UNLANG_UPDATE_MAP_INIT = 0, //!< Start processing a map. - UNLANG_UPDATE_MAP_EXPANDED_LHS, //!< Expand the LHS xlat or exec (if needed). - UNLANG_UPDATE_MAP_EXPANDED_RHS //!< Expand the RHS xlat or exec (if needed). -} unlang_update_state_t; - -/** State of an update block - * - */ -typedef struct { - fr_dcursor_t maps; //!< Cursor of maps to evaluate. - - fr_dlist_head_t vlm_head; //!< Head of list of VP List Mod. - - fr_value_box_list_t lhs_result; //!< Result of expanding the LHS - fr_value_box_list_t rhs_result; //!< Result of expanding the RHS. - - unlang_update_state_t state; //!< What we're currently doing. -} unlang_frame_state_update_t; - /** State of a map block * */ @@ -79,220 +59,6 @@ typedef struct { */ #define MAP_CTX(_mod_inst, _map_inst, _rctx) &(map_ctx_t){ .moi = _mod_inst, .mpi = _map_inst, .rctx = _rctx } -/** Apply a list of modifications on one or more fr_pair_t lists. - * - * @param[in] request The current request. - * @param[out] p_result The rcode indicating what the result - * of the operation was. - * @return - * - UNLANG_ACTION_CALCULATE_RESULT changes were applied. - * - UNLANG_ACTION_PUSHED_CHILD async execution of an expansion is required. - */ -static unlang_action_t list_mod_apply(unlang_result_t *p_result, request_t *request) -{ - unlang_stack_t *stack = request->stack; - unlang_stack_frame_t *frame = &stack->frame[stack->depth]; - unlang_frame_state_update_t *update_state = frame->state; - vp_list_mod_t const *vlm = NULL; - - /* - * No modifications... - */ - if (fr_dlist_empty(&update_state->vlm_head)) { - RDEBUG2("Nothing to update"); - goto done; - } - - /* - * Apply the list of modifications. This should not fail - * except on memory allocation error. - */ - while ((vlm = fr_dlist_next(&update_state->vlm_head, vlm))) { - int ret; - - ret = map_list_mod_apply(request, vlm); - if (!fr_cond_assert(ret == 0)) { - TALLOC_FREE(frame->state); - - return UNLANG_ACTION_FAIL; - } - } - -done: - RETURN_UNLANG_NOOP; -} - -/** Create a list of modifications to apply to one or more fr_pair_t lists - * - * @param[out] p_result The rcode indicating what the result - * of the operation was. - * @param[in] request The current request. - * @param[in] frame Current stack frame. - * @return - * - UNLANG_ACTION_CALCULATE_RESULT changes were applied. - * - UNLANG_ACTION_PUSHED_CHILD async execution of an expansion is required. - */ -static unlang_action_t list_mod_create(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame) -{ - unlang_frame_state_update_t *update_state = talloc_get_type_abort(frame->state, unlang_frame_state_update_t); - map_t *map; - - /* - * Iterate over the maps producing a set of modifications to apply. - */ - for (map = fr_dcursor_current(&update_state->maps); - map; - map = fr_dcursor_next(&update_state->maps)) { - repeatable_set(frame); /* Call us again when done */ - - switch (update_state->state) { - case UNLANG_UPDATE_MAP_INIT: - update_state->state = UNLANG_UPDATE_MAP_EXPANDED_LHS; - - fr_assert(fr_value_box_list_empty(&update_state->lhs_result)); /* Should have been consumed */ - fr_assert(fr_value_box_list_empty(&update_state->rhs_result)); /* Should have been consumed */ - - switch (map->lhs->type) { - default: - break; - - case TMPL_TYPE_EXEC: - if (unlang_tmpl_push(update_state, NULL, &update_state->lhs_result, - request, map->lhs, - NULL, UNLANG_SUB_FRAME) < 0) { - return UNLANG_ACTION_STOP_PROCESSING; - } - return UNLANG_ACTION_PUSHED_CHILD; - - case TMPL_TYPE_XLAT: - if (unlang_xlat_push(update_state, NULL, &update_state->lhs_result, - request, tmpl_xlat(map->lhs), false) < 0) { - return UNLANG_ACTION_STOP_PROCESSING; - } - return UNLANG_ACTION_PUSHED_CHILD; - - case TMPL_TYPE_REGEX_XLAT_UNRESOLVED: - case TMPL_TYPE_REGEX: - case TMPL_TYPE_REGEX_UNCOMPILED: - case TMPL_TYPE_REGEX_XLAT: - case TMPL_TYPE_XLAT_UNRESOLVED: - fr_assert(0); - error: - TALLOC_FREE(frame->state); - repeatable_clear(frame); - return UNLANG_ACTION_FAIL; - } - FALL_THROUGH; - - case UNLANG_UPDATE_MAP_EXPANDED_LHS: - /* - * map_to_list_mod() already concatenates the LHS, so we don't need to do it here. - */ - if (!map->rhs) goto next; - - update_state->state = UNLANG_UPDATE_MAP_EXPANDED_RHS; - - switch (map->rhs->type) { - default: - break; - - case TMPL_TYPE_EXEC: - if (unlang_tmpl_push(update_state, NULL, &update_state->rhs_result, - request, map->rhs, NULL, UNLANG_SUB_FRAME) < 0) { - return UNLANG_ACTION_STOP_PROCESSING; - } - return UNLANG_ACTION_PUSHED_CHILD; - - case TMPL_TYPE_XLAT: - if (unlang_xlat_push(update_state, NULL, &update_state->rhs_result, - request, tmpl_xlat(map->rhs), false) < 0) { - return UNLANG_ACTION_STOP_PROCESSING; - } - return UNLANG_ACTION_PUSHED_CHILD; - - case TMPL_TYPE_REGEX: - case TMPL_TYPE_REGEX_UNCOMPILED: - case TMPL_TYPE_REGEX_XLAT: - case TMPL_TYPE_REGEX_XLAT_UNRESOLVED: - case TMPL_TYPE_XLAT_UNRESOLVED: - fr_assert(0); - goto error; - } - FALL_THROUGH; - - case UNLANG_UPDATE_MAP_EXPANDED_RHS: - { - vp_list_mod_t *new_mod; - /* - * Concat the top level results together - */ - if (!fr_value_box_list_empty(&update_state->rhs_result) && - (fr_value_box_list_concat_in_place(update_state, - fr_value_box_list_head(&update_state->rhs_result), &update_state->rhs_result, FR_TYPE_STRING, - FR_VALUE_BOX_LIST_FREE, true, - SIZE_MAX) < 0)) { - RPEDEBUG("Failed concatenating RHS expansion results"); - goto error; - } - - if (map_to_list_mod(update_state, &new_mod, - request, map, - &update_state->lhs_result, &update_state->rhs_result) < 0) goto error; - if (new_mod) fr_dlist_insert_tail(&update_state->vlm_head, new_mod); - - fr_value_box_list_talloc_free(&update_state->rhs_result); - } - - next: - update_state->state = UNLANG_UPDATE_MAP_INIT; - fr_value_box_list_talloc_free(&update_state->lhs_result); - - break; - } - } - - return list_mod_apply(p_result, request); -} - - -/** Execute an update block - * - * Update blocks execute in two phases, first there's an evaluation phase where - * each input map is evaluated, outputting one or more modification maps. The modification - * maps detail a change that should be made to a list in the current request. - * The request is not modified during this phase. - * - * The second phase applies those modification maps to the current request. - * This re-enables the atomic functionality of update blocks provided in v2.x.x. - * If one map fails in the evaluation phase, no more maps are processed, and the current - * result is discarded. - */ -static unlang_action_t unlang_update_state_init(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame) -{ - unlang_group_t *g = unlang_generic_to_group(frame->instruction); - unlang_map_t *gext = unlang_group_to_map(g); - unlang_frame_state_update_t *update_state; - - /* - * Initialise the frame state - */ - MEM(frame->state = update_state = talloc_zero_pooled_object(request->stack, unlang_frame_state_update_t, - (sizeof(map_t) + - (sizeof(tmpl_t) * 2) + 128), /* 128 is for string buffers */ - unlang_list_num_elements(&g->children))); - - fr_dcursor_init(&update_state->maps, &gext->map.head); - fr_value_box_list_init(&update_state->lhs_result); - fr_value_box_list_init(&update_state->rhs_result); - fr_dlist_init(&update_state->vlm_head, vp_list_mod_t, entry); - - /* - * Call list_mod_create - */ - frame_repeat(frame, list_mod_create); - return list_mod_create(p_result, request, frame); -} - static unlang_action_t map_proc_resume(unlang_result_t *p_result, request_t *request, #ifdef WITH_VERIFY_PTR unlang_stack_frame_t *frame @@ -444,389 +210,6 @@ static unlang_action_t unlang_map_state_init(unlang_result_t *p_result, request_ return map_proc_apply(p_result, request, frame); } -static int edit_section_alloc(CONF_SECTION *parent, CONF_SECTION **child, char const *name1, fr_token_t op) -{ - CONF_SECTION *cs; - - cs = cf_section_alloc(parent, parent, name1, NULL); - if (!cs) return -1; - - cf_section_add_name2_quote(cs, op); - - if (child) *child = cs; - - return 0; -} - -static int edit_pair_alloc(CONF_SECTION *cs, CONF_PAIR *original, char const *attr, fr_token_t op, char const *value, fr_token_t list_op) -{ - CONF_PAIR *cp; - fr_token_t rhs_quote; - - if (original) { - rhs_quote = cf_pair_value_quote(original); - } else { - rhs_quote = T_BARE_WORD; - } - - cp = cf_pair_alloc(cs, attr, value, op, T_BARE_WORD, rhs_quote); - if (!cp) return -1; - - if (!original) return 0; - - cf_filename_set(cp, cf_filename(original)); - cf_lineno_set(cp, cf_lineno(original)); - - if (fr_debug_lvl >= 3) { - if (list_op == T_INVALID) { - cf_log_err(original, "%s %s %s --> %s %s %s", - cf_pair_attr(original), fr_tokens[cf_pair_operator(original)], cf_pair_value(original), - attr, fr_tokens[op], value); - } else { - if (*attr == '&') attr++; - cf_log_err(original, "%s %s %s --> %s %s { %s %s %s }", - cf_pair_attr(original), fr_tokens[cf_pair_operator(original)], cf_pair_value(original), - cf_section_name1(cs), fr_tokens[list_op], attr, fr_tokens[op], value); - } - } else if (fr_debug_lvl >= 2) { - if (list_op == T_INVALID) { - cf_log_err(original, "--> %s %s %s", - attr, fr_tokens[op], value); - } else { - cf_log_err(original, "--> %s %s { %s %s %s }", - cf_section_name1(cs), fr_tokens[list_op], attr, fr_tokens[op], value); - } - } - - return 0; -} - -/* - * Convert "update" to "edit" using evil spells and sorcery. - */ -static unlang_t *compile_update_to_edit(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_SECTION *cs) -{ - char const *name2 = cf_section_name2(cs); - CONF_ITEM *ci; - CONF_SECTION *group; - unlang_group_t *g; - char list_buffer[32]; - char value_buffer[256]; - char attr_buffer[256]; - char const *list; - - g = unlang_generic_to_group(parent); - - /* - * Wrap it all in a group, no matter what. Because of - * limitations in the cf_pair_alloc() API. - */ - group = cf_section_alloc(g->cs, g->cs, "group", NULL); - if (!group) return NULL; - - (void) cf_item_remove(g->cs, group); /* was added at the end */ - cf_item_insert_after(g->cs, cs, group); - - /* - * Hoist this out of the loop, and make sure it never has a '&' prefix. - */ - if (name2) { - if (*name2 == '&') name2++; - snprintf(list_buffer, sizeof(list_buffer), "%s", name2); - } else { - snprintf(list_buffer, sizeof(list_buffer), "%s", tmpl_list_name(unlang_ctx->rules->attr.list_def, "")); - - } - - /* - * Loop over the entries, rewriting them. - */ - for (ci = cf_item_next(cs, NULL); - ci != NULL; - ci = cf_item_next(cs, ci)) { - CONF_PAIR *cp; - CONF_SECTION *child; - int rcode; - fr_token_t op; - char const *attr, *value, *end; - - if (cf_item_is_section(ci)) { - cf_log_err(ci, "Cannot specify subsections for 'update'"); - return NULL; - } - - if (!cf_item_is_pair(ci)) continue; - - cp = cf_item_to_pair(ci); - - attr = cf_pair_attr(cp); - value = cf_pair_value(cp); - op = cf_pair_operator(cp); - - fr_assert(attr); - fr_assert(value); - - list = list_buffer; - - if (*attr == '&') attr++; - - end = strchr(attr, '.'); - if (!end) end = attr + strlen(attr); - - /* - * Separate out the various possibilities for the "name", which could be a list, an - * attribute name, or a list followed by an attribute name. - * - * Note that even if we have "update request { ....}", the v3 parser allowed the contents - * of the "update" section to still specify parent / lists. Which makes parsing it all - * annoying. - * - * The good news is that all we care about is whether or not there's a parent / list ref. - * We don't care what that ref is. - */ - { - fr_dict_attr_t const *tmpl_list; - - /* - * Allow for a "parent" or "outer" reference. There may be multiple - * "parent.parent", so we keep processing them until we get a list reference. - */ - if (fr_table_value_by_substr(tmpl_request_ref_table, attr, end - attr, REQUEST_UNKNOWN) != REQUEST_UNKNOWN) { - - /* - * Catch one more case where the behavior is different. - * - * &request += &config[*] - */ - if ((cf_pair_value_quote(cp) == T_BARE_WORD) && (*value == '&') && - (strchr(value, '.') == NULL) && (strchr(value, '[') != NULL)) { - char const *p = strchr(value, '['); - - cf_log_err(cp, "Cannot do array assignments for lists. Just use '%s %s %.*s'", - list, fr_tokens[op], (int) (p - value), value); - return NULL; - } - - goto attr_is_list; - - /* - * Doesn't have a parent ref, maybe it's a list ref? - */ - } else if (tmpl_attr_list_from_substr(&tmpl_list, &FR_SBUFF_IN(attr, (end - attr))) > 0) { - char *p; - - attr_is_list: - snprintf(attr_buffer, sizeof(attr_buffer), "%s", attr); - list = attr_buffer; - attr = NULL; - - p = strchr(attr_buffer, '.'); - if (p) { - *(p++) = '\0'; - attr = p; - } - } - } - - switch (op) { - /* - * FOO !* ANY - * - * The RHS doesn't matter, so we ignore it. - */ - case T_OP_CMP_FALSE: - if (!attr) { - /* - * Set list to empty value. - */ - rcode = edit_section_alloc(group, NULL, list, T_OP_SET); - - } else { - if (strchr(attr, '[') == NULL) { - snprintf(value_buffer, sizeof(value_buffer), "%s[*]", attr); - } else { - snprintf(value_buffer, sizeof(value_buffer), "%s", attr); - } - - rcode = edit_pair_alloc(group, cp, list, T_OP_SUB_EQ, value_buffer, T_INVALID); - } - break; - - case T_OP_SET: - /* - * Must be a list-to-list operation - */ - if (!attr) { - list_op: - rcode = edit_pair_alloc(group, cp, list, op, value, T_INVALID); - break; - } - goto pair_op; - - case T_OP_EQ: - /* - * Allow &list = "foo" - */ - if (!attr) { - if (!value) { - cf_log_err(cp, "Missing value"); - return NULL; - } - - rcode = edit_pair_alloc(group, cp, list, op, value, T_INVALID); - break; - } - - pair_op: - fr_assert(*attr != '&'); - if (snprintf(value_buffer, sizeof(value_buffer), "%s.%s", list, attr) < 0) { - cf_log_err(cp, "RHS of update too long to convert to edit automatically"); - return NULL; - } - - rcode = edit_pair_alloc(group, cp, value_buffer, op, value, T_INVALID); - break; - - case T_OP_ADD_EQ: - case T_OP_PREPEND: - if (!attr) goto list_op; - - rcode = edit_section_alloc(group, &child, list, op); - if (rcode < 0) break; - - rcode = edit_pair_alloc(child, cp, attr, T_OP_EQ, value, op); - break; - - /* - * Remove matching attributes - */ - case T_OP_SUB_EQ: - op = T_OP_CMP_EQ; - - filter: - if (!attr) { - cf_log_err(cp, "Invalid operator for list assignment"); - return NULL; - } - - rcode = edit_section_alloc(group, &child, list, T_OP_SUB_EQ); - if (rcode < 0) break; - - if (strchr(attr, '[') != 0) { - cf_log_err(cp, "Cannot do filtering with array indexes"); - return NULL; - } - - rcode = edit_pair_alloc(child, cp, attr, op, value, T_OP_SUB_EQ); - break; - - /* - * Keep matching attributes, i.e. remove non-matching ones. - */ - case T_OP_CMP_EQ: - op = T_OP_NE; - goto filter; - - case T_OP_NE: - op = T_OP_CMP_EQ; - goto filter; - - case T_OP_LT: - op = T_OP_GE; - goto filter; - - case T_OP_LE: - op = T_OP_GT; - goto filter; - - case T_OP_GT: - op = T_OP_LE; - goto filter; - - case T_OP_GE: - op = T_OP_LT; - goto filter; - - default: - cf_log_err(cp, "Unsupported operator - cannot auto-convert to edit section"); - return NULL; - } - - if (rcode < 0) { - cf_log_err(cp, "Failed converting entry"); - return NULL; - } - } - - return UNLANG_IGNORE; -} - -static unlang_t *unlang_compile_update(unlang_t *parent, unlang_compile_ctx_t *unlang_ctx, CONF_ITEM const *ci) -{ - CONF_SECTION *cs = cf_item_to_section(ci); - int rcode; - - unlang_group_t *g; - unlang_map_t *gext; - - unlang_t *c; - char const *name2 = cf_section_name2(cs); - - tmpl_rules_t t_rules; - - if (main_config_migrate_option_get("forbid_update")) { - cf_log_err(cs, "The use of 'update' sections is forbidden by the server configuration"); - return NULL; - } - - /* - * If we're migrating "update" sections to edit, then go - * do that now. - */ - if (main_config_migrate_option_get("rewrite_update")) { - return compile_update_to_edit(parent, unlang_ctx, cs); - } - - /* - * We allow unknown attributes here. - */ - t_rules = *(unlang_ctx->rules); - t_rules.attr.allow_unknown = true; - t_rules.attr.allow_wildcard = true; - RULES_VERIFY(&t_rules); - - g = unlang_group_allocate(parent, cs, UNLANG_TYPE_UPDATE); - if (!g) return NULL; - - gext = unlang_group_to_map(g); - - /* - * This looks at cs->name2 to determine which list to update - */ - map_list_init(&gext->map); - rcode = map_afrom_cs(gext, &gext->map, cs, &t_rules, &t_rules, unlang_fixup_update, NULL, 128); - if (rcode < 0) return NULL; /* message already printed */ - if (map_list_empty(&gext->map)) { - cf_log_err(cs, "'update' sections cannot be empty"); - error: - talloc_free(g); - return NULL; - } - - c = unlang_group_to_generic(g); - if (name2) { - c->name = name2; - c->debug_name = talloc_typed_asprintf(c, "update %s", name2); - } else { - c->name = "update"; - c->debug_name = c->name; - } - - if (!pass2_fixup_update(g, unlang_ctx->rules)) goto error; - - return c; -} - static int compile_map_name(unlang_group_t *g) { unlang_map_t *gext = unlang_group_to_map(g); @@ -1056,18 +439,6 @@ static unlang_t *unlang_compile_map(unlang_t *parent, unlang_compile_ctx_t *unla void unlang_map_init(void) { - unlang_register(&(unlang_op_t){ - .name = "update", - .type = UNLANG_TYPE_UPDATE, - .flag = UNLANG_OP_FLAG_DEBUG_BRACES, - - .compile = unlang_compile_update, - .interpret = unlang_update_state_init, - - .unlang_size = sizeof(unlang_map_t), - .unlang_name = "unlang_map_t", - }); - unlang_register(&(unlang_op_t){ .name = "map", .type = UNLANG_TYPE_MAP,