]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
allow tmpl_dcursor as function arguments
authorAlan T. DeKok <aland@freeradius.org>
Sun, 11 May 2025 23:27:35 +0000 (19:27 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Sun, 11 May 2025 23:27:35 +0000 (19:27 -0400)
with some limitations - only strings are allowed for now :(

src/lib/unlang/xlat.h
src/lib/unlang/xlat_builtin.c
src/lib/unlang/xlat_eval.c
src/lib/unlang/xlat_tokenize.c
src/tests/keywords/cbor

index 7123867b3ec5b6cd939a42c3dda2fb644c79fccf..4095795810421360956f0f54d66ec0ec5fb26ece 100644 (file)
@@ -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.
index 6c4741d368ce6a61fbea7b71a5e5220faefc4df5..4e00bf675c5cb64f9b3d3244707cfc7dc8f13c79 100644 (file)
@@ -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.
         */
index ae2e32c602019fa905065e51a385a53356f1e70a..c4cccbf706699f46a12cef7775b3b31d9ad14df5 100644 (file)
@@ -27,6 +27,7 @@
 RCSID("$Id$")
 
 #include <freeradius-devel/server/base.h>
+#include <freeradius-devel/server/tmpl_dcursor.h>
 #include <freeradius-devel/unlang/xlat_priv.h>
 
 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
index d6c6f6b525f724a06de3bf8316a4f323f0d63fd9..498f14dc5901b22de268f5bae3ff314c39a21ca3 100644 (file)
@@ -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",
index 2c30e3104488b5b4b58325097b8339703bec67b3..9aa14c47bb3f06a1b9ccb0300ae3eb8d32fa98ea 100644 (file)
@@ -1,9 +1,6 @@
 octets cbor
 string foo
 
-#
-#  @todo - this should be a dcursor
-#
 cbor = %cbor.encode('User-Name')
 
 #