26 RCSID(
"$Id: dccefced32820311bb0f271ff7f1812e2b8d01dc $")
28 #include <freeradius-devel/server/base.h>
29 #include <freeradius-devel/server/tmpl_dcursor.h>
30 #include <freeradius-devel/util/debug.h>
31 #include <freeradius-devel/util/edit.h>
32 #include <freeradius-devel/util/calc.h>
33 #include <freeradius-devel/unlang/tmpl.h>
34 #include <freeradius-devel/unlang/edit.h>
35 #include <freeradius-devel/unlang/transaction.h>
36 #include <freeradius-devel/unlang/unlang_priv.h>
46 #define RDEBUG_ASSIGN(_name, _op, _box) do { \
47 RDEBUG2(((_box)->type == FR_TYPE_STRING) ? "%s %s \"%pV\"" : "%s %s %pV", _name, fr_tokens[_op], _box); \
100 #define MAP_INFO cf_filename(map->ci), cf_lineno(map->ci)
113 RWDEBUG(
"%s %s ... - Assignment failed - No value on right-hand side", map->
lhs->name,
fr_tokens[map->
op]);
121 RWDEBUG(
"Failed converting result to string");
132 .dict_def = request->dict,
133 .list_def = request_attr_request,
134 .prefix = TMPL_ATTR_REF_PREFIX_NO
138 RPEDEBUG(
"Expansion result \"%s\" is not an attribute reference", box->vb_strvalue);
143 fr_value_box_list_talloc_free(&
out->result);
193 switch (
vp->vp_type) {
207 switch (
vp->vp_type) {
226 map_t const *child = NULL;
228 if (map) child = map_list_head(&map->
child);
234 if (map) child = map_list_next(&map->
child, child);
278 XDEBUG(
"apply_edits_to_list %s", map->
lhs->name);
284 children = &
current->rhs.pair_list;
300 REDEBUG(
"Invalid data type for assignment to list");
305 box = fr_value_box_list_head(&
current->rhs.result);
311 RWDEBUG(
"%s %s ... - Assignment failed to having no value on right-hand side", map->
lhs->name,
fr_tokens[map->
op]);
319 RWDEBUG(
"Failed converting result to string");
324 children = &
current->rhs.pair_list;
338 .allow_compare =
true,
339 .tainted = box->tainted,
344 RPEDEBUG(
"Failed parsing string '%pV' as attribute list", box);
388 RWDEBUG(
"Not removing immutable %pP",
vp);
393 RWDEBUG(
"Attribute cannot be removed, as it is being used in a 'foreach' loop - %pP",
vp);
414 REDEBUG(
"%s[%d] Wildcard for structural attribute %s is not yet implemented.",
MAP_INFO,
current->rhs.vpt->name);
418 children = &
vp->vp_group;
441 children = &
current->rhs.pair_list;
458 #ifdef STATIC_ANALYZER
459 if (!
current->lhs.vp)
return -1;
477 RDEBUG(
"Cannot perform assignment: Attribute \"%s\" is not a child of parent \"%s\"",
478 child->da->name,
current->lhs.vp->da->name);
488 (children != &
current->rhs.pair_list));
499 if (children == &
current->rhs.pair_list) {
520 RWDEBUG(
"Attribute cannot be removed, as it is being used in a 'foreach' loop - %s",
vp->
da->name);
625 bool single =
false, pair =
false;
628 XDEBUG(
"apply_edits_to_leaf %s", map->
lhs->name);
631 REDEBUG(
"%s[%d] The left side of an assignment must be an attribute reference",
MAP_INFO);
664 box = fr_value_box_list_head(&
current->rhs.result);
668 fr_value_box_list_insert_tail(&
current->rhs.result, box);
672 RWDEBUG(
"Failed converting result to string");
675 box = fr_value_box_list_head(&
current->rhs.result);
680 if (fr_value_box_list_num_elements(&
current->rhs.result) == 1) {
681 box = fr_value_box_list_head(&
current->rhs.result);
718 RWDEBUG(
"%s %s ... - Assignment failed - No value on right-hand side", map->
lhs->name,
fr_tokens[map->
op]);
734 if (
current->temporary_pair_list) {
738 if (!
current->parent->lhs.vp) {
797 if (single)
goto done;
809 if (
current->lhs.vp->da->flags.local) {
839 #ifdef STATIC_ANALYZER
840 if (!
current->lhs.vp)
return -1;
859 RPEDEBUG(
"Assigning value to %s failed", map->
lhs->name);
871 fr_value_box_list_talloc_free(&
current->rhs.result);
901 if (!
vp)
return NULL;
928 #define DECLARE(_x) static int _x(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
942 TALLOC_FREE(
current->lhs.to_free);
943 TALLOC_FREE(
current->rhs.to_free);
1012 if (rcode < 0)
return -1;
1014 if (!rcode)
continue;
1088 child->
ctx =
current->lhs.vp ? (TALLOC_CTX *)
current->lhs.vp : (TALLOC_CTX *) child;
1094 memset(&child->
lhs, 0,
sizeof(child->
lhs));
1095 memset(&child->
rhs, 0,
sizeof(child->
rhs));
1098 fr_value_box_list_init(&child->
lhs.
result);
1099 fr_value_box_list_init(&child->
rhs.
result);
1129 if (rcode < 0)
return -1;
1155 XDEBUG(
"%s map %s", __FUNCTION__, map->
lhs->name);
1164 fr_value_box_list_insert_tail(&
current->parent->rhs.result, box);
1188 fr_value_box_list_insert_tail(&
current->parent->rhs.result, box);
1221 RWDEBUG(
"Failed expanding result");
1231 if (!fr_value_box_list_next(&
current->lhs.result, box))
goto done;
1262 fr_value_box_list_move(&
current->parent->rhs.result, &
current->lhs.result);
1273 if (
fr_value_box_from_str(dst, dst, da->type, da, box->vb_strvalue, box->vb_length, erules, box->tainted) < 0) {
1278 fr_value_box_list_talloc_free(&
current->lhs.result);
1279 fr_value_box_list_insert_tail(&
current->parent->rhs.result, dst);
1296 XDEBUG(
"%s map %s", __FUNCTION__, map->
lhs->name);
1377 (map_list_num_elements(&map->
child) > 0)) {
1378 RWDEBUG(
"Cannot set one entry to multiple values for %s",
current->lhs.vpt->name);
1426 }
else if (
current->lhs.create) {
1440 if (rcode < 0)
return -1;
1474 RWDEBUG(
"Cannot modify immutable value for %s",
current->lhs.vpt->name);
1523 if (rcode < 0)
return -1;
1573 TALLOC_FREE(frame->
state);
1612 fr_value_box_list_init(&
current->lhs.result);
1613 fr_value_box_list_init(&
current->rhs.result);
1624 state->
ours =
false;
1690 static unlang_t edit_instruction = {
1693 .debug_name =
"edit",
1712 .
self = edit_instruction,
1714 map_list_init(&edit->
maps);
1738 .frame_state_type =
"unlang_frame_state_edit_t",
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
static void * fr_dcursor_next_peek(fr_dcursor_t *cursor)
Return the next iterator item without advancing the cursor.
#define fr_dcursor_init(_cursor, _head)
Initialise a cursor.
static void * fr_dcursor_set_current(fr_dcursor_t *cursor, void *item)
Set the cursor to a specified item.
fr_dcursor_eval_t void const * uctx
fr_dcursor_iter_t void * current
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
bool fr_dict_attr_can_contain(fr_dict_attr_t const *parent, fr_dict_attr_t const *child)
See if a structural da is allowed to contain another da.
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
static bool fr_dlist_empty(fr_dlist_head_t const *list_head)
Check whether a list has any items.
static unlang_t * unlang_edit_to_generic(unlang_edit_t const *p)
map_list_t maps
Head of the map list.
static unlang_edit_t * unlang_generic_to_edit(unlang_t const *p)
Cast a generic structure to the edit extension.
int unlang_interpret_push(request_t *request, unlang_t const *instruction, rlm_rcode_t default_rcode, bool do_next_sibling, bool top_frame)
Push a new frame onto the stack.
#define REXDENT()
Exdent (unindent) R* messages by one level.
#define RINDENT_SAVE(_x, _request)
Save indentation for later restoral.
#define RINDENT_RESTORE(_request, _x)
#define RPEDEBUG(fmt,...)
#define RINDENT()
Indent R* messages by one level.
void unlang_register(int type, unlang_op_t *op)
Register an operation with the interpreter.
@ L_DBG_LVL_2
2nd highest priority debug messages (-xx | -X).
static char * stack[MAX_STACK]
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_OCTETS
Raw octets.
@ FR_TYPE_GROUP
A grouping of other attributes.
fr_pair_t * fr_pair_parent(fr_pair_t const *vp)
Return a pointer to the parent pair.
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
Duplicate a list of pairs.
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
void fr_pair_list_init(fr_pair_list_t *list)
Initialise a pair list header.
bool fr_pair_immutable(fr_pair_t const *vp)
fr_pair_t * fr_pair_copy(TALLOC_CTX *ctx, fr_pair_t const *vp)
Copy a single valuepair.
fr_pair_list_t * fr_pair_parent_list(fr_pair_t const *vp)
Return a pointer to the parent pair list.
fr_value_box_t * fr_pair_dcursor_nested_init(fr_dcursor_t *cursor, fr_dcursor_t *parent)
Initialises a special dcursor over another cursor which returns fr_pair_t, but we return fr_value_box...
fr_slen_t fr_pair_list_afrom_substr(fr_pair_parse_t const *root, fr_pair_parse_t *relative, fr_sbuff_t *in)
Parse a fr_pair_list_t from a substring.
struct fr_pair_parse_s fr_pair_parse_t
rlm_rcode_t
Return codes indicating the result of the module call.
@ RLM_MODULE_INVALID
The module considers the request invalid.
@ RLM_MODULE_OK
The module is OK, continue.
@ RLM_MODULE_FAIL
Module failed, don't reply.
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
@ RLM_MODULE_REJECT
Immediately reject the request.
@ RLM_MODULE_NOTFOUND
User not found.
@ RLM_MODULE_UPDATED
OK (pairs modified).
@ RLM_MODULE_NOT_SET
Error resolving rcode (should not be returned by modules).
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
fr_dict_attr_t const * request_attr_request
fr_dict_attr_t const * request_attr_control
fr_dict_attr_t const * request_attr_state
fr_dict_attr_t const * request_attr_reply
#define FR_SBUFF_IN(_start, _len_or_end)
Set of parsing rules for *unescape_until functions.
static int16_t tmpl_attr_tail_num(tmpl_t const *vpt)
Return the last attribute reference's attribute number.
#define tmpl_is_xlat(vpt)
#define tmpl_value(_tmpl)
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
#define tmpl_is_attr(vpt)
#define tmpl_is_exec(vpt)
ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, char const *name, tmpl_rules_t const *rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
@ TMPL_TYPE_ATTR
Reference to one or more attributes.
@ TMPL_TYPE_XLAT
Pre-parsed xlat expansion.
@ TMPL_TYPE_EXEC
Callout to an external script or program.
@ TMPL_TYPE_DATA
Value in native boxed format.
#define tmpl_is_data(vpt)
void tmpl_debug(tmpl_t const *vpt)
int pair_append_by_tmpl_parent(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, tmpl_t const *vpt, bool skip_list))
Allocate and insert a leaf vp from a tmpl_t, building the parent vps if needed.
Optional arguments passed to vp_tmpl functions.
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_aka_sim_id_type_t type
fr_token_t op
The operator that controls insertion of the dst attribute.
tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
map_list_t child
parent map, for nested ones
tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Stores an attribute, a value and various bits of other data.
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
int unlang_tmpl_push(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, tmpl_t const *tmpl, unlang_tmpl_args_t *args)
Push a tmpl onto the stack for evaluation.
void tmpl_dcursor_clear(tmpl_dcursor_ctx_t *cc)
Clear any temporary state allocations.
#define tmpl_dcursor_build_init(_err, _ctx, _cc, _cursor, _request, _vpt, _build, _uctx)
#define tmpl_dcursor_init(_err, _ctx, _cc, _cursor, _request, _vpt)
Maintains state between cursor calls.
char const * fr_tokens[T_TOKEN_LAST]
const bool fr_comparison_op[T_TOKEN_LAST]
fr_edit_list_t * unlang_interpret_edit_list(request_t *request)
static fr_event_list_t * el
fr_pair_t * vp_parent
parent of the current VP
int(* unlang_edit_expand_t)(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
static int check_rhs(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
static unlang_action_t unlang_edit_state_init(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Execute an update block.
tmpl_t const * vpt
expanded tmpl
struct unlang_frame_state_edit_s unlang_frame_state_edit_t
static int expand_rhs_list(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
static void edit_state_init_internal(request_t *request, unlang_frame_state_edit_t *state, fr_edit_list_t *el, map_list_t const *map_list)
static void edit_debug_attr_vp(request_t *request, fr_pair_t *vp, map_t const *map)
static unlang_action_t process_edit(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Apply a map (recursively) to a request.
map_list_t const * map_list
fr_pair_list_t pair_list
for structural attributes
void unlang_edit_init(void)
static int expand_lhs(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
unlang_edit_expand_t func
for process state
static int next_map(UNUSED request_t *request, UNUSED unlang_frame_state_edit_t *state, edit_map_t *current)
tmpl_t * to_free
tmpl to free.
static int check_lhs(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
int unlang_edit_push(request_t *request, bool *success, fr_edit_list_t *el, map_list_t const *map_list)
Push a map onto the stack for edit evaluation.
#define XDEBUG(...)
fr_pair_t editing
static int check_lhs_value(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
#define RDEBUG_ASSIGN(_name, _op, _box)
static int check_lhs_nested(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
static int expand_rhs(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
static int expanded_lhs_value(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
static int apply_edits_to_list(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
static int edit_delete_lhs(request_t *request, edit_map_t *current, bool delete)
map_t const * map
the map to evaluate
static fr_pair_t * edit_list_pair_build(fr_pair_t *parent, fr_dcursor_t *cursor, fr_dict_attr_t const *da, void *uctx)
Simple pair building callback for use with tmpl_dcursors.
fr_edit_list_t * el
edit list
fr_edit_list_t * el
edit list
static bool pair_is_editable(request_t *request, fr_pair_t *vp)
static int tmpl_attr_from_result(TALLOC_CTX *ctx, map_t const *map, edit_result_t *out, request_t *request)
static void edit_debug_attr_list(request_t *request, fr_pair_list_t const *list, map_t const *map)
bool create
whether we need to create the VP
fr_value_box_list_t result
result of expansion
unlang_edit_expand_t check_lhs
for special cases
static int tmpl_to_values(TALLOC_CTX *ctx, edit_result_t *out, request_t *request, tmpl_t const *vpt)
static int edit_create_lhs_vp(request_t *request, TALLOC_CTX *ctx, edit_map_t *current)
static int expanded_lhs_attribute(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
bool * success
whether or not the edit succeeded
unlang_edit_expand_t expanded_lhs
for special cases
edit_map_t * current
what we're currently doing.
static int apply_edits_to_leaf(request_t *request, unlang_frame_state_edit_t *state, edit_map_t *current)
edit_result_t rhs
RHS child entries.
edit_result_t lhs
LHS child entries.
fr_pair_t * vp
VP referenced by tmpl.
int unlang_xlat_push(TALLOC_CTX *ctx, bool *p_success, fr_value_box_list_t *out, request_t *request, xlat_exp_head_t const *xlat, bool top_frame)
Push a pre-compiled xlat onto the stack for evaluation.
static void repeatable_clear(unlang_stack_frame_t *frame)
void * state
Stack frame specialisations.
@ UNLANG_TYPE_EDIT
edit VPs in place. After 20 years!
static void frame_repeat(unlang_stack_frame_t *frame, unlang_process_t process)
Mark the current stack frame up for repeat, and set a new process function.
unlang_t const * instruction
The unlang node we're evaluating.
static void repeatable_set(unlang_stack_frame_t *frame)
unlang_type_t type
The specialisation of this node.
A node in a graph of unlang_op_t (s) that we execute.
Our interpreter stack, as distinct from the C stack.
An unlang stack associated with a request.
int fr_edit_list_apply_list_assignment(fr_edit_list_t *el, fr_pair_t *dst, fr_token_t op, fr_pair_list_t *src, bool copy)
Apply operators to lists.
int fr_edit_list_pair_delete(fr_edit_list_t *el, fr_pair_list_t *list, fr_pair_t *vp)
Delete a VP.
void fr_edit_list_abort(fr_edit_list_t *el)
Abort the entries in an edit list.
int fr_edit_list_free_pair_children(fr_edit_list_t *el, fr_pair_t *vp)
Free children of a structural pair.
int fr_edit_list_apply_pair_assignment(fr_edit_list_t *el, fr_pair_t *vp, fr_token_t op, fr_value_box_t const *in)
Apply operators to pairs.
fr_edit_list_t * fr_edit_list_alloc(TALLOC_CTX *ctx, int hint, fr_edit_list_t *parent)
Allocate an edit list.
#define fr_edit_list_insert_pair_tail(_el, _list, _vp)
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
#define fr_pair_list_foreach(_list_head, _iter)
Iterate over the contents of a fr_pair_list_t.
void fr_pair_list_free(fr_pair_list_t *list)
Free memory used by a valuepair list.
void fr_pair_list_append(fr_pair_list_t *dst, fr_pair_list_t *src)
Appends a list of fr_pair_t from a temporary list to a destination list.
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
char const * fr_strerror(void)
Get the last library error.
static char const * fr_type_to_str(fr_type_t type)
Return a static string containing the type name.
#define fr_type_is_group(_x)
#define fr_type_is_variable_size(_x)
#define fr_type_is_structural(_x)
#define fr_type_is_fixed_size(_x)
#define FR_TYPE_STRUCTURAL
#define fr_type_is_null(_x)
#define fr_type_is_leaf(_x)
ssize_t fr_value_box_from_str(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, char const *in, size_t inlen, fr_sbuff_unescape_rules_t const *erules, bool tainted)
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
int fr_value_box_cmp_op(fr_token_t op, fr_value_box_t const *a, fr_value_box_t const *b)
Compare two attributes using an operator.
fr_sbuff_unescape_rules_t * fr_value_unescape_by_quote[T_TOKEN_LAST]
int fr_value_box_list_concat_in_place(TALLOC_CTX *ctx, fr_value_box_t *out, fr_value_box_list_t *list, fr_type_t type, fr_value_box_list_action_t proc_action, bool flatten, size_t max_size)
Concatenate a list of value boxes.
@ FR_VALUE_BOX_LIST_FREE_BOX
Free each processed box.
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
#define fr_value_box_alloc_null(_ctx)
Allocate a value box for later use with a value assignment function.
static size_t char ** out