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.
}
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
};
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;
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;
}
}
/*
* 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;
/*
* 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;
}
p += len;
}
- tmpl_dcursor_clear(&cc);
- talloc_free(vpt);
-
/*
* Pass the options string back to the caller.
*/
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;
}
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;
}
/*
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));
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
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;
*/
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;
}
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",
octets cbor
string foo
-#
-# @todo - this should be a dcursor
-#
cbor = %cbor.encode('User-Name')
#