2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * @brief map / template functions
25 * @copyright 2013 The FreeRADIUS server project
26 * @copyright 2013 Alan DeKok <aland@freeradius.org>
31 #include <freeradius-devel/radiusd.h>
32 #include <freeradius-devel/rad_assert.h>
37 static void map_dump(REQUEST *request, vp_map_t const *map)
39 RDEBUG(">>> MAP TYPES LHS: %s, RHS: %s",
40 fr_int2str(tmpl_names, map->lhs->type, "???"),
41 fr_int2str(tmpl_names, map->rhs->type, "???"));
44 RDEBUG(">>> MAP NAMES %s %s", map->lhs->name, map->rhs->name);
50 /** re-parse a map where the lhs is an unknown attribute.
53 * @param map to process.
54 * @param rhs_type quotation type around rhs.
55 * @param rhs string to re-parse.
57 bool map_cast_from_hex(vp_map_t *map, FR_TOKEN rhs_type, char const *rhs)
69 rad_assert(map != NULL);
71 rad_assert(map->lhs != NULL);
72 rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
74 rad_assert(map->rhs == NULL);
75 rad_assert(rhs != NULL);
80 * If the attribute is still unknown, go parse the RHS.
82 da = dict_attrbyvalue(map->lhs->tmpl_da->attr, map->lhs->tmpl_da->vendor);
83 if (!da || da->flags.is_unknown) return false;
86 * If the RHS is something OTHER than an octet
87 * string, go parse it as that.
89 if (rhs_type != T_BARE_WORD) return false;
90 if ((rhs[0] != '0') || (tolower((int)rhs[1]) != 'x')) return false;
91 if (!rhs[2]) return false;
93 len = strlen(rhs + 2);
95 ptr = talloc_array(map, uint8_t, len >> 1);
96 if (!ptr) return false;
98 len = fr_hex2bin(ptr, len >> 1, rhs + 2, len);
101 * If we can't parse it, or if it's malformed,
102 * it's still unknown.
104 rlen = data2vp(NULL, NULL, NULL, NULL, da, ptr, len, len, &vp);
107 if (rlen < 0) return false;
109 if ((size_t) rlen < len) {
111 fr_pair_list_free(&vp);
116 * Was still parsed as an unknown attribute.
118 if (vp->da->flags.is_unknown) goto free_vp;
121 * Set the RHS to the PARSED name, not the crap octet
122 * string which was input.
124 map->rhs = tmpl_alloc(map, TMPL_TYPE_DATA, NULL, 0);
125 if (!map->rhs) goto free_vp;
127 map->rhs->tmpl_data_type = da->type;
128 map->rhs->tmpl_data_length = vp->vp_length;
129 if (vp->da->flags.is_pointer) {
130 if (vp->da->type == PW_TYPE_STRING) {
131 map->rhs->tmpl_data_value.ptr = talloc_bstrndup(map->rhs, vp->data.ptr, vp->vp_length);
133 map->rhs->tmpl_data_value.ptr = talloc_memdup(map->rhs, vp->data.ptr, vp->vp_length);
136 memcpy(&map->rhs->tmpl_data_value, &vp->data, sizeof(map->rhs->tmpl_data_value));
138 map->rhs->name = vp_aprints_value(map->rhs, vp, '"');
139 map->rhs->len = talloc_array_length(map->rhs->name) - 1;
142 * Set the LHS to the REAL attribute name.
144 vpt = tmpl_alloc(map, TMPL_TYPE_ATTR, map->lhs->tmpl_da->name, -1);
145 memcpy(&vpt->data.attribute, &map->lhs->data.attribute, sizeof(vpt->data.attribute));
149 * Be sure to keep the "&control:" or "control:" prefix.
150 * If it's there, we re-generate it from whatever was in
151 * the original name, including the '&'.
155 len = radius_list_name(&list, p, PAIR_LIST_UNKNOWN);
157 if (list != PAIR_LIST_UNKNOWN) {
158 rad_const_free(vpt->name);
160 vpt->name = talloc_asprintf(vpt, "%.*s:%s",
161 (int) len, map->lhs->name,
162 map->lhs->tmpl_da->name);
163 vpt->len = strlen(vpt->name);
166 talloc_free(map->lhs);
169 fr_pair_list_free(&vp);
176 /** Convert CONFIG_PAIR (which may contain refs) to vp_map_t.
178 * Treats the left operand as an attribute reference
179 * @verbatim<request>.<list>.<attribute>@endverbatim
181 * Treatment of left operand depends on quotation, barewords are treated as
182 * attribute references, double quoted values are treated as expandable strings,
183 * single quoted values are treated as literal strings.
185 * Return must be freed with talloc_free
187 * @param[in] ctx for talloc.
188 * @param[in] out Where to write the pointer to the new value_pair_map_struct.
189 * @param[in] cp to convert to map.
190 * @param[in] dst_request_def The default request to insert unqualified
192 * @param[in] dst_list_def The default list to insert unqualified attributes
194 * @param[in] src_request_def The default request to resolve attribute
196 * @param[in] src_list_def The default list to resolve unqualified attributes
198 * @return vp_map_t if successful or NULL on error.
200 int map_afrom_cp(TALLOC_CTX *ctx, vp_map_t **out, CONF_PAIR *cp,
201 request_refs_t dst_request_def, pair_lists_t dst_list_def,
202 request_refs_t src_request_def, pair_lists_t src_list_def)
205 char const *attr, *value;
213 map = talloc_zero(ctx, vp_map_t);
214 map->op = cf_pair_operator(cp);
215 map->ci = cf_pair_to_item(cp);
217 attr = cf_pair_attr(cp);
218 value = cf_pair_value(cp);
220 cf_log_err_cp(cp, "Missing attribute value");
225 * LHS may be an expansion (that expands to an attribute reference)
226 * or an attribute reference. Quoting determines which it is.
228 type = cf_pair_attr_type(cp);
230 case T_DOUBLE_QUOTED_STRING:
231 case T_BACK_QUOTED_STRING:
232 slen = tmpl_afrom_str(ctx, &map->lhs, attr, talloc_array_length(attr) - 1,
233 type, dst_request_def, dst_list_def, true);
238 fr_canonicalize_error(ctx, &spaces, &text, slen, attr);
239 cf_log_err_cp(cp, "%s", text);
240 cf_log_err_cp(cp, "%s^ %s", spaces, fr_strerror());
249 slen = tmpl_afrom_attr_str(ctx, &map->lhs, attr, dst_request_def, dst_list_def, true, true);
251 cf_log_err_cp(cp, "Failed parsing attribute reference");
256 if (tmpl_define_unknown_attr(map->lhs) < 0) {
257 cf_log_err_cp(cp, "Failed creating attribute %s: %s",
258 map->lhs->name, fr_strerror());
266 * RHS might be an attribute reference.
268 type = cf_pair_value_type(cp);
270 if ((map->lhs->type == TMPL_TYPE_ATTR) &&
271 map->lhs->tmpl_da->flags.is_unknown &&
272 !map_cast_from_hex(map, type, value)) {
276 slen = tmpl_afrom_str(map, &map->rhs, value, strlen(value), type, src_request_def, src_list_def, true);
277 if (slen < 0) goto marker;
278 if (tmpl_define_unknown_attr(map->rhs) < 0) {
279 cf_log_err_cp(cp, "Failed creating attribute %s: %s", map->rhs->name, fr_strerror());
284 cf_log_err_cp(cp, "%s", fr_strerror());
289 * We cannot assign a count to an attribute. That must
290 * be done in an xlat.
292 if ((map->rhs->type == TMPL_TYPE_ATTR) &&
293 (map->rhs->tmpl_num == NUM_COUNT)) {
294 cf_log_err_cp(cp, "Cannot assign from a count");
309 /** Convert an 'update' config section into an attribute map.
311 * Uses 'name2' of section to set default request and lists.
313 * @param[in] cs the update section
314 * @param[out] out Where to store the head of the map.
315 * @param[in] dst_list_def The default destination list, usually dictated by
316 * the section the module is being called in.
317 * @param[in] src_list_def The default source list, usually dictated by the
318 * section the module is being called in.
319 * @param[in] validate map using this callback (may be NULL).
320 * @param[in] ctx to pass to callback.
321 * @param[in] max number of mappings to process.
322 * @return -1 on error, else 0.
324 int map_afrom_cs(vp_map_t **out, CONF_SECTION *cs,
325 pair_lists_t dst_list_def, pair_lists_t src_list_def,
326 map_validate_t validate, void *ctx,
329 char const *cs_list, *p;
331 request_refs_t request_def = REQUEST_CURRENT;
336 unsigned int total = 0;
337 vp_map_t **tail, *map;
344 * The first map has cs as the parent.
345 * The rest have the previous map as the parent.
349 ci = cf_section_to_item(cs);
351 cs_list = p = cf_section_name2(cs);
353 p += radius_request_name(&request_def, p, REQUEST_CURRENT);
354 if (request_def == REQUEST_UNKNOWN) {
355 cf_log_err(ci, "Default request specified in mapping section is invalid");
359 dst_list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN);
360 if (dst_list_def == PAIR_LIST_UNKNOWN) {
361 cf_log_err(ci, "Default list \"%s\" specified "
362 "in mapping section is invalid", p);
367 for (ci = cf_item_find_next(cs, NULL);
369 ci = cf_item_find_next(cs, ci)) {
370 if (total++ == max) {
371 cf_log_err(ci, "Map size exceeded");
377 if (!cf_item_is_pair(ci)) {
378 cf_log_err(ci, "Entry is not in \"attribute = value\" format");
382 cp = cf_item_to_pair(ci);
383 if (map_afrom_cp(parent, &map, cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def) < 0) {
390 * Check the types in the map are valid
392 if (validate && (validate(map, ctx) < 0)) goto error;
394 parent = *tail = map;
403 /** Convert strings to vp_map_t
405 * Treatment of operands depends on quotation, barewords are treated
406 * as attribute references, double quoted values are treated as
407 * expandable strings, single quoted values are treated as literal
410 * Return must be freed with talloc_free
412 * @param[in] ctx for talloc
413 * @param[out] out Where to store the head of the map.
414 * @param[in] lhs of the operation
415 * @param[in] lhs_type type of the LHS string
416 * @param[in] op the operation to perform
417 * @param[in] rhs of the operation
418 * @param[in] rhs_type type of the RHS string
419 * @param[in] dst_request_def The default request to insert unqualified
421 * @param[in] dst_list_def The default list to insert unqualified attributes
423 * @param[in] src_request_def The default request to resolve attribute
425 * @param[in] src_list_def The default list to resolve unqualified attributes
427 * @return vp_map_t if successful or NULL on error.
429 int map_afrom_fields(TALLOC_CTX *ctx, vp_map_t **out, char const *lhs, FR_TOKEN lhs_type,
430 FR_TOKEN op, char const *rhs, FR_TOKEN rhs_type,
431 request_refs_t dst_request_def,
432 pair_lists_t dst_list_def,
433 request_refs_t src_request_def,
434 pair_lists_t src_list_def)
439 map = talloc_zero(ctx, vp_map_t);
441 slen = tmpl_afrom_str(map, &map->lhs, lhs, strlen(lhs), lhs_type, dst_request_def, dst_list_def, true);
450 if ((map->lhs->type == TMPL_TYPE_ATTR) &&
451 map->lhs->tmpl_da->flags.is_unknown &&
452 map_cast_from_hex(map, rhs_type, rhs)) {
456 slen = tmpl_afrom_str(map, &map->rhs, rhs, strlen(rhs), rhs_type, src_request_def, src_list_def, true);
457 if (slen < 0) goto error;
466 /** Convert a value pair string to valuepair map
468 * Takes a valuepair string with list and request qualifiers and converts it into a
471 * @param ctx where to allocate the map.
472 * @param out Where to write the new map (must be freed with talloc_free()).
473 * @param vp_str string to parse.
474 * @param dst_request_def to use if attribute isn't qualified.
475 * @param dst_list_def to use if attribute isn't qualified.
476 * @param src_request_def to use if attribute isn't qualified.
477 * @param src_list_def to use if attribute isn't qualified.
478 * @return 0 on success, < 0 on error.
480 int map_afrom_attr_str(TALLOC_CTX *ctx, vp_map_t **out, char const *vp_str,
481 request_refs_t dst_request_def, pair_lists_t dst_list_def,
482 request_refs_t src_request_def, pair_lists_t src_list_def)
484 char const *p = vp_str;
488 vp_map_t *map = NULL;
490 quote = gettoken(&p, raw.l_opand, sizeof(raw.l_opand), false);
500 fr_strerror_printf("Left operand must be an attribute");
505 if (raw.op == T_INVALID) goto error;
507 raw.quote = gettoken(&p, raw.r_opand, sizeof(raw.r_opand), false);
508 if (raw.quote == T_INVALID) goto error;
509 if (!fr_str_tok[raw.quote]) {
510 fr_strerror_printf("Right operand must be an attribute or string");
514 if (map_afrom_fields(ctx, &map, raw.l_opand, T_BARE_WORD, raw.op, raw.r_opand, raw.quote,
515 dst_request_def, dst_list_def, src_request_def, src_list_def) < 0) {
519 rad_assert(map != NULL);
527 /** Compare map where LHS is #TMPL_TYPE_ATTR
529 * Compares maps by lhs->tmpl_da, lhs->tmpl_tag, lhs->tmpl_num
531 * @note both map->lhs must be #TMPL_TYPE_ATTR.
533 * @param a first map.
534 * @param b second map.
536 int8_t map_cmp_by_lhs_attr(void const *a, void const *b)
538 vp_tmpl_t const *my_a = ((vp_map_t const *)a)->lhs;
539 vp_tmpl_t const *my_b = ((vp_map_t const *)b)->lhs;
546 rad_assert(my_a->type == TMPL_TYPE_ATTR);
547 rad_assert(my_b->type == TMPL_TYPE_ATTR);
549 cmp = fr_pointer_cmp(my_a->tmpl_da, my_b->tmpl_da);
550 if (cmp != 0) return cmp;
552 if (my_a->tmpl_tag < my_b->tmpl_tag) return -1;
554 if (my_a->tmpl_tag > my_b->tmpl_tag) return 1;
556 if (my_a->tmpl_num < my_b->tmpl_num) return -1;
558 if (my_a->tmpl_num > my_b->tmpl_num) return 1;
563 static void map_sort_split(vp_map_t *source, vp_map_t **front, vp_map_t **back)
569 * Stopping condition - no more elements left to split
571 if (!source || !source->next) {
579 * Fast advances twice as fast as slow, so when it gets to the end,
580 * slow will point to the middle of the linked list.
598 static vp_map_t *map_sort_merge(vp_map_t *a, vp_map_t *b, fr_cmp_t cmp)
600 vp_map_t *result = NULL;
606 * Compare things in the maps
608 if (cmp(a, b) <= 0) {
610 result->next = map_sort_merge(a->next, b, cmp);
613 result->next = map_sort_merge(a, b->next, cmp);
619 /** Sort a linked list of #vp_map_t using merge sort
621 * @param[in,out] maps List of #vp_map_t to sort.
622 * @param[in] cmp to sort with
624 void map_sort(vp_map_t **maps, fr_cmp_t cmp)
626 vp_map_t *head = *maps;
631 * If there's 0-1 elements it must already be sorted.
633 if (!head || !head->next) {
637 map_sort_split(head, &a, &b); /* Split into sublists */
638 map_sort(&a, cmp); /* Traverse left */
639 map_sort(&b, cmp); /* Traverse right */
642 * merge the two sorted lists together
644 *maps = map_sort_merge(a, b, cmp);
647 /** Process map which has exec as a src
649 * Evaluate maps which specify exec as a src. This may be used by various sorts of update sections,
650 * and so has been broken out into it's own function.
652 * @param[in,out] ctx to allocate new #VALUE_PAIR (s) in.
653 * @param[out] out Where to write the #VALUE_PAIR (s).
654 * @param[in] request structure (used only for talloc).
655 * @param[in] map the map. The LHS (dst) must be TMPL_TYPE_ATTR or TMPL_TYPE_LIST. The RHS (src)
656 * must be TMPL_TYPE_EXEC.
657 * @return -1 on failure, 0 on success.
659 static int map_exec_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map)
662 char *expanded = NULL;
664 VALUE_PAIR **input_pairs = NULL;
665 VALUE_PAIR *output_pairs = NULL;
671 rad_assert(map->rhs->type == TMPL_TYPE_EXEC);
672 rad_assert((map->lhs->type == TMPL_TYPE_ATTR) || (map->lhs->type == TMPL_TYPE_LIST));
675 * We always put the request pairs into the environment
677 input_pairs = radius_list(request, PAIR_LIST_REQUEST);
680 * Automagically switch output type depending on our destination
681 * If dst is a list, then we create attributes from the output of the program
682 * if dst is an attribute, then we create an attribute of that type and then
683 * call fr_pair_value_from_str on the output of the script.
685 result = radius_exec_program(ctx, answer, sizeof(answer),
686 (map->lhs->type == TMPL_TYPE_LIST) ? &output_pairs : NULL,
687 request, map->rhs->name, input_pairs ? *input_pairs : NULL,
688 true, true, EXEC_TIMEOUT);
689 talloc_free(expanded);
691 talloc_free(output_pairs);
695 switch (map->lhs->type) {
698 REDEBUG("No valid attributes received from program");
708 vp = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
711 vp->tag = map->lhs->tmpl_tag;
712 if (fr_pair_value_from_str(vp, answer, -1) < 0) {
713 fr_pair_list_free(&vp);
728 /** Convert a map to a VALUE_PAIR.
730 * @param[in,out] ctx to allocate #VALUE_PAIR (s) in.
731 * @param[out] out Where to write the #VALUE_PAIR (s), which may be NULL if not found
732 * @param[in] request The current request.
733 * @param[in] map the map. The LHS (dst) has to be #TMPL_TYPE_ATTR or #TMPL_TYPE_LIST.
734 * @param[in] uctx unused.
739 int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, UNUSED void *uctx)
743 VALUE_PAIR *vp = NULL, *new, *found = NULL;
744 REQUEST *context = request;
752 rad_assert(map->lhs != NULL);
753 rad_assert(map->rhs != NULL);
755 rad_assert((map->lhs->type == TMPL_TYPE_LIST) || (map->lhs->type == TMPL_TYPE_ATTR));
758 * Special case for !*, we don't need to parse RHS as this is a unary operator.
760 if (map->op == T_OP_CMP_FALSE) return 0;
763 * List to list found, this is a special case because we don't need
764 * to allocate any attributes, just finding the current list, and change
767 if ((map->lhs->type == TMPL_TYPE_LIST) && (map->rhs->type == TMPL_TYPE_LIST)) {
768 VALUE_PAIR **from = NULL;
770 if (radius_request(&context, map->rhs->tmpl_request) == 0) {
771 from = radius_list(context, map->rhs->tmpl_list);
775 found = fr_pair_list_copy(ctx, *from);
778 * List to list copy is empty if the src list has no attributes.
780 if (!found) return 0;
782 for (vp = fr_cursor_init(&cursor, &found);
784 vp = fr_cursor_next(&cursor)) {
796 switch (map->rhs->type) {
797 case TMPL_TYPE_XLAT_STRUCT:
798 rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
799 rad_assert(map->lhs->tmpl_da); /* We need to know which attribute to create */
800 rad_assert(map->rhs->tmpl_xlat != NULL);
802 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
806 slen = radius_axlat_struct(&str, request, map->rhs->tmpl_xlat, NULL, NULL);
813 * We do the debug printing because radius_axlat_struct
814 * doesn't have access to the original string. It's been
815 * mangled during the parsing to xlat_exp_t
817 RDEBUG2("EXPAND %s", map->rhs->name);
818 RDEBUG2(" --> %s", str);
820 rcode = fr_pair_value_from_str(new, str, -1);
823 fr_pair_list_free(&new);
827 new->tag = map->lhs->tmpl_tag;
832 rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
833 rad_assert(map->lhs->tmpl_da); /* We need to know which attribute to create */
835 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
839 slen = radius_axlat(&str, request, map->rhs->name, NULL, NULL);
845 rcode = fr_pair_value_from_str(new, str, -1);
848 fr_pair_list_free(&new);
852 new->tag = map->lhs->tmpl_tag;
856 case TMPL_TYPE_LITERAL:
857 rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
858 rad_assert(map->lhs->tmpl_da); /* We need to know which attribute to create */
860 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
863 if (fr_pair_value_from_str(new, map->rhs->name, -1) < 0) {
868 new->tag = map->lhs->tmpl_tag;
876 rad_assert(((map->lhs->type == TMPL_TYPE_ATTR) && map->lhs->tmpl_da) ||
877 ((map->lhs->type == TMPL_TYPE_LIST) && !map->lhs->tmpl_da));
880 * @todo should log error, and return -1 for v3.1 (causes update to fail)
882 if (tmpl_copy_vps(ctx, &found, request, map->rhs) < 0) return 0;
884 vp = fr_cursor_init(&from, &found);
887 * Src/Dst attributes don't match, convert src attributes
890 if ((map->lhs->type == TMPL_TYPE_ATTR) &&
891 (map->rhs->tmpl_da->type != map->lhs->tmpl_da->type)) {
894 (void) fr_cursor_init(&to, out);
895 for (; vp; vp = fr_cursor_next(&from)) {
896 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
899 len = value_data_cast(new, &new->data, new->da->type, new->da,
900 vp->da->type, vp->da, &vp->data, vp->vp_length);
902 REDEBUG("Attribute conversion failed: %s", fr_strerror());
903 fr_pair_list_free(&found);
904 fr_pair_list_free(&new);
908 new->vp_length = len;
909 vp = fr_cursor_remove(&from);
912 if (new->da->type == PW_TYPE_STRING) {
913 rad_assert(new->vp_strvalue != NULL);
917 new->tag = map->lhs->tmpl_tag;
918 fr_cursor_insert(&to, new);
924 * Otherwise we just need to fixup the attribute types
927 for (; vp; vp = fr_cursor_next(&from)) {
928 vp->da = map->lhs->tmpl_da;
930 vp->tag = map->lhs->tmpl_tag;
937 rad_assert(map->lhs->tmpl_da);
938 rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
939 rad_assert(map->lhs->tmpl_da->type == map->rhs->tmpl_data_type);
941 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
944 len = value_data_copy(new, &new->data, new->da->type, &map->rhs->tmpl_data_value,
945 map->rhs->tmpl_data_length);
946 if (len < 0) goto error;
948 new->vp_length = len;
950 new->tag = map->lhs->tmpl_tag;
957 * This essentially does the same as rlm_exec xlat, except it's non-configurable.
958 * It's only really here as a convenience for people who expect the contents of
959 * backticks to be executed in a shell.
961 * exec string is xlat expanded and arguments are shell escaped.
964 return map_exec_to_vp(ctx, out, request, map);
967 rad_assert(0); /* Should have been caught at parse time */
970 fr_pair_list_free(&vp);
977 #define DEBUG_OVERWRITE(_old, _new) \
979 if (RDEBUG_ENABLED3) {\
980 char *old = vp_aprints_value(request, _old, '"');\
981 char *new = vp_aprints_value(request, _new, '"');\
982 RDEBUG3("Overwriting value \"%s\" with \"%s\"", old, new);\
988 /** Convert vp_map_t to VALUE_PAIR(s) and add them to a REQUEST.
990 * Takes a single vp_map_t, resolves request and list identifiers
991 * to pointers in the current request, then attempts to retrieve module
992 * specific value(s) using callback, and adds the resulting values to the
993 * correct request/list.
995 * @param request The current request.
996 * @param map specifying destination attribute and location and src identifier.
997 * @param func to retrieve module specific values and convert them to
999 * @param ctx to be passed to func.
1000 * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success.
1002 int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t func, void *ctx)
1006 VALUE_PAIR **list, *vp, *dst, *head = NULL;
1010 vp_cursor_t dst_list, src_list;
1016 rad_assert(map->lhs != NULL);
1017 rad_assert(map->rhs != NULL);
1020 * Preprocessing of the LHS of the map.
1022 switch (map->lhs->type) {
1024 * Already in the correct form.
1026 case TMPL_TYPE_LIST:
1027 case TMPL_TYPE_ATTR:
1031 * Everything else gets expanded, then re-parsed as an
1032 * attribute reference.
1034 case TMPL_TYPE_XLAT:
1035 case TMPL_TYPE_XLAT_STRUCT:
1036 case TMPL_TYPE_EXEC:
1041 slen = tmpl_aexpand(request, &attr, request, map->lhs, NULL, NULL);
1043 REDEBUG("Left side \"%.*s\" of map failed expansion", (int)map->lhs->len, map->lhs->name);
1048 slen = tmpl_from_attr_str(&exp_lhs, attr, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) ;
1050 REDEBUG("Left side \"%.*s\" expansion not an attribute reference: %s",
1051 (int)map->lhs->len, map->lhs->name, fr_strerror());
1055 rad_assert((exp_lhs.type == TMPL_TYPE_ATTR) || (exp_lhs.type == TMPL_TYPE_LIST));
1057 memcpy(&exp_map, map, sizeof(exp_map));
1058 exp_map.lhs = &exp_lhs;
1070 * Sanity check inputs. We can have a list or attribute
1073 if ((map->lhs->type != TMPL_TYPE_LIST) &&
1074 (map->lhs->type != TMPL_TYPE_ATTR)) {
1075 REDEBUG("Left side \"%.*s\" of map should be an attr or list but is an %s",
1076 (int)map->lhs->len, map->lhs->name,
1077 fr_int2str(tmpl_names, map->lhs->type, "<INVALID>"));
1082 if (radius_request(&context, map->lhs->tmpl_request) < 0) {
1083 REDEBUG("Mapping \"%.*s\" -> \"%.*s\" invalid in this context",
1084 (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name);
1089 * If there's no CoA packet and we're updating it,
1092 if (((map->lhs->tmpl_list == PAIR_LIST_COA) ||
1093 (map->lhs->tmpl_list == PAIR_LIST_DM)) && !request->coa) {
1094 if (!request_alloc_coa(context)) {
1095 REDEBUG("Failed to create a CoA/Disconnect Request message");
1098 context->coa->proxy->code = (map->lhs->tmpl_list == PAIR_LIST_COA) ?
1099 PW_CODE_COA_REQUEST :
1100 PW_CODE_DISCONNECT_REQUEST;
1103 list = radius_list(context, map->lhs->tmpl_list);
1105 REDEBUG("Mapping \"%.*s\" -> \"%.*s\" invalid in this context",
1106 (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name);
1111 parent = radius_list_ctx(context, map->lhs->tmpl_list);
1115 * The callback should either return -1 to signify operations error,
1116 * -2 when it can't find the attribute or list being referenced, or
1117 * 0 to signify success. It may return "success", but still have no
1120 if (map->rhs->type != TMPL_TYPE_NULL) {
1121 rcode = func(parent, &head, request, map, ctx);
1127 RDEBUG2("No attributes updated");
1131 if (rad_debug_lvl) map_debug_log(request, map, NULL);
1137 for (vp = fr_cursor_init(&src_list, &head);
1139 vp = fr_cursor_next(&src_list)) {
1142 if (rad_debug_lvl) map_debug_log(request, map, vp);
1146 * The destination is a list (which is a completely different set of operations)
1148 if (map->lhs->type == TMPL_TYPE_LIST) {
1150 case T_OP_CMP_FALSE:
1151 /* We don't need the src VPs (should just be 'ANY') */
1154 /* Clear the entire dst list */
1155 fr_pair_list_free(list);
1157 if (map->lhs->tmpl_list == PAIR_LIST_REQUEST) {
1158 context->username = NULL;
1159 context->password = NULL;
1164 if (map->rhs->type == TMPL_TYPE_LIST) {
1165 fr_pair_list_free(list);
1170 rad_assert(map->rhs->type == TMPL_TYPE_EXEC);
1172 fr_pair_list_move(parent, list, &head);
1173 fr_pair_list_free(&head);
1178 fr_pair_list_free(&head);
1184 * Find the destination attribute. We leave with either
1185 * the dst_list and vp pointing to the attribute or the VP
1186 * being NULL (no attribute at that index).
1188 num = map->lhs->tmpl_num;
1189 (void) fr_cursor_init(&dst_list, list);
1190 if (num != NUM_ANY) {
1191 while ((dst = fr_cursor_next_by_da(&dst_list, map->lhs->tmpl_da, map->lhs->tmpl_tag))) {
1192 if (num-- == 0) break;
1195 dst = fr_cursor_next_by_da(&dst_list, map->lhs->tmpl_da, map->lhs->tmpl_tag);
1197 rad_assert(!dst || (map->lhs->tmpl_da == dst->da));
1200 * The destination is an attribute
1206 * !* - Remove all attributes which match dst in the specified list.
1207 * This doesn't use attributes returned by the func(), and immediately frees them.
1209 case T_OP_CMP_FALSE:
1210 /* We don't need the src VPs (should just be 'ANY') */
1215 * Wildcard: delete all of the matching ones, based on tag.
1217 if (map->lhs->tmpl_num == NUM_ANY) {
1218 fr_pair_delete_by_num(list, map->lhs->tmpl_da->attr, map->lhs->tmpl_da->vendor, map->lhs->tmpl_tag);
1221 * We've found the Nth one. Delete it, and only it.
1224 dst = fr_cursor_remove(&dst_list);
1225 fr_pair_list_free(&dst);
1229 * Check that the User-Name and User-Password
1230 * caches point to the correct attribute.
1235 * -= - Delete attributes in the dst list which match any of the
1236 * src_list attributes.
1238 * This operation has two modes:
1239 * - If map->lhs->tmpl_num > 0, we check each of the src_list attributes against
1240 * the dst attribute, to see if any of their values match.
1241 * - If map->lhs->tmpl_num == NUM_ANY, we compare all instances of the dst attribute
1242 * against each of the src_list attributes.
1245 /* We didn't find any attributes earlier */
1247 fr_pair_list_free(&head);
1252 * Instance specific[n] delete
1254 if (map->lhs->tmpl_num != NUM_ANY) {
1255 for (vp = fr_cursor_first(&src_list);
1257 vp = fr_cursor_next(&src_list)) {
1258 head->op = T_OP_CMP_EQ;
1259 rcode = radius_compare_vps(request, vp, dst);
1261 dst = fr_cursor_remove(&dst_list);
1262 fr_pair_list_free(&dst);
1266 fr_pair_list_free(&head);
1267 if (!found) return 0;
1272 * All instances[*] delete
1274 for (dst = fr_cursor_current(&dst_list);
1276 dst = fr_cursor_next_by_da(&dst_list, map->lhs->tmpl_da, map->lhs->tmpl_tag)) {
1277 for (vp = fr_cursor_first(&src_list);
1279 vp = fr_cursor_next(&src_list)) {
1280 head->op = T_OP_CMP_EQ;
1281 rcode = radius_compare_vps(request, vp, dst);
1283 dst = fr_cursor_remove(&dst_list);
1284 fr_pair_list_free(&dst);
1289 fr_pair_list_free(&head);
1290 if (!found) return 0;
1295 * Another fixup pass to set tags on attributes were about to insert
1297 if (map->lhs->tmpl_tag != TAG_ANY) {
1298 for (vp = fr_cursor_init(&src_list, &head);
1300 vp = fr_cursor_next(&src_list)) {
1301 vp->tag = map->lhs->tmpl_tag;
1307 * = - Set only if not already set
1311 RDEBUG3("Refusing to overwrite (use :=)");
1312 fr_pair_list_free(&head);
1316 /* Insert first instance (if multiple) */
1317 fr_cursor_first(&src_list);
1318 fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
1319 /* Free any we didn't insert */
1320 fr_pair_list_free(&head);
1324 * := - Overwrite existing attribute with last src_list attribute
1327 /* Wind to last instance */
1328 fr_cursor_last(&src_list);
1330 DEBUG_OVERWRITE(dst, fr_cursor_current(&src_list));
1331 dst = fr_cursor_replace(&dst_list, fr_cursor_remove(&src_list));
1332 fr_pair_list_free(&dst);
1334 fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
1336 /* Free any we didn't insert */
1337 fr_pair_list_free(&head);
1341 * += - Add all src_list attributes to the destination
1344 /* Insert all the instances! (if multiple) */
1345 fr_pair_add(list, head);
1363 fr_pair_list_sort(&head, fr_pair_cmp_by_da_tag);
1364 fr_pair_list_sort(list, fr_pair_cmp_by_da_tag);
1366 fr_cursor_first(&dst_list);
1368 for (b = fr_cursor_first(&src_list);
1370 b = fr_cursor_next(&src_list)) {
1371 for (a = fr_cursor_current(&dst_list);
1373 a = fr_cursor_next(&dst_list)) {
1376 cmp = fr_pair_cmp_by_da_tag(a, b); /* attribute and tag match */
1378 else if (cmp < 0) continue;
1380 cmp = (value_data_cmp_op(map->op, a->da->type, &a->data, a->vp_length, b->da->type, &b->data, b->vp_length) == 0);
1382 a = fr_cursor_remove(&dst_list);
1386 if (!a) break; /* end of the list */
1388 fr_pair_list_free(&head);
1393 rad_assert(0); /* Should have been caught be the caller */
1401 * Update the cached username && password. This is code
1402 * we execute on EVERY update (sigh) so that SOME modules
1403 * MIGHT NOT have to do the search themselves.
1405 * TBH, we should probably make each module just do the
1406 * search themselves.
1408 if (map->lhs->tmpl_list == PAIR_LIST_REQUEST) {
1409 context->username = NULL;
1410 context->password = NULL;
1412 for (vp = fr_cursor_init(&src_list, list);
1414 vp = fr_cursor_next(&src_list)) {
1416 if (vp->da->vendor != 0) continue;
1417 if (vp->da->flags.has_tag) continue;
1419 if (!context->username && (vp->da->attr == PW_USER_NAME)) {
1420 context->username = vp;
1424 if (vp->da->attr == PW_STRIPPED_USER_NAME) {
1425 context->username = vp;
1429 if (vp->da->attr == PW_USER_PASSWORD) {
1430 context->password = vp;
1438 /** Check whether the destination of a map is currently valid
1440 * @param request The current request.
1441 * @param map to check.
1442 * @return true if the map resolves to a request and list else false.
1444 bool map_dst_valid(REQUEST *request, vp_map_t const *map)
1446 REQUEST *context = request;
1450 if (radius_request(&context, map->lhs->tmpl_request) < 0) return false;
1451 if (!radius_list(context, map->lhs->tmpl_list)) return false;
1456 /** Print a map to a string
1458 * @param[out] buffer for the output string
1459 * @param[in] bufsize of the buffer
1460 * @param[in] map to print
1461 * @return the size of the string printed
1463 size_t map_prints(char *buffer, size_t bufsize, vp_map_t const *map)
1466 DICT_ATTR const *da = NULL;
1468 char *end = buffer + bufsize;
1472 if (map->lhs->type == TMPL_TYPE_ATTR) da = map->lhs->tmpl_da;
1474 len = tmpl_prints(buffer, bufsize, map->lhs, da);
1478 strlcpy(p, fr_token_name(map->op), end - p);
1483 * The RHS doesn't matter for many operators
1485 if ((map->op == T_OP_CMP_TRUE) ||
1486 (map->op == T_OP_CMP_FALSE)) {
1487 strlcpy(p, "ANY", (end - p));
1492 rad_assert(map->rhs != NULL);
1494 if ((map->lhs->type == TMPL_TYPE_ATTR) &&
1495 (map->lhs->tmpl_da->type == PW_TYPE_STRING) &&
1496 (map->rhs->type == TMPL_TYPE_LITERAL)) {
1498 len = tmpl_prints(p, end - p, map->rhs, da);
1503 len = tmpl_prints(p, end - p, map->rhs, da);
1511 * Debug print a map / VP
1513 void map_debug_log(REQUEST *request, vp_map_t const *map, VALUE_PAIR const *vp)
1519 rad_assert(map->lhs != NULL);
1520 rad_assert(map->rhs != NULL);
1522 rad_assert(vp || (map->rhs->type == TMPL_TYPE_NULL));
1524 switch (map->rhs->type) {
1526 * Just print the value being assigned
1529 case TMPL_TYPE_LITERAL:
1530 vp_prints_value(buffer, sizeof(buffer), vp, map->rhs->quote);
1534 case TMPL_TYPE_XLAT:
1535 case TMPL_TYPE_XLAT_STRUCT:
1536 vp_prints_value(buffer, sizeof(buffer), vp, map->rhs->quote);
1540 case TMPL_TYPE_DATA:
1541 vp_prints_value(buffer, sizeof(buffer), vp, map->rhs->quote);
1546 * For the lists, we can't use the original name, and have to
1547 * rebuild it using tmpl_prints, for each attribute we're
1550 case TMPL_TYPE_LIST:
1556 * Fudge a temporary tmpl that describes the attribute we're copying
1557 * this is a combination of the original list tmpl, and values from
1558 * the VALUE_PAIR. This way, we get tag info included.
1560 memcpy(&vpt, map->rhs, sizeof(vpt));
1561 vpt.tmpl_da = vp->da;
1562 vpt.tmpl_tag = vp->tag;
1563 vpt.type = TMPL_TYPE_ATTR;
1566 * Not appropriate to use map->rhs->quote here, as that's the quoting
1567 * around the list ref. The attribute value has no quoting, so we choose
1568 * the quoting based on the data type, and whether it's printable.
1570 if (vp->da->type == PW_TYPE_STRING) quote = is_printable(vp->vp_strvalue,
1571 vp->vp_length) ? '\'' : '"';
1572 vp_prints_value(buffer, sizeof(buffer), vp, quote);
1573 tmpl_prints(attr, sizeof(attr), &vpt, vp->da);
1574 value = talloc_typed_asprintf(request, "%s -> %s", attr, buffer);
1578 case TMPL_TYPE_ATTR:
1583 * Not appropriate to use map->rhs->quote here, as that's the quoting
1584 * around the attr ref. The attribute value has no quoting, so we choose
1585 * the quoting based on the data type, and whether it's printable.
1587 if (vp->da->type == PW_TYPE_STRING) quote = is_printable(vp->vp_strvalue,
1588 vp->vp_length) ? '\'' : '"';
1589 vp_prints_value(buffer, sizeof(buffer), vp, quote);
1590 value = talloc_typed_asprintf(request, "%.*s -> %s", (int)map->rhs->len, map->rhs->name, buffer);
1594 case TMPL_TYPE_NULL:
1595 strcpy(buffer, "ANY");
1600 switch (map->lhs->type) {
1601 case TMPL_TYPE_LIST:
1602 RDEBUG("%.*s:%s %s %s", (int)map->lhs->len, map->lhs->name, vp ? vp->da->name : "",
1603 fr_int2str(fr_tokens, vp ? vp->op : map->op, "<INVALID>"), value);
1606 case TMPL_TYPE_ATTR:
1607 RDEBUG("%s %s %s", map->lhs->name,
1608 fr_int2str(fr_tokens, vp ? vp->op : map->op, "<INVALID>"), value);
1615 if (value != buffer) talloc_free(value);