From: Alan T. DeKok Date: Sun, 11 May 2025 23:27:35 +0000 (-0400) Subject: allow tmpl_dcursor as function arguments X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=19ff3a8910305be2013cf417e17398de29375d45;p=thirdparty%2Ffreeradius-server.git allow tmpl_dcursor as function arguments with some limitations - only strings are allowed for now :( --- diff --git a/src/lib/unlang/xlat.h b/src/lib/unlang/xlat.h index 7123867b3ec..40957958104 100644 --- a/src/lib/unlang/xlat.h +++ b/src/lib/unlang/xlat.h @@ -145,6 +145,8 @@ typedef struct { uint8_t required : 1; //!< Argument must be present, and non-empty. uint8_t concat : 1; //!< Concat boxes together. uint8_t single : 1; //!< Argument must only contain a single box + uint8_t cursor : 1; //!< Argument is an attribute reference, we pass a cursor to the function + uint8_t allow_wildcard : 1; //!< For parsing the cursor uint8_t will_escape : 1; //!< the function will do escaping and concatenation. uint8_t always_escape : 1; //!< Pass all arguments to escape function not just ///< tainted ones. diff --git a/src/lib/unlang/xlat_builtin.c b/src/lib/unlang/xlat_builtin.c index 6c4741d368c..4e00bf675c5 100644 --- a/src/lib/unlang/xlat_builtin.c +++ b/src/lib/unlang/xlat_builtin.c @@ -4046,7 +4046,7 @@ static int protocol_xlat_instantiate(xlat_inst_ctx_t const *mctx) } static xlat_arg_parser_t const protocol_encode_xlat_args[] = { - { .required = true, .single = true, .type = FR_TYPE_STRING }, + { .required = true, .single = true, .cursor = true, .allow_wildcard = true, .type = FR_TYPE_VOID, .safe_for = FR_VALUE_BOX_SAFE_FOR_ANY }, XLAT_ARG_PARSER_TERMINATOR }; @@ -4065,10 +4065,8 @@ static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *args) { - tmpl_t *vpt; fr_pair_t *vp; - fr_dcursor_t cursor; - tmpl_dcursor_ctx_t cc; + fr_dcursor_t *cursor; bool tainted = false; fr_value_box_t *encoded; @@ -4083,24 +4081,14 @@ static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, memcpy(&tp_encode, xctx->inst, sizeof(tp_encode)); /* const issues */ - if (tmpl_afrom_attr_str(ctx, NULL, &vpt, in_head->vb_strvalue, - &(tmpl_rules_t){ - .attr = { - .dict_def = request->proto_dict, /* we can't encode local attributes */ - .list_def = request_attr_request, - .allow_wildcard = true, - } - }) <= 0) { - RPEDEBUG("Failed parsing attribute reference"); - return XLAT_ACTION_FAIL; - } + fr_value_box_debug(in_head); + cursor = fr_value_box_get_void_type(in_head, fr_dcursor_t); /* * Create the encoding context. */ if (tp_encode->test_ctx) { - if (tp_encode->test_ctx(&encode_ctx, vpt, request->proto_dict) < 0) { - talloc_free(vpt); + if (tp_encode->test_ctx(&encode_ctx, cursor, request->proto_dict) < 0) { return XLAT_ACTION_FAIL; } } @@ -4108,9 +4096,9 @@ static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, /* * Loop over the attributes, encoding them. */ - for (vp = tmpl_dcursor_init(NULL, NULL, &cc, &cursor, request, vpt); + for (vp = fr_dcursor_current(cursor); vp != NULL; - vp = fr_dcursor_next(&cursor)) { + vp = fr_dcursor_next(cursor)) { if (vp->da->flags.internal) continue; /* @@ -4125,11 +4113,9 @@ static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, * their value is true. */ - len = tp_encode->func(&FR_DBUFF_TMP(p, end), &cursor, encode_ctx); + len = tp_encode->func(&FR_DBUFF_TMP(p, end), cursor, encode_ctx); if (len < 0) { RPEDEBUG("Protocol encoding failed"); - tmpl_dcursor_clear(&cc); - talloc_free(vpt); return XLAT_ACTION_FAIL; } @@ -4137,9 +4123,6 @@ static xlat_action_t protocol_encode_xlat(TALLOC_CTX *ctx, fr_dcursor_t *out, p += len; } - tmpl_dcursor_clear(&cc); - talloc_free(vpt); - /* * Pass the options string back to the caller. */ diff --git a/src/lib/unlang/xlat_eval.c b/src/lib/unlang/xlat_eval.c index ae2e32c6020..c4cccbf7066 100644 --- a/src/lib/unlang/xlat_eval.c +++ b/src/lib/unlang/xlat_eval.c @@ -27,6 +27,7 @@ RCSID("$Id$") #include +#include #include static int instance_count = 0; @@ -436,14 +437,7 @@ static xlat_action_t xlat_process_arg_list(TALLOC_CTX *ctx, fr_value_box_list_t } fr_assert(fr_value_box_list_num_elements(list) == 1); - /* - * Cast to the type we actually want. - */ - if ((arg->type != FR_TYPE_VOID) && (arg->type != type)) { - goto do_cast; - } - - return XLAT_ACTION_DONE; + goto check_types; } /* @@ -460,8 +454,13 @@ static xlat_action_t xlat_process_arg_list(TALLOC_CTX *ctx, fr_value_box_list_t return XLAT_ACTION_FAIL; } - if ((arg->type != FR_TYPE_VOID) && (vb->type != arg->type)) { - do_cast: + check_types: + if (arg->type == FR_TYPE_VOID) goto do_void; + + /* + * Cast to the correct type if necessary. + */ + if (vb->type != arg->type) { if (fr_value_box_cast_in_place(ctx, vb, arg->type, NULL) < 0) { cast_error: RPEDEBUG("Function \"%s\" failed to cast argument %u to type %s", name, arg_num, fr_type_to_str(arg->type)); @@ -482,6 +481,51 @@ static xlat_action_t xlat_process_arg_list(TALLOC_CTX *ctx, fr_value_box_list_t if (fr_value_box_cast_in_place(ctx, vb, arg->type, NULL) < 0) goto cast_error; } while ((vb = fr_value_box_list_next(list, vb))); + + } else { + tmpl_t *vpt; + tmpl_dcursor_ctx_t *cc; + fr_dcursor_t *cursor; + + do_void: + if (!arg->cursor) return XLAT_ACTION_DONE; + + /* + * Cursor names have to be strings, which are completely safe. + */ + if (vb->type != FR_TYPE_STRING) { + REDEBUG("Expected attribute reference as string, not %s", fr_type_to_str(vb->type)); + return XLAT_ACTION_FAIL; + } + +#if 1 + if (!fr_value_box_is_safe_for(vb, FR_VALUE_BOX_SAFE_FOR_ANY)) { + fr_value_box_debug(vb); + REDEBUG("Refusing to reference attribute from unsafe data"); + return XLAT_ACTION_FAIL; + } +#endif + + if (tmpl_afrom_attr_str(ctx, NULL, &vpt, vb->vb_strvalue, + &(tmpl_rules_t){ + .attr = { + .dict_def = request->proto_dict, /* we can't encode local attributes */ + .list_def = request_attr_request, + .allow_wildcard = arg->allow_wildcard, + } + }) <= 0) { + RPEDEBUG("Failed parsing attribute reference"); + return XLAT_ACTION_FAIL; + } + + MEM(cc = talloc(vb, tmpl_dcursor_ctx_t)); + MEM(cursor = talloc(vb, fr_dcursor_t)); + + tmpl_dcursor_init(NULL, NULL, cc, cursor, request, vpt); + fr_value_box_clear_value(vb); + fr_value_box_set_void_type(vb, cursor, fr_dcursor_t); /* asserts that the ptr has the specified type */ + + talloc_set_destructor(cc, _tmpl_dcursor_clear); } #undef ESCAPE diff --git a/src/lib/unlang/xlat_tokenize.c b/src/lib/unlang/xlat_tokenize.c index d6c6f6b525f..498f14dc590 100644 --- a/src/lib/unlang/xlat_tokenize.c +++ b/src/lib/unlang/xlat_tokenize.c @@ -213,7 +213,9 @@ static int xlat_tmpl_normalize(xlat_exp_t *node) return 0; } - +/** Validate and sanity check function arguments. + * + */ static int xlat_validate_function_arg(xlat_arg_parser_t const *arg_p, xlat_exp_t *arg, int argc) { xlat_exp_t *node; @@ -229,21 +231,65 @@ static int xlat_validate_function_arg(xlat_arg_parser_t const *arg_p, xlat_exp_t */ arg->group->is_argv = (arg_p->func != NULL) | arg_p->will_escape; + node = xlat_exp_head(arg->group); + + if (!node) { + if (!arg_p->required) return 0; + + fr_strerror_const("Missing argument"); + return -1; + } + /* * The caller doesn't care about the type, we don't do any validation. - * - * @todo - maybe check single / required? */ if (arg_p->type == FR_TYPE_VOID) { - return 0; - } + if (!arg_p->cursor) return 0; - node = xlat_exp_head(arg->group); + if (node->type == XLAT_BOX) { + check_box: + if (node->data.type != FR_TYPE_STRING) { + fr_strerror_printf("Cursor must be a string attribute reference, not %s", + fr_type_to_str(node->data.type)); + return -1; + } - if (!node) { - if (!arg_p->required) return 0; + return 0; + } - fr_strerror_const("Missing argument"); + /* + * The expression parser should not allow anything else here. + */ + fr_assert((node->type == XLAT_TMPL) || (node->type == XLAT_GROUP)); + + /* + * Func, etc. + */ + if (node->type != XLAT_TMPL) return 0; + + if (tmpl_rules_cast(node->vpt) != FR_TYPE_NULL) { + fr_strerror_const("Cursor cannot have cast"); + return -1; + } + + if (xlat_tmpl_normalize(node) < 0) return -1; + + if (node->type == XLAT_BOX) goto check_box; + + if (!tmpl_is_attr(node->vpt)) { + fr_strerror_printf("Invalid argument - expected attribute reference"); + return -1; + } + + /* + * @todo - in xlat_frame_eval(), push the vpt pointer? We don't want the _values_ of the + * referenced attributes, we want a cursor which walks over the referenced attributes. + * + * Except that the xlat_frame_eval_repeat() doesn't have access to the arg list, so it + * can't check that we want a tmpl / cursor. Maybe hack that by restricting the cursor + * argument to the first one, or the first + variadic of the same type. + */ + fr_strerror_const("Please use strings for references - unquoted strings are not yet supported"); return -1; } @@ -1691,7 +1737,7 @@ fr_slen_t xlat_tokenize_argv(TALLOC_CTX *ctx, xlat_exp_head_t **out, fr_sbuff_t node->flags = node->group->flags; /* - * Validate the argument immediately on parsing it, and not later. + * Check number of arguments. */ if (arg->type == FR_TYPE_NULL) { fr_strerror_printf("Too many arguments, expected %zu, got %d", diff --git a/src/tests/keywords/cbor b/src/tests/keywords/cbor index 2c30e310448..9aa14c47bb3 100644 --- a/src/tests/keywords/cbor +++ b/src/tests/keywords/cbor @@ -1,9 +1,6 @@ octets cbor string foo -# -# @todo - this should be a dcursor -# cbor = %cbor.encode('User-Name') #