996b790102a873d39a1c42df1d52f28158b5e291
[freeradius.git] / src / main / map.c
1 /*
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.
6  *
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.
11  *
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
15  */
16
17 /*
18  * $Id$
19  *
20  * @brief map / template functions
21  * @file main/map.c
22  *
23  * @ingroup AVP
24  *
25  * @copyright 2013 The FreeRADIUS server project
26  * @copyright 2013  Alan DeKok <aland@freeradius.org>
27  */
28
29 RCSID("$Id$")
30
31 #include <freeradius-devel/radiusd.h>
32 #include <freeradius-devel/rad_assert.h>
33
34 #include <ctype.h>
35
36 #ifdef DEBUG_MAP
37 static void map_dump(REQUEST *request, vp_map_t const *map)
38 {
39         RDEBUG(">>> MAP TYPES LHS: %s, RHS: %s",
40                fr_int2str(tmpl_names, map->lhs->type, "???"),
41                fr_int2str(tmpl_names, map->rhs->type, "???"));
42
43         if (map->rhs) {
44                 RDEBUG(">>> MAP NAMES %s %s", map->lhs->name, map->rhs->name);
45         }
46 }
47 #endif
48
49
50 /** re-parse a map where the lhs is an unknown attribute.
51  *
52  *
53  * @param map to process.
54  * @param rhs_type quotation type around rhs.
55  * @param rhs string to re-parse.
56  */
57 bool map_cast_from_hex(vp_map_t *map, FR_TOKEN rhs_type, char const *rhs)
58 {
59         size_t len;
60         ssize_t rlen;
61         uint8_t *ptr;
62         char const *p;
63         pair_lists_t list;
64
65         DICT_ATTR const *da;
66         VALUE_PAIR *vp;
67         vp_tmpl_t *vpt;
68
69         rad_assert(map != NULL);
70
71         rad_assert(map->lhs != NULL);
72         rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
73
74         rad_assert(map->rhs == NULL);
75         rad_assert(rhs != NULL);
76
77         VERIFY_MAP(map);
78
79         /*
80          *      If the attribute is still unknown, go parse the RHS.
81          */
82         da = dict_attrbyvalue(map->lhs->tmpl_da->attr, map->lhs->tmpl_da->vendor);
83         if (!da || da->flags.is_unknown) return false;
84
85         /*
86          *      If the RHS is something OTHER than an octet
87          *      string, go parse it as that.
88          */
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;
92
93         len = strlen(rhs + 2);
94
95         ptr = talloc_array(map, uint8_t, len >> 1);
96         if (!ptr) return false;
97
98         len = fr_hex2bin(ptr, len >> 1, rhs + 2, len);
99
100         /*
101          *      If we can't parse it, or if it's malformed,
102          *      it's still unknown.
103          */
104         rlen = data2vp(NULL, NULL, NULL, NULL, da, ptr, len, len, &vp);
105         talloc_free(ptr);
106
107         if (rlen < 0) return false;
108
109         if ((size_t) rlen < len) {
110         free_vp:
111                 fr_pair_list_free(&vp);
112                 return false;
113         }
114
115         /*
116          *      Was still parsed as an unknown attribute.
117          */
118         if (vp->da->flags.is_unknown) goto free_vp;
119
120         /*
121          *      Set the RHS to the PARSED name, not the crap octet
122          *      string which was input.
123          */
124         map->rhs = tmpl_alloc(map, TMPL_TYPE_DATA, NULL, 0);
125         if (!map->rhs) goto free_vp;
126
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);
132                 } else {
133                         map->rhs->tmpl_data_value.ptr = talloc_memdup(map->rhs, vp->data.ptr, vp->vp_length);
134                 }
135         } else {
136                 memcpy(&map->rhs->tmpl_data_value, &vp->data, sizeof(map->rhs->tmpl_data_value));
137         }
138         map->rhs->name = vp_aprints_value(map->rhs, vp, '"');
139         map->rhs->len = talloc_array_length(map->rhs->name) - 1;
140
141         /*
142          *      Set the LHS to the REAL attribute name.
143          */
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));
146         vpt->tmpl_da = da;
147
148         /*
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 '&'.
152          */
153         p = map->lhs->name;
154         if (*p == '&') p++;
155         len = radius_list_name(&list, p, PAIR_LIST_UNKNOWN);
156
157         if (list != PAIR_LIST_UNKNOWN) {
158                 rad_const_free(vpt->name);
159
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);
164         }
165
166         talloc_free(map->lhs);
167         map->lhs = vpt;
168
169         fr_pair_list_free(&vp);
170
171         VERIFY_MAP(map);
172
173         return true;
174 }
175
176 /** Convert CONFIG_PAIR (which may contain refs) to vp_map_t.
177  *
178  * Treats the left operand as an attribute reference
179  * @verbatim<request>.<list>.<attribute>@endverbatim
180  *
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.
184  *
185  * Return must be freed with talloc_free
186  *
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
191  *      attributes into.
192  * @param[in] dst_list_def The default list to insert unqualified attributes
193  *      into.
194  * @param[in] src_request_def The default request to resolve attribute
195  *      references in.
196  * @param[in] src_list_def The default list to resolve unqualified attributes
197  *      in.
198  * @return vp_map_t if successful or NULL on error.
199  */
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)
203 {
204         vp_map_t *map;
205         char const *attr, *value;
206         ssize_t slen;
207         FR_TOKEN type;
208
209         *out = NULL;
210
211         if (!cp) return -1;
212
213         map = talloc_zero(ctx, vp_map_t);
214         map->op = cf_pair_operator(cp);
215         map->ci = cf_pair_to_item(cp);
216
217         attr = cf_pair_attr(cp);
218         value = cf_pair_value(cp);
219         if (!value) {
220                 cf_log_err_cp(cp, "Missing attribute value");
221                 goto error;
222         }
223
224         /*
225          *      LHS may be an expansion (that expands to an attribute reference)
226          *      or an attribute reference. Quoting determines which it is.
227          */
228         type = cf_pair_attr_type(cp);
229         switch (type) {
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);
234                 if (slen <= 0) {
235                         char *spaces, *text;
236
237                 marker:
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());
241
242                         talloc_free(spaces);
243                         talloc_free(text);
244                         goto error;
245                 }
246                 break;
247
248         default:
249                 slen = tmpl_afrom_attr_str(ctx, &map->lhs, attr, dst_request_def, dst_list_def, true, true);
250                 if (slen <= 0) {
251                         cf_log_err_cp(cp, "Failed parsing attribute reference");
252
253                         goto marker;
254                 }
255
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());
259                         goto error;
260                 }
261
262                 break;
263         }
264
265         /*
266          *      RHS might be an attribute reference.
267          */
268         type = cf_pair_value_type(cp);
269
270         if ((map->lhs->type == TMPL_TYPE_ATTR) &&
271             map->lhs->tmpl_da->flags.is_unknown &&
272             !map_cast_from_hex(map, type, value)) {
273                 goto error;
274
275         } else {
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());
280                         goto error;
281                 }
282         }
283         if (!map->rhs) {
284                 cf_log_err_cp(cp, "%s", fr_strerror());
285                 goto error;
286         }
287
288         /*
289          *      We cannot assign a count to an attribute.  That must
290          *      be done in an xlat.
291          */
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");
295                 goto error;
296         }
297
298         VERIFY_MAP(map);
299
300         *out = map;
301
302         return 0;
303
304 error:
305         talloc_free(map);
306         return -1;
307 }
308
309 /** Convert an 'update' config section into an attribute map.
310  *
311  * Uses 'name2' of section to set default request and lists.
312  *
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.
323  */
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,
327                  unsigned int max)
328 {
329         char const *cs_list, *p;
330
331         request_refs_t request_def = REQUEST_CURRENT;
332
333         CONF_ITEM *ci;
334         CONF_PAIR *cp;
335
336         unsigned int total = 0;
337         vp_map_t **tail, *map;
338         TALLOC_CTX *parent;
339
340         *out = NULL;
341         tail = out;
342
343         /*
344          *      The first map has cs as the parent.
345          *      The rest have the previous map as the parent.
346          */
347         parent = cs;
348
349         ci = cf_section_to_item(cs);
350
351         cs_list = p = cf_section_name2(cs);
352         if (cs_list) {
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");
356                         return -1;
357                 }
358
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);
363                         return -1;
364                 }
365         }
366
367         for (ci = cf_item_find_next(cs, NULL);
368              ci != NULL;
369              ci = cf_item_find_next(cs, ci)) {
370                 if (total++ == max) {
371                         cf_log_err(ci, "Map size exceeded");
372                 error:
373                         TALLOC_FREE(*out);
374                         return -1;
375                 }
376
377                 if (!cf_item_is_pair(ci)) {
378                         cf_log_err(ci, "Entry is not in \"attribute = value\" format");
379                         goto error;
380                 }
381
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) {
384                         goto error;
385                 }
386
387                 VERIFY_MAP(map);
388
389                 /*
390                  *      Check the types in the map are valid
391                  */
392                 if (validate && (validate(map, ctx) < 0)) goto error;
393
394                 parent = *tail = map;
395                 tail = &(map->next);
396         }
397
398         return 0;
399
400 }
401
402
403 /** Convert strings to vp_map_t
404  *
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
408  * strings.
409  *
410  * Return must be freed with talloc_free
411  *
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
420  *      attributes into.
421  * @param[in] dst_list_def The default list to insert unqualified attributes
422  *      into.
423  * @param[in] src_request_def The default request to resolve attribute
424  *      references in.
425  * @param[in] src_list_def The default list to resolve unqualified attributes
426  *      in.
427  * @return vp_map_t if successful or NULL on error.
428  */
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)
435 {
436         ssize_t slen;
437         vp_map_t *map;
438
439         map = talloc_zero(ctx, vp_map_t);
440
441         slen = tmpl_afrom_str(map, &map->lhs, lhs, strlen(lhs), lhs_type, dst_request_def, dst_list_def, true);
442         if (slen < 0) {
443         error:
444                 talloc_free(map);
445                 return -1;
446         }
447
448         map->op = op;
449
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)) {
453                 return 0;
454         }
455
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;
458
459         VERIFY_MAP(map);
460
461         *out = map;
462
463         return 0;
464 }
465
466 /** Convert a value pair string to valuepair map
467  *
468  * Takes a valuepair string with list and request qualifiers and converts it into a
469  * vp_map_t.
470  *
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.
479  */
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)
483 {
484         char const *p = vp_str;
485         FR_TOKEN quote;
486
487         VALUE_PAIR_RAW raw;
488         vp_map_t *map = NULL;
489
490         quote = gettoken(&p, raw.l_opand, sizeof(raw.l_opand), false);
491         switch (quote) {
492         case T_BARE_WORD:
493                 break;
494
495         case T_INVALID:
496         error:
497                 return -1;
498
499         default:
500                 fr_strerror_printf("Left operand must be an attribute");
501                 return -1;
502         }
503
504         raw.op = getop(&p);
505         if (raw.op == T_INVALID) goto error;
506
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");
511                 return -1;
512         }
513
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) {
516                 return -1;
517         }
518
519         rad_assert(map != NULL);
520         *out = map;
521
522         VERIFY_MAP(map);
523
524         return 0;
525 }
526
527 /** Compare map where LHS is #TMPL_TYPE_ATTR
528  *
529  * Compares maps by lhs->tmpl_da, lhs->tmpl_tag, lhs->tmpl_num
530  *
531  * @note both map->lhs must be #TMPL_TYPE_ATTR.
532  *
533  * @param a first map.
534  * @param b second map.
535  */
536 int8_t map_cmp_by_lhs_attr(void const *a, void const *b)
537 {
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;
540
541         VERIFY_TMPL(my_a);
542         VERIFY_TMPL(my_b);
543
544         uint8_t cmp;
545
546         rad_assert(my_a->type == TMPL_TYPE_ATTR);
547         rad_assert(my_b->type == TMPL_TYPE_ATTR);
548
549         cmp = fr_pointer_cmp(my_a->tmpl_da, my_b->tmpl_da);
550         if (cmp != 0) return cmp;
551
552         if (my_a->tmpl_tag < my_b->tmpl_tag) return -1;
553
554         if (my_a->tmpl_tag > my_b->tmpl_tag) return 1;
555
556         if (my_a->tmpl_num < my_b->tmpl_num) return -1;
557
558         if (my_a->tmpl_num > my_b->tmpl_num) return 1;
559
560         return 0;
561 }
562
563 static void map_sort_split(vp_map_t *source, vp_map_t **front, vp_map_t **back)
564 {
565         vp_map_t *fast;
566         vp_map_t *slow;
567
568         /*
569          *      Stopping condition - no more elements left to split
570          */
571         if (!source || !source->next) {
572                 *front = source;
573                 *back = NULL;
574
575                 return;
576         }
577
578         /*
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.
581          */
582         slow = source;
583         fast = source->next;
584
585         while (fast) {
586                 fast = fast->next;
587                 if (fast) {
588                         slow = slow->next;
589                         fast = fast->next;
590                 }
591         }
592
593         *front = source;
594         *back = slow->next;
595         slow->next = NULL;
596 }
597
598 static vp_map_t *map_sort_merge(vp_map_t *a, vp_map_t *b, fr_cmp_t cmp)
599 {
600         vp_map_t *result = NULL;
601
602         if (!a) return b;
603         if (!b) return a;
604
605         /*
606          *      Compare things in the maps
607          */
608         if (cmp(a, b) <= 0) {
609                 result = a;
610                 result->next = map_sort_merge(a->next, b, cmp);
611         } else {
612                 result = b;
613                 result->next = map_sort_merge(a, b->next, cmp);
614         }
615
616         return result;
617 }
618
619 /** Sort a linked list of #vp_map_t using merge sort
620  *
621  * @param[in,out] maps List of #vp_map_t to sort.
622  * @param[in] cmp to sort with
623  */
624 void map_sort(vp_map_t **maps, fr_cmp_t cmp)
625 {
626         vp_map_t *head = *maps;
627         vp_map_t *a;
628         vp_map_t *b;
629
630         /*
631          *      If there's 0-1 elements it must already be sorted.
632          */
633         if (!head || !head->next) {
634                 return;
635         }
636
637         map_sort_split(head, &a, &b);   /* Split into sublists */
638         map_sort(&a, cmp);              /* Traverse left */
639         map_sort(&b, cmp);              /* Traverse right */
640
641         /*
642          *      merge the two sorted lists together
643          */
644         *maps = map_sort_merge(a, b, cmp);
645 }
646
647 /** Process map which has exec as a src
648  *
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.
651  *
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.
658  */
659 static int map_exec_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map)
660 {
661         int result;
662         char *expanded = NULL;
663         char answer[1024];
664         VALUE_PAIR **input_pairs = NULL;
665         VALUE_PAIR *output_pairs = NULL;
666
667         *out = NULL;
668
669         VERIFY_MAP(map);
670
671         rad_assert(map->rhs->type == TMPL_TYPE_EXEC);
672         rad_assert((map->lhs->type == TMPL_TYPE_ATTR) || (map->lhs->type == TMPL_TYPE_LIST));
673
674         /*
675          *      We always put the request pairs into the environment
676          */
677         input_pairs = radius_list(request, PAIR_LIST_REQUEST);
678
679         /*
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.
684          */
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);
690         if (result != 0) {
691                 talloc_free(output_pairs);
692                 return -1;
693         }
694
695         switch (map->lhs->type) {
696         case TMPL_TYPE_LIST:
697                 if (!output_pairs) {
698                         REDEBUG("No valid attributes received from program");
699                         return -2;
700                 }
701                 *out = output_pairs;
702                 return 0;
703
704         case TMPL_TYPE_ATTR:
705         {
706                 VALUE_PAIR *vp;
707
708                 vp = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
709                 if (!vp) return -1;
710                 vp->op = map->op;
711                 vp->tag = map->lhs->tmpl_tag;
712                 if (fr_pair_value_from_str(vp, answer, -1) < 0) {
713                         fr_pair_list_free(&vp);
714                         return -2;
715                 }
716                 *out = vp;
717
718                 return 0;
719         }
720
721         default:
722                 rad_assert(0);
723         }
724
725         return -1;
726 }
727
728 /** Convert a map to a VALUE_PAIR.
729  *
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.
735  * @return
736  *      - 0 on success.
737  *      - -1 on failure.
738  */
739 int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, UNUSED void *uctx)
740 {
741         int rcode = 0;
742         ssize_t len;
743         VALUE_PAIR *vp = NULL, *new, *found = NULL;
744         REQUEST *context = request;
745         vp_cursor_t cursor;
746         ssize_t slen;
747         char *str;
748
749         *out = NULL;
750
751         VERIFY_MAP(map);
752         rad_assert(map->lhs != NULL);
753         rad_assert(map->rhs != NULL);
754
755         rad_assert((map->lhs->type == TMPL_TYPE_LIST) || (map->lhs->type == TMPL_TYPE_ATTR));
756
757         /*
758          *      Special case for !*, we don't need to parse RHS as this is a unary operator.
759          */
760         if (map->op == T_OP_CMP_FALSE) return 0;
761
762         /*
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
765          *      the op.
766          */
767         if ((map->lhs->type == TMPL_TYPE_LIST) && (map->rhs->type == TMPL_TYPE_LIST)) {
768                 VALUE_PAIR **from = NULL;
769
770                 if (radius_request(&context, map->rhs->tmpl_request) == 0) {
771                         from = radius_list(context, map->rhs->tmpl_list);
772                 }
773                 if (!from) return 0;
774
775                 found = fr_pair_list_copy(ctx, *from);
776
777                 /*
778                  *      List to list copy is empty if the src list has no attributes.
779                  */
780                 if (!found) return 0;
781
782                 for (vp = fr_cursor_init(&cursor, &found);
783                      vp;
784                      vp = fr_cursor_next(&cursor)) {
785                         vp->op = T_OP_ADD;
786                 }
787
788                 *out = found;
789
790                 return 0;
791         }
792
793         /*
794          *      And parse the RHS
795          */
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);
801
802                 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
803                 if (!new) return -1;
804
805                 str = NULL;
806                 slen = radius_axlat_struct(&str, request, map->rhs->tmpl_xlat, NULL, NULL);
807                 if (slen < 0) {
808                         rcode = slen;
809                         goto error;
810                 }
811
812                 /*
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
816                  */
817                 RDEBUG2("EXPAND %s", map->rhs->name);
818                 RDEBUG2("   --> %s", str);
819
820                 rcode = fr_pair_value_from_str(new, str, -1);
821                 talloc_free(str);
822                 if (rcode < 0) {
823                         fr_pair_list_free(&new);
824                         goto error;
825                 }
826                 new->op = map->op;
827                 new->tag = map->lhs->tmpl_tag;
828                 *out = new;
829                 break;
830
831         case TMPL_TYPE_XLAT:
832                 rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
833                 rad_assert(map->lhs->tmpl_da);  /* We need to know which attribute to create */
834
835                 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
836                 if (!new) return -1;
837
838                 str = NULL;
839                 slen = radius_axlat(&str, request, map->rhs->name, NULL, NULL);
840                 if (slen < 0) {
841                         rcode = slen;
842                         goto error;
843                 }
844
845                 rcode = fr_pair_value_from_str(new, str, -1);
846                 talloc_free(str);
847                 if (rcode < 0) {
848                         fr_pair_list_free(&new);
849                         goto error;
850                 }
851                 new->op = map->op;
852                 new->tag = map->lhs->tmpl_tag;
853                 *out = new;
854                 break;
855
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 */
859
860                 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
861                 if (!new) return -1;
862
863                 if (fr_pair_value_from_str(new, map->rhs->name, -1) < 0) {
864                         rcode = 0;
865                         goto error;
866                 }
867                 new->op = map->op;
868                 new->tag = map->lhs->tmpl_tag;
869                 *out = new;
870                 break;
871
872         case TMPL_TYPE_ATTR:
873         {
874                 vp_cursor_t from;
875
876                 rad_assert(((map->lhs->type == TMPL_TYPE_ATTR) && map->lhs->tmpl_da) ||
877                            ((map->lhs->type == TMPL_TYPE_LIST) && !map->lhs->tmpl_da));
878
879                 /*
880                  * @todo should log error, and return -1 for v3.1 (causes update to fail)
881                  */
882                 if (tmpl_copy_vps(ctx, &found, request, map->rhs) < 0) return 0;
883
884                 vp = fr_cursor_init(&from, &found);
885
886                 /*
887                  *  Src/Dst attributes don't match, convert src attributes
888                  *  to match dst.
889                  */
890                 if ((map->lhs->type == TMPL_TYPE_ATTR) &&
891                     (map->rhs->tmpl_da->type != map->lhs->tmpl_da->type)) {
892                         vp_cursor_t to;
893
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);
897                                 if (!new) return -1;
898
899                                 len = value_data_cast(new, &new->data, new->da->type, new->da,
900                                                       vp->da->type, vp->da, &vp->data, vp->vp_length);
901                                 if (len < 0) {
902                                         REDEBUG("Attribute conversion failed: %s", fr_strerror());
903                                         fr_pair_list_free(&found);
904                                         fr_pair_list_free(&new);
905                                         return -1;
906                                 }
907
908                                 new->vp_length = len;
909                                 vp = fr_cursor_remove(&from);
910                                 talloc_free(vp);
911
912                                 if (new->da->type == PW_TYPE_STRING) {
913                                         rad_assert(new->vp_strvalue != NULL);
914                                 }
915
916                                 new->op = map->op;
917                                 new->tag = map->lhs->tmpl_tag;
918                                 fr_cursor_insert(&to, new);
919                         }
920                         return 0;
921                 }
922
923                 /*
924                  *   Otherwise we just need to fixup the attribute types
925                  *   and operators
926                  */
927                 for (; vp; vp = fr_cursor_next(&from)) {
928                         vp->da = map->lhs->tmpl_da;
929                         vp->op = map->op;
930                         vp->tag = map->lhs->tmpl_tag;
931                 }
932                 *out = found;
933         }
934                 break;
935
936         case TMPL_TYPE_DATA:
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);
940
941                 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
942                 if (!new) return -1;
943
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;
947
948                 new->vp_length = len;
949                 new->op = map->op;
950                 new->tag = map->lhs->tmpl_tag;
951                 *out = new;
952
953                 VERIFY_MAP(map);
954                 break;
955
956         /*
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.
960          *
961          *      exec string is xlat expanded and arguments are shell escaped.
962          */
963         case TMPL_TYPE_EXEC:
964                 return map_exec_to_vp(ctx, out, request, map);
965
966         default:
967                 rad_assert(0);  /* Should have been caught at parse time */
968
969         error:
970                 fr_pair_list_free(&vp);
971                 return rcode;
972         }
973
974         return 0;
975 }
976
977 #define DEBUG_OVERWRITE(_old, _new) \
978 do {\
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);\
983                 talloc_free(old);\
984                 talloc_free(new);\
985         }\
986 } while (0)
987
988 /** Convert vp_map_t to VALUE_PAIR(s) and add them to a REQUEST.
989  *
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.
994  *
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
998  *      VALUE_PAIRS.
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.
1001  */
1002 int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t func, void *ctx)
1003 {
1004         int rcode = 0;
1005         int num;
1006         VALUE_PAIR **list, *vp, *dst, *head = NULL;
1007         bool found = false;
1008         REQUEST *context;
1009         TALLOC_CTX *parent;
1010         vp_cursor_t dst_list, src_list;
1011
1012         vp_map_t        exp_map;
1013         vp_tmpl_t       exp_lhs;
1014
1015         VERIFY_MAP(map);
1016         rad_assert(map->lhs != NULL);
1017         rad_assert(map->rhs != NULL);
1018
1019         /*
1020          *      Preprocessing of the LHS of the map.
1021          */
1022         switch (map->lhs->type) {
1023         /*
1024          *      Already in the correct form.
1025          */
1026         case TMPL_TYPE_LIST:
1027         case TMPL_TYPE_ATTR:
1028                 break;
1029
1030         /*
1031          *      Everything else gets expanded, then re-parsed as an
1032          *      attribute reference.
1033          */
1034         case TMPL_TYPE_XLAT:
1035         case TMPL_TYPE_XLAT_STRUCT:
1036         case TMPL_TYPE_EXEC:
1037         {
1038                 char *attr;
1039                 ssize_t slen;
1040
1041                 slen = tmpl_aexpand(request, &attr, request, map->lhs, NULL, NULL);
1042                 if (slen <= 0) {
1043                         REDEBUG("Left side \"%.*s\" of map failed expansion", (int)map->lhs->len, map->lhs->name);
1044                         rad_assert(!attr);
1045                         return -1;
1046                 }
1047
1048                 slen = tmpl_from_attr_str(&exp_lhs, attr, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) ;
1049                 if (slen <= 0) {
1050                         REDEBUG("Left side \"%.*s\" expansion not an attribute reference: %s",
1051                                 (int)map->lhs->len, map->lhs->name, fr_strerror());
1052                         talloc_free(attr);
1053                         return -1;
1054                 }
1055                 rad_assert((exp_lhs.type == TMPL_TYPE_ATTR) || (exp_lhs.type == TMPL_TYPE_LIST));
1056
1057                 memcpy(&exp_map, map, sizeof(exp_map));
1058                 exp_map.lhs = &exp_lhs;
1059                 map = &exp_map;
1060         }
1061                 break;
1062
1063         default:
1064                 rad_assert(0);
1065                 break;
1066         }
1067
1068
1069         /*
1070          *      Sanity check inputs.  We can have a list or attribute
1071          *      as a destination.
1072          */
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>"));
1078                 return -2;
1079         }
1080
1081         context = request;
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);
1085                 return -2;
1086         }
1087
1088         /*
1089          *      If there's no CoA packet and we're updating it,
1090          *      auto-allocate it.
1091          */
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");
1096                         return -2;
1097                 }
1098                 context->coa->proxy->code = (map->lhs->tmpl_list == PAIR_LIST_COA) ?
1099                                             PW_CODE_COA_REQUEST :
1100                                             PW_CODE_DISCONNECT_REQUEST;
1101         }
1102
1103         list = radius_list(context, map->lhs->tmpl_list);
1104         if (!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);
1107
1108                 return -2;
1109         }
1110
1111         parent = radius_list_ctx(context, map->lhs->tmpl_list);
1112         rad_assert(parent);
1113
1114         /*
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
1118          *      VPs to work with.
1119          */
1120         if (map->rhs->type != TMPL_TYPE_NULL) {
1121                 rcode = func(parent, &head, request, map, ctx);
1122                 if (rcode < 0) {
1123                         rad_assert(!head);
1124                         return rcode;
1125                 }
1126                 if (!head) {
1127                         RDEBUG2("No attributes updated");
1128                         return rcode;
1129                 }
1130         } else {
1131                 if (rad_debug_lvl) map_debug_log(request, map, NULL);
1132         }
1133
1134         /*
1135          *      Print the VPs
1136          */
1137         for (vp = fr_cursor_init(&src_list, &head);
1138              vp;
1139              vp = fr_cursor_next(&src_list)) {
1140                 VERIFY_VP(vp);
1141
1142                 if (rad_debug_lvl) map_debug_log(request, map, vp);
1143         }
1144
1145         /*
1146          *      The destination is a list (which is a completely different set of operations)
1147          */
1148         if (map->lhs->type == TMPL_TYPE_LIST) {
1149                 switch (map->op) {
1150                 case T_OP_CMP_FALSE:
1151                         /* We don't need the src VPs (should just be 'ANY') */
1152                         rad_assert(!head);
1153
1154                         /* Clear the entire dst list */
1155                         fr_pair_list_free(list);
1156
1157                         if (map->lhs->tmpl_list == PAIR_LIST_REQUEST) {
1158                                 context->username = NULL;
1159                                 context->password = NULL;
1160                         }
1161                         return 0;
1162
1163                 case T_OP_SET:
1164                         if (map->rhs->type == TMPL_TYPE_LIST) {
1165                                 fr_pair_list_free(list);
1166                                 *list = head;
1167                                 head = NULL;
1168                         } else {
1169                 case T_OP_EQ:
1170                                 rad_assert(map->rhs->type == TMPL_TYPE_EXEC);
1171                 case T_OP_ADD:
1172                                 fr_pair_list_move(parent, list, &head);
1173                                 fr_pair_list_free(&head);
1174                         }
1175                         goto finish;
1176
1177                 default:
1178                         fr_pair_list_free(&head);
1179                         return -1;
1180                 }
1181         }
1182
1183         /*
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).
1187          */
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;
1193                 }
1194         } else {
1195                 dst = fr_cursor_next_by_da(&dst_list, map->lhs->tmpl_da, map->lhs->tmpl_tag);
1196         }
1197         rad_assert(!dst || (map->lhs->tmpl_da == dst->da));
1198
1199         /*
1200          *      The destination is an attribute
1201          */
1202         switch (map->op) {
1203         default:
1204                 break;
1205         /*
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.
1208          */
1209         case T_OP_CMP_FALSE:
1210                 /* We don't need the src VPs (should just be 'ANY') */
1211                 rad_assert(!head);
1212                 if (!dst) return 0;
1213
1214                 /*
1215                  *      Wildcard: delete all of the matching ones, based on tag.
1216                  */
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);
1219                         dst = NULL;
1220                 /*
1221                  *      We've found the Nth one.  Delete it, and only it.
1222                  */
1223                 } else {
1224                         dst = fr_cursor_remove(&dst_list);
1225                         fr_pair_list_free(&dst);
1226                 }
1227
1228                 /*
1229                  *      Check that the User-Name and User-Password
1230                  *      caches point to the correct attribute.
1231                  */
1232                 goto finish;
1233
1234         /*
1235          *      -= - Delete attributes in the dst list which match any of the
1236          *      src_list attributes.
1237          *
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.
1243          */
1244         case T_OP_SUB:
1245                 /* We didn't find any attributes earlier */
1246                 if (!dst) {
1247                         fr_pair_list_free(&head);
1248                         return 0;
1249                 }
1250
1251                 /*
1252                  *      Instance specific[n] delete
1253                  */
1254                 if (map->lhs->tmpl_num != NUM_ANY) {
1255                         for (vp = fr_cursor_first(&src_list);
1256                              vp;
1257                              vp = fr_cursor_next(&src_list)) {
1258                                 head->op = T_OP_CMP_EQ;
1259                                 rcode = radius_compare_vps(request, vp, dst);
1260                                 if (rcode == 0) {
1261                                         dst = fr_cursor_remove(&dst_list);
1262                                         fr_pair_list_free(&dst);
1263                                         found = true;
1264                                 }
1265                         }
1266                         fr_pair_list_free(&head);
1267                         if (!found) return 0;
1268                         goto finish;
1269                 }
1270
1271                 /*
1272                  *      All instances[*] delete
1273                  */
1274                 for (dst = fr_cursor_current(&dst_list);
1275                      dst;
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);
1278                              vp;
1279                              vp = fr_cursor_next(&src_list)) {
1280                                 head->op = T_OP_CMP_EQ;
1281                                 rcode = radius_compare_vps(request, vp, dst);
1282                                 if (rcode == 0) {
1283                                         dst = fr_cursor_remove(&dst_list);
1284                                         fr_pair_list_free(&dst);
1285                                         found = true;
1286                                 }
1287                         }
1288                 }
1289                 fr_pair_list_free(&head);
1290                 if (!found) return 0;
1291                 goto finish;
1292         }
1293
1294         /*
1295          *      Another fixup pass to set tags on attributes were about to insert
1296          */
1297         if (map->lhs->tmpl_tag != TAG_ANY) {
1298                 for (vp = fr_cursor_init(&src_list, &head);
1299                      vp;
1300                      vp = fr_cursor_next(&src_list)) {
1301                         vp->tag = map->lhs->tmpl_tag;
1302                 }
1303         }
1304
1305         switch (map->op) {
1306         /*
1307          *      = - Set only if not already set
1308          */
1309         case T_OP_EQ:
1310                 if (dst) {
1311                         RDEBUG3("Refusing to overwrite (use :=)");
1312                         fr_pair_list_free(&head);
1313                         return 0;
1314                 }
1315
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);
1321                 break;
1322
1323         /*
1324          *      := - Overwrite existing attribute with last src_list attribute
1325          */
1326         case T_OP_SET:
1327                 /* Wind to last instance */
1328                 fr_cursor_last(&src_list);
1329                 if (dst) {
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);
1333                 } else {
1334                         fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
1335                 }
1336                 /* Free any we didn't insert */
1337                 fr_pair_list_free(&head);
1338                 break;
1339
1340         /*
1341          *      += - Add all src_list attributes to the destination
1342          */
1343         case T_OP_ADD:
1344                 /* Insert all the instances! (if multiple) */
1345                 fr_pair_add(list, head);
1346                 head = NULL;
1347                 break;
1348
1349         /*
1350          *      Filter operators
1351          */
1352         case T_OP_REG_NE:
1353         case T_OP_NE:
1354         case T_OP_REG_EQ:
1355         case T_OP_CMP_EQ:
1356         case T_OP_GE:
1357         case T_OP_GT:
1358         case T_OP_LE:
1359         case T_OP_LT:
1360         {
1361                 VALUE_PAIR *a, *b;
1362
1363                 fr_pair_list_sort(&head, fr_pair_cmp_by_da_tag);
1364                 fr_pair_list_sort(list, fr_pair_cmp_by_da_tag);
1365
1366                 fr_cursor_first(&dst_list);
1367
1368                 for (b = fr_cursor_first(&src_list);
1369                      b;
1370                      b = fr_cursor_next(&src_list)) {
1371                         for (a = fr_cursor_current(&dst_list);
1372                              a;
1373                              a = fr_cursor_next(&dst_list)) {
1374                                 int8_t cmp;
1375
1376                                 cmp = fr_pair_cmp_by_da_tag(a, b);      /* attribute and tag match */
1377                                 if (cmp > 0) break;
1378                                 else if (cmp < 0) continue;
1379
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);
1381                                 if (cmp != 0) {
1382                                         a = fr_cursor_remove(&dst_list);
1383                                         talloc_free(a);
1384                                 }
1385                         }
1386                         if (!a) break;  /* end of the list */
1387                 }
1388                 fr_pair_list_free(&head);
1389         }
1390                 break;
1391
1392         default:
1393                 rad_assert(0);  /* Should have been caught be the caller */
1394                 return -1;
1395         }
1396
1397 finish:
1398         rad_assert(!head);
1399
1400         /*
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.
1404          *
1405          *      TBH, we should probably make each module just do the
1406          *      search themselves.
1407          */
1408         if (map->lhs->tmpl_list == PAIR_LIST_REQUEST) {
1409                 context->username = NULL;
1410                 context->password = NULL;
1411
1412                 for (vp = fr_cursor_init(&src_list, list);
1413                      vp;
1414                      vp = fr_cursor_next(&src_list)) {
1415
1416                         if (vp->da->vendor != 0) continue;
1417                         if (vp->da->flags.has_tag) continue;
1418
1419                         if (!context->username && (vp->da->attr == PW_USER_NAME)) {
1420                                 context->username = vp;
1421                                 continue;
1422                         }
1423
1424                         if (vp->da->attr == PW_STRIPPED_USER_NAME) {
1425                                 context->username = vp;
1426                                 continue;
1427                         }
1428
1429                         if (vp->da->attr == PW_USER_PASSWORD) {
1430                                 context->password = vp;
1431                                 continue;
1432                         }
1433                 }
1434         }
1435         return 0;
1436 }
1437
1438 /** Check whether the destination of a map is currently valid
1439  *
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.
1443  */
1444 bool map_dst_valid(REQUEST *request, vp_map_t const *map)
1445 {
1446         REQUEST *context = request;
1447
1448         VERIFY_MAP(map);
1449
1450         if (radius_request(&context, map->lhs->tmpl_request) < 0) return false;
1451         if (!radius_list(context, map->lhs->tmpl_list)) return false;
1452
1453         return true;
1454 }
1455
1456 /**  Print a map to a string
1457  *
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
1462  */
1463 size_t map_prints(char *buffer, size_t bufsize, vp_map_t const *map)
1464 {
1465         size_t len;
1466         DICT_ATTR const *da = NULL;
1467         char *p = buffer;
1468         char *end = buffer + bufsize;
1469
1470         VERIFY_MAP(map);
1471
1472         if (map->lhs->type == TMPL_TYPE_ATTR) da = map->lhs->tmpl_da;
1473
1474         len = tmpl_prints(buffer, bufsize, map->lhs, da);
1475         p += len;
1476
1477         *(p++) = ' ';
1478         strlcpy(p, fr_token_name(map->op), end - p);
1479         p += strlen(p);
1480         *(p++) = ' ';
1481
1482         /*
1483          *      The RHS doesn't matter for many operators
1484          */
1485         if ((map->op == T_OP_CMP_TRUE) ||
1486             (map->op == T_OP_CMP_FALSE)) {
1487                 strlcpy(p, "ANY", (end - p));
1488                 p += strlen(p);
1489                 return p - buffer;
1490         }
1491
1492         rad_assert(map->rhs != NULL);
1493
1494         if ((map->lhs->type == TMPL_TYPE_ATTR) &&
1495             (map->lhs->tmpl_da->type == PW_TYPE_STRING) &&
1496             (map->rhs->type == TMPL_TYPE_LITERAL)) {
1497                 *(p++) = '\'';
1498                 len = tmpl_prints(p, end - p, map->rhs, da);
1499                 p += len;
1500                 *(p++) = '\'';
1501                 *p = '\0';
1502         } else {
1503                 len = tmpl_prints(p, end - p, map->rhs, da);
1504                 p += len;
1505         }
1506
1507         return p - buffer;
1508 }
1509
1510 /*
1511  *      Debug print a map / VP
1512  */
1513 void map_debug_log(REQUEST *request, vp_map_t const *map, VALUE_PAIR const *vp)
1514 {
1515         char *value;
1516         char buffer[1024];
1517
1518         VERIFY_MAP(map);
1519         rad_assert(map->lhs != NULL);
1520         rad_assert(map->rhs != NULL);
1521
1522         rad_assert(vp || (map->rhs->type == TMPL_TYPE_NULL));
1523
1524         switch (map->rhs->type) {
1525         /*
1526          *      Just print the value being assigned
1527          */
1528         default:
1529         case TMPL_TYPE_LITERAL:
1530                 vp_prints_value(buffer, sizeof(buffer), vp, map->rhs->quote);
1531                 value = buffer;
1532                 break;
1533
1534         case TMPL_TYPE_XLAT:
1535         case TMPL_TYPE_XLAT_STRUCT:
1536                 vp_prints_value(buffer, sizeof(buffer), vp, map->rhs->quote);
1537                 value = buffer;
1538                 break;
1539
1540         case TMPL_TYPE_DATA:
1541                 vp_prints_value(buffer, sizeof(buffer), vp, map->rhs->quote);
1542                 value = buffer;
1543                 break;
1544
1545         /*
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
1548          *      copying.
1549          */
1550         case TMPL_TYPE_LIST:
1551         {
1552                 char            attr[256];
1553                 char            quote = '\0';
1554                 vp_tmpl_t       vpt;
1555                 /*
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.
1559                  */
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;
1564
1565                 /*
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.
1569                  */
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);
1575         }
1576                 break;
1577
1578         case TMPL_TYPE_ATTR:
1579         {
1580                 char quote = '\0';
1581
1582                 /*
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.
1586                  */
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);
1591         }
1592                 break;
1593
1594         case TMPL_TYPE_NULL:
1595                 strcpy(buffer, "ANY");
1596                 value = buffer;
1597                 break;
1598         }
1599
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);
1604                 break;
1605
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);
1609                 break;
1610
1611         default:
1612                 break;
1613         }
1614
1615         if (value != buffer) talloc_free(value);
1616 }