88d89c19130e70b4c0ef6504947bbdf21556fc44
[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 #if 0
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         if (!cs) return 0;
344
345         /*
346          *      The first map has cs as the parent.
347          *      The rest have the previous map as the parent.
348          */
349         parent = cs;
350
351         ci = cf_section_to_item(cs);
352
353         cs_list = p = cf_section_name2(cs);
354         if (cs_list) {
355                 p += radius_request_name(&request_def, p, REQUEST_CURRENT);
356                 if (request_def == REQUEST_UNKNOWN) {
357                         cf_log_err(ci, "Default request specified in mapping section is invalid");
358                         return -1;
359                 }
360
361                 dst_list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN);
362                 if (dst_list_def == PAIR_LIST_UNKNOWN) {
363                         cf_log_err(ci, "Default list \"%s\" specified "
364                                    "in mapping section is invalid", p);
365                         return -1;
366                 }
367         }
368
369         for (ci = cf_item_find_next(cs, NULL);
370              ci != NULL;
371              ci = cf_item_find_next(cs, ci)) {
372                 if (total++ == max) {
373                         cf_log_err(ci, "Map size exceeded");
374                 error:
375                         TALLOC_FREE(*out);
376                         return -1;
377                 }
378
379                 if (!cf_item_is_pair(ci)) {
380                         cf_log_err(ci, "Entry is not in \"attribute = value\" format");
381                         goto error;
382                 }
383
384                 cp = cf_item_to_pair(ci);
385                 if (map_afrom_cp(parent, &map, cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def) < 0) {
386                         goto error;
387                 }
388
389                 VERIFY_MAP(map);
390
391                 /*
392                  *      Check the types in the map are valid
393                  */
394                 if (validate && (validate(map, ctx) < 0)) goto error;
395
396                 parent = *tail = map;
397                 tail = &(map->next);
398         }
399
400         return 0;
401
402 }
403
404
405 /** Convert strings to vp_map_t
406  *
407  * Treatment of operands depends on quotation, barewords are treated
408  * as attribute references, double quoted values are treated as
409  * expandable strings, single quoted values are treated as literal
410  * strings.
411  *
412  * Return must be freed with talloc_free
413  *
414  * @param[in] ctx for talloc
415  * @param[out] out Where to store the head of the map.
416  * @param[in] lhs of the operation
417  * @param[in] lhs_type type of the LHS string
418  * @param[in] op the operation to perform
419  * @param[in] rhs of the operation
420  * @param[in] rhs_type type of the RHS string
421  * @param[in] dst_request_def The default request to insert unqualified
422  *      attributes into.
423  * @param[in] dst_list_def The default list to insert unqualified attributes
424  *      into.
425  * @param[in] src_request_def The default request to resolve attribute
426  *      references in.
427  * @param[in] src_list_def The default list to resolve unqualified attributes
428  *      in.
429  * @return vp_map_t if successful or NULL on error.
430  */
431 int map_afrom_fields(TALLOC_CTX *ctx, vp_map_t **out, char const *lhs, FR_TOKEN lhs_type,
432                      FR_TOKEN op, char const *rhs, FR_TOKEN rhs_type,
433                      request_refs_t dst_request_def,
434                      pair_lists_t dst_list_def,
435                      request_refs_t src_request_def,
436                      pair_lists_t src_list_def)
437 {
438         ssize_t slen;
439         vp_map_t *map;
440
441         map = talloc_zero(ctx, vp_map_t);
442
443         slen = tmpl_afrom_str(map, &map->lhs, lhs, strlen(lhs), lhs_type, dst_request_def, dst_list_def, true);
444         if (slen < 0) {
445         error:
446                 talloc_free(map);
447                 return -1;
448         }
449
450         map->op = op;
451
452         if ((map->lhs->type == TMPL_TYPE_ATTR) &&
453             map->lhs->tmpl_da->flags.is_unknown &&
454             map_cast_from_hex(map, rhs_type, rhs)) {
455                 return 0;
456         }
457
458         slen = tmpl_afrom_str(map, &map->rhs, rhs, strlen(rhs), rhs_type, src_request_def, src_list_def, true);
459         if (slen < 0) goto error;
460
461         VERIFY_MAP(map);
462
463         *out = map;
464
465         return 0;
466 }
467
468 /** Convert a value pair string to valuepair map
469  *
470  * Takes a valuepair string with list and request qualifiers and converts it into a
471  * vp_map_t.
472  *
473  * @param ctx where to allocate the map.
474  * @param out Where to write the new map (must be freed with talloc_free()).
475  * @param vp_str string to parse.
476  * @param dst_request_def to use if attribute isn't qualified.
477  * @param dst_list_def to use if attribute isn't qualified.
478  * @param src_request_def to use if attribute isn't qualified.
479  * @param src_list_def to use if attribute isn't qualified.
480  * @return 0 on success, < 0 on error.
481  */
482 int map_afrom_attr_str(TALLOC_CTX *ctx, vp_map_t **out, char const *vp_str,
483                        request_refs_t dst_request_def, pair_lists_t dst_list_def,
484                        request_refs_t src_request_def, pair_lists_t src_list_def)
485 {
486         char const *p = vp_str;
487         FR_TOKEN quote;
488
489         VALUE_PAIR_RAW raw;
490         vp_map_t *map = NULL;
491
492         quote = gettoken(&p, raw.l_opand, sizeof(raw.l_opand), false);
493         switch (quote) {
494         case T_BARE_WORD:
495                 break;
496
497         case T_INVALID:
498         error:
499                 return -1;
500
501         default:
502                 fr_strerror_printf("Left operand must be an attribute");
503                 return -1;
504         }
505
506         raw.op = getop(&p);
507         if (raw.op == T_INVALID) goto error;
508
509         raw.quote = gettoken(&p, raw.r_opand, sizeof(raw.r_opand), false);
510         if (raw.quote == T_INVALID) goto error;
511         if (!fr_str_tok[raw.quote]) {
512                 fr_strerror_printf("Right operand must be an attribute or string");
513                 return -1;
514         }
515
516         if (map_afrom_fields(ctx, &map, raw.l_opand, T_BARE_WORD, raw.op, raw.r_opand, raw.quote,
517                              dst_request_def, dst_list_def, src_request_def, src_list_def) < 0) {
518                 return -1;
519         }
520
521         rad_assert(map != NULL);
522         *out = map;
523
524         VERIFY_MAP(map);
525
526         return 0;
527 }
528
529 /** Compare map where LHS is #TMPL_TYPE_ATTR
530  *
531  * Compares maps by lhs->tmpl_da, lhs->tmpl_tag, lhs->tmpl_num
532  *
533  * @note both map->lhs must be #TMPL_TYPE_ATTR.
534  *
535  * @param a first map.
536  * @param b second map.
537  */
538 int8_t map_cmp_by_lhs_attr(void const *a, void const *b)
539 {
540         vp_tmpl_t const *my_a = ((vp_map_t const *)a)->lhs;
541         vp_tmpl_t const *my_b = ((vp_map_t const *)b)->lhs;
542
543         VERIFY_TMPL(my_a);
544         VERIFY_TMPL(my_b);
545
546         uint8_t cmp;
547
548         rad_assert(my_a->type == TMPL_TYPE_ATTR);
549         rad_assert(my_b->type == TMPL_TYPE_ATTR);
550
551         cmp = fr_pointer_cmp(my_a->tmpl_da, my_b->tmpl_da);
552         if (cmp != 0) return cmp;
553
554         if (my_a->tmpl_tag < my_b->tmpl_tag) return -1;
555
556         if (my_a->tmpl_tag > my_b->tmpl_tag) return 1;
557
558         if (my_a->tmpl_num < my_b->tmpl_num) return -1;
559
560         if (my_a->tmpl_num > my_b->tmpl_num) return 1;
561
562         return 0;
563 }
564
565 static void map_sort_split(vp_map_t *source, vp_map_t **front, vp_map_t **back)
566 {
567         vp_map_t *fast;
568         vp_map_t *slow;
569
570         /*
571          *      Stopping condition - no more elements left to split
572          */
573         if (!source || !source->next) {
574                 *front = source;
575                 *back = NULL;
576
577                 return;
578         }
579
580         /*
581          *      Fast advances twice as fast as slow, so when it gets to the end,
582          *      slow will point to the middle of the linked list.
583          */
584         slow = source;
585         fast = source->next;
586
587         while (fast) {
588                 fast = fast->next;
589                 if (fast) {
590                         slow = slow->next;
591                         fast = fast->next;
592                 }
593         }
594
595         *front = source;
596         *back = slow->next;
597         slow->next = NULL;
598 }
599
600 static vp_map_t *map_sort_merge(vp_map_t *a, vp_map_t *b, fr_cmp_t cmp)
601 {
602         vp_map_t *result = NULL;
603
604         if (!a) return b;
605         if (!b) return a;
606
607         /*
608          *      Compare things in the maps
609          */
610         if (cmp(a, b) <= 0) {
611                 result = a;
612                 result->next = map_sort_merge(a->next, b, cmp);
613         } else {
614                 result = b;
615                 result->next = map_sort_merge(a, b->next, cmp);
616         }
617
618         return result;
619 }
620
621 /** Sort a linked list of #vp_map_t using merge sort
622  *
623  * @param[in,out] maps List of #vp_map_t to sort.
624  * @param[in] cmp to sort with
625  */
626 void map_sort(vp_map_t **maps, fr_cmp_t cmp)
627 {
628         vp_map_t *head = *maps;
629         vp_map_t *a;
630         vp_map_t *b;
631
632         /*
633          *      If there's 0-1 elements it must already be sorted.
634          */
635         if (!head || !head->next) {
636                 return;
637         }
638
639         map_sort_split(head, &a, &b);   /* Split into sublists */
640         map_sort(&a, cmp);              /* Traverse left */
641         map_sort(&b, cmp);              /* Traverse right */
642
643         /*
644          *      merge the two sorted lists together
645          */
646         *maps = map_sort_merge(a, b, cmp);
647 }
648
649 /** Process map which has exec as a src
650  *
651  * Evaluate maps which specify exec as a src. This may be used by various sorts of update sections,
652  * and so has been broken out into it's own function.
653  *
654  * @param[in,out] ctx to allocate new #VALUE_PAIR (s) in.
655  * @param[out] out Where to write the #VALUE_PAIR (s).
656  * @param[in] request structure (used only for talloc).
657  * @param[in] map the map. The LHS (dst) must be TMPL_TYPE_ATTR or TMPL_TYPE_LIST. The RHS (src)
658  *      must be TMPL_TYPE_EXEC.
659  * @return -1 on failure, 0 on success.
660  */
661 static int map_exec_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map)
662 {
663         int result;
664         char *expanded = NULL;
665         char answer[1024];
666         VALUE_PAIR **input_pairs = NULL;
667         VALUE_PAIR *output_pairs = NULL;
668
669         *out = NULL;
670
671         VERIFY_MAP(map);
672
673         rad_assert(map->rhs->type == TMPL_TYPE_EXEC);
674         rad_assert((map->lhs->type == TMPL_TYPE_ATTR) || (map->lhs->type == TMPL_TYPE_LIST));
675
676         /*
677          *      We always put the request pairs into the environment
678          */
679         input_pairs = radius_list(request, PAIR_LIST_REQUEST);
680
681         /*
682          *      Automagically switch output type depending on our destination
683          *      If dst is a list, then we create attributes from the output of the program
684          *      if dst is an attribute, then we create an attribute of that type and then
685          *      call fr_pair_value_from_str on the output of the script.
686          */
687         result = radius_exec_program(ctx, answer, sizeof(answer),
688                                      (map->lhs->type == TMPL_TYPE_LIST) ? &output_pairs : NULL,
689                                      request, map->rhs->name, input_pairs ? *input_pairs : NULL,
690                                      true, true, EXEC_TIMEOUT);
691         talloc_free(expanded);
692         if (result != 0) {
693                 talloc_free(output_pairs);
694                 return -1;
695         }
696
697         switch (map->lhs->type) {
698         case TMPL_TYPE_LIST:
699                 if (!output_pairs) {
700                         REDEBUG("No valid attributes received from program");
701                         return -2;
702                 }
703                 *out = output_pairs;
704                 return 0;
705
706         case TMPL_TYPE_ATTR:
707         {
708                 VALUE_PAIR *vp;
709
710                 vp = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
711                 if (!vp) return -1;
712                 vp->op = map->op;
713                 vp->tag = map->lhs->tmpl_tag;
714                 if (fr_pair_value_from_str(vp, answer, -1) < 0) {
715                         fr_pair_list_free(&vp);
716                         return -2;
717                 }
718                 *out = vp;
719
720                 return 0;
721         }
722
723         default:
724                 rad_assert(0);
725         }
726
727         return -1;
728 }
729
730 /** Convert a map to a VALUE_PAIR.
731  *
732  * @param[in,out] ctx to allocate #VALUE_PAIR (s) in.
733  * @param[out] out Where to write the #VALUE_PAIR (s), which may be NULL if not found
734  * @param[in] request The current request.
735  * @param[in] map the map. The LHS (dst) has to be #TMPL_TYPE_ATTR or #TMPL_TYPE_LIST.
736  * @param[in] uctx unused.
737  * @return
738  *      - 0 on success.
739  *      - -1 on failure.
740  */
741 int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, UNUSED void *uctx)
742 {
743         int rcode = 0;
744         ssize_t len;
745         VALUE_PAIR *vp = NULL, *new, *found = NULL;
746         REQUEST *context = request;
747         vp_cursor_t cursor;
748         ssize_t slen;
749         char *str;
750
751         *out = NULL;
752
753         VERIFY_MAP(map);
754         rad_assert(map->lhs != NULL);
755         rad_assert(map->rhs != NULL);
756
757         rad_assert((map->lhs->type == TMPL_TYPE_LIST) || (map->lhs->type == TMPL_TYPE_ATTR));
758
759         /*
760          *      Special case for !*, we don't need to parse RHS as this is a unary operator.
761          */
762         if (map->op == T_OP_CMP_FALSE) return 0;
763
764         /*
765          *      List to list found, this is a special case because we don't need
766          *      to allocate any attributes, just finding the current list, and change
767          *      the op.
768          */
769         if ((map->lhs->type == TMPL_TYPE_LIST) && (map->rhs->type == TMPL_TYPE_LIST)) {
770                 VALUE_PAIR **from = NULL;
771
772                 if (radius_request(&context, map->rhs->tmpl_request) == 0) {
773                         from = radius_list(context, map->rhs->tmpl_list);
774                 }
775                 if (!from) return 0;
776
777                 found = fr_pair_list_copy(ctx, *from);
778
779                 /*
780                  *      List to list copy is empty if the src list has no attributes.
781                  */
782                 if (!found) return 0;
783
784                 for (vp = fr_cursor_init(&cursor, &found);
785                      vp;
786                      vp = fr_cursor_next(&cursor)) {
787                         vp->op = T_OP_ADD;
788                 }
789
790                 *out = found;
791
792                 return 0;
793         }
794
795         /*
796          *      And parse the RHS
797          */
798         switch (map->rhs->type) {
799         case TMPL_TYPE_XLAT_STRUCT:
800                 rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
801                 rad_assert(map->lhs->tmpl_da);  /* We need to know which attribute to create */
802                 rad_assert(map->rhs->tmpl_xlat != NULL);
803
804                 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
805                 if (!new) return -1;
806
807                 str = NULL;
808                 slen = radius_axlat_struct(&str, request, map->rhs->tmpl_xlat, NULL, NULL);
809                 if (slen < 0) {
810                         rcode = slen;
811                         goto error;
812                 }
813
814                 /*
815                  *      We do the debug printing because radius_axlat_struct
816                  *      doesn't have access to the original string.  It's been
817                  *      mangled during the parsing to xlat_exp_t
818                  */
819                 RDEBUG2("EXPAND %s", map->rhs->name);
820                 RDEBUG2("   --> %s", str);
821
822                 rcode = fr_pair_value_from_str(new, str, -1);
823                 talloc_free(str);
824                 if (rcode < 0) {
825                         fr_pair_list_free(&new);
826                         goto error;
827                 }
828                 new->op = map->op;
829                 new->tag = map->lhs->tmpl_tag;
830                 *out = new;
831                 break;
832
833         case TMPL_TYPE_XLAT:
834                 rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
835                 rad_assert(map->lhs->tmpl_da);  /* We need to know which attribute to create */
836
837                 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
838                 if (!new) return -1;
839
840                 str = NULL;
841                 slen = radius_axlat(&str, request, map->rhs->name, NULL, NULL);
842                 if (slen < 0) {
843                         rcode = slen;
844                         goto error;
845                 }
846
847                 rcode = fr_pair_value_from_str(new, str, -1);
848                 talloc_free(str);
849                 if (rcode < 0) {
850                         fr_pair_list_free(&new);
851                         goto error;
852                 }
853                 new->op = map->op;
854                 new->tag = map->lhs->tmpl_tag;
855                 *out = new;
856                 break;
857
858         case TMPL_TYPE_LITERAL:
859                 rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
860                 rad_assert(map->lhs->tmpl_da);  /* We need to know which attribute to create */
861
862                 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
863                 if (!new) return -1;
864
865                 if (fr_pair_value_from_str(new, map->rhs->name, -1) < 0) {
866                         rcode = 0;
867                         goto error;
868                 }
869                 new->op = map->op;
870                 new->tag = map->lhs->tmpl_tag;
871                 *out = new;
872                 break;
873
874         case TMPL_TYPE_ATTR:
875         {
876                 vp_cursor_t from;
877
878                 rad_assert(((map->lhs->type == TMPL_TYPE_ATTR) && map->lhs->tmpl_da) ||
879                            ((map->lhs->type == TMPL_TYPE_LIST) && !map->lhs->tmpl_da));
880
881                 /*
882                  * @todo should log error, and return -1 for v3.1 (causes update to fail)
883                  */
884                 if (tmpl_copy_vps(ctx, &found, request, map->rhs) < 0) return 0;
885
886                 vp = fr_cursor_init(&from, &found);
887
888                 /*
889                  *  Src/Dst attributes don't match, convert src attributes
890                  *  to match dst.
891                  */
892                 if ((map->lhs->type == TMPL_TYPE_ATTR) &&
893                     (map->rhs->tmpl_da->type != map->lhs->tmpl_da->type)) {
894                         vp_cursor_t to;
895
896                         (void) fr_cursor_init(&to, out);
897                         for (; vp; vp = fr_cursor_next(&from)) {
898                                 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
899                                 if (!new) return -1;
900
901                                 len = value_data_cast(new, &new->data, new->da->type, new->da,
902                                                       vp->da->type, vp->da, &vp->data, vp->vp_length);
903                                 if (len < 0) {
904                                         REDEBUG("Attribute conversion failed: %s", fr_strerror());
905                                         fr_pair_list_free(&found);
906                                         fr_pair_list_free(&new);
907                                         return -1;
908                                 }
909
910                                 new->vp_length = len;
911                                 vp = fr_cursor_remove(&from);
912                                 talloc_free(vp);
913
914                                 if (new->da->type == PW_TYPE_STRING) {
915                                         rad_assert(new->vp_strvalue != NULL);
916                                 }
917
918                                 new->op = map->op;
919                                 new->tag = map->lhs->tmpl_tag;
920                                 fr_cursor_insert(&to, new);
921                         }
922                         return 0;
923                 }
924
925                 /*
926                  *   Otherwise we just need to fixup the attribute types
927                  *   and operators
928                  */
929                 for (; vp; vp = fr_cursor_next(&from)) {
930                         vp->da = map->lhs->tmpl_da;
931                         vp->op = map->op;
932                         vp->tag = map->lhs->tmpl_tag;
933                 }
934                 *out = found;
935         }
936                 break;
937
938         case TMPL_TYPE_DATA:
939                 rad_assert(map->lhs->tmpl_da);
940                 rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
941                 rad_assert(map->lhs->tmpl_da->type == map->rhs->tmpl_data_type);
942
943                 new = fr_pair_afrom_da(ctx, map->lhs->tmpl_da);
944                 if (!new) return -1;
945
946                 len = value_data_copy(new, &new->data, new->da->type, &map->rhs->tmpl_data_value,
947                                       map->rhs->tmpl_data_length);
948                 if (len < 0) goto error;
949
950                 new->vp_length = len;
951                 new->op = map->op;
952                 new->tag = map->lhs->tmpl_tag;
953                 *out = new;
954
955                 VERIFY_MAP(map);
956                 break;
957
958         /*
959          *      This essentially does the same as rlm_exec xlat, except it's non-configurable.
960          *      It's only really here as a convenience for people who expect the contents of
961          *      backticks to be executed in a shell.
962          *
963          *      exec string is xlat expanded and arguments are shell escaped.
964          */
965         case TMPL_TYPE_EXEC:
966                 return map_exec_to_vp(ctx, out, request, map);
967
968         default:
969                 rad_assert(0);  /* Should have been caught at parse time */
970
971         error:
972                 fr_pair_list_free(&vp);
973                 return rcode;
974         }
975
976         return 0;
977 }
978
979 #define DEBUG_OVERWRITE(_old, _new) \
980 do {\
981         if (RDEBUG_ENABLED3) {\
982                 char *old = vp_aprints_value(request, _old, '"');\
983                 char *new = vp_aprints_value(request, _new, '"');\
984                 RDEBUG3("Overwriting value \"%s\" with \"%s\"", old, new);\
985                 talloc_free(old);\
986                 talloc_free(new);\
987         }\
988 } while (0)
989
990 /** Convert vp_map_t to VALUE_PAIR(s) and add them to a REQUEST.
991  *
992  * Takes a single vp_map_t, resolves request and list identifiers
993  * to pointers in the current request, then attempts to retrieve module
994  * specific value(s) using callback, and adds the resulting values to the
995  * correct request/list.
996  *
997  * @param request The current request.
998  * @param map specifying destination attribute and location and src identifier.
999  * @param func to retrieve module specific values and convert them to
1000  *      VALUE_PAIRS.
1001  * @param ctx to be passed to func.
1002  * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success.
1003  */
1004 int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t func, void *ctx)
1005 {
1006         int rcode = 0;
1007         int num;
1008         VALUE_PAIR **list, *vp, *dst, *head = NULL;
1009         bool found = false;
1010         REQUEST *context;
1011         TALLOC_CTX *parent;
1012         vp_cursor_t dst_list, src_list;
1013
1014         vp_map_t        exp_map;
1015         vp_tmpl_t       exp_lhs;
1016
1017         VERIFY_MAP(map);
1018         rad_assert(map->lhs != NULL);
1019         rad_assert(map->rhs != NULL);
1020
1021         /*
1022          *      Preprocessing of the LHS of the map.
1023          */
1024         switch (map->lhs->type) {
1025         /*
1026          *      Already in the correct form.
1027          */
1028         case TMPL_TYPE_LIST:
1029         case TMPL_TYPE_ATTR:
1030                 break;
1031
1032         /*
1033          *      Everything else gets expanded, then re-parsed as an
1034          *      attribute reference.
1035          */
1036         case TMPL_TYPE_XLAT:
1037         case TMPL_TYPE_XLAT_STRUCT:
1038         case TMPL_TYPE_EXEC:
1039         {
1040                 char *attr;
1041                 ssize_t slen;
1042
1043                 slen = tmpl_aexpand(request, &attr, request, map->lhs, NULL, NULL);
1044                 if (slen <= 0) {
1045                         REDEBUG("Left side \"%.*s\" of map failed expansion", (int)map->lhs->len, map->lhs->name);
1046                         rad_assert(!attr);
1047                         return -1;
1048                 }
1049
1050                 slen = tmpl_from_attr_str(&exp_lhs, attr, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) ;
1051                 if (slen <= 0) {
1052                         REDEBUG("Left side \"%.*s\" expansion not an attribute reference: %s",
1053                                 (int)map->lhs->len, map->lhs->name, fr_strerror());
1054                         talloc_free(attr);
1055                         return -1;
1056                 }
1057                 rad_assert((exp_lhs.type == TMPL_TYPE_ATTR) || (exp_lhs.type == TMPL_TYPE_LIST));
1058
1059                 memcpy(&exp_map, map, sizeof(exp_map));
1060                 exp_map.lhs = &exp_lhs;
1061                 map = &exp_map;
1062         }
1063                 break;
1064
1065         default:
1066                 rad_assert(0);
1067                 break;
1068         }
1069
1070
1071         /*
1072          *      Sanity check inputs.  We can have a list or attribute
1073          *      as a destination.
1074          */
1075         if ((map->lhs->type != TMPL_TYPE_LIST) &&
1076             (map->lhs->type != TMPL_TYPE_ATTR)) {
1077                 REDEBUG("Left side \"%.*s\" of map should be an attr or list but is an %s",
1078                         (int)map->lhs->len, map->lhs->name,
1079                         fr_int2str(tmpl_names, map->lhs->type, "<INVALID>"));
1080                 return -2;
1081         }
1082
1083         context = request;
1084         if (radius_request(&context, map->lhs->tmpl_request) < 0) {
1085                 REDEBUG("Mapping \"%.*s\" -> \"%.*s\" invalid in this context",
1086                         (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name);
1087                 return -2;
1088         }
1089
1090         /*
1091          *      If there's no CoA packet and we're updating it,
1092          *      auto-allocate it.
1093          */
1094         if (((map->lhs->tmpl_list == PAIR_LIST_COA) ||
1095              (map->lhs->tmpl_list == PAIR_LIST_DM)) && !request->coa) {
1096                 if (!request_alloc_coa(context)) {
1097                         REDEBUG("Failed to create a CoA/Disconnect Request message");
1098                         return -2;
1099                 }
1100                 context->coa->proxy->code = (map->lhs->tmpl_list == PAIR_LIST_COA) ?
1101                                             PW_CODE_COA_REQUEST :
1102                                             PW_CODE_DISCONNECT_REQUEST;
1103         }
1104
1105         list = radius_list(context, map->lhs->tmpl_list);
1106         if (!list) {
1107                 REDEBUG("Mapping \"%.*s\" -> \"%.*s\" invalid in this context",
1108                         (int)map->rhs->len, map->rhs->name, (int)map->lhs->len, map->lhs->name);
1109
1110                 return -2;
1111         }
1112
1113         parent = radius_list_ctx(context, map->lhs->tmpl_list);
1114         rad_assert(parent);
1115
1116         /*
1117          *      The callback should either return -1 to signify operations error,
1118          *      -2 when it can't find the attribute or list being referenced, or
1119          *      0 to signify success. It may return "success", but still have no
1120          *      VPs to work with.
1121          */
1122         if (map->rhs->type != TMPL_TYPE_NULL) {
1123                 rcode = func(parent, &head, request, map, ctx);
1124                 if (rcode < 0) {
1125                         rad_assert(!head);
1126                         return rcode;
1127                 }
1128                 if (!head) {
1129                         RDEBUG2("No attributes updated");
1130                         return rcode;
1131                 }
1132         } else {
1133                 if (rad_debug_lvl) map_debug_log(request, map, NULL);
1134         }
1135
1136         /*
1137          *      Print the VPs
1138          */
1139         for (vp = fr_cursor_init(&src_list, &head);
1140              vp;
1141              vp = fr_cursor_next(&src_list)) {
1142                 VERIFY_VP(vp);
1143
1144                 if (rad_debug_lvl) map_debug_log(request, map, vp);
1145         }
1146
1147         /*
1148          *      The destination is a list (which is a completely different set of operations)
1149          */
1150         if (map->lhs->type == TMPL_TYPE_LIST) {
1151                 switch (map->op) {
1152                 case T_OP_CMP_FALSE:
1153                         /* We don't need the src VPs (should just be 'ANY') */
1154                         rad_assert(!head);
1155
1156                         /* Clear the entire dst list */
1157                         fr_pair_list_free(list);
1158
1159                         if (map->lhs->tmpl_list == PAIR_LIST_REQUEST) {
1160                                 context->username = NULL;
1161                                 context->password = NULL;
1162                         }
1163                         return 0;
1164
1165                 case T_OP_SET:
1166                         if (map->rhs->type == TMPL_TYPE_LIST) {
1167                                 fr_pair_list_free(list);
1168                                 *list = head;
1169                                 head = NULL;
1170                         } else {
1171                 case T_OP_EQ:
1172                                 rad_assert(map->rhs->type == TMPL_TYPE_EXEC);
1173                 case T_OP_ADD:
1174                                 fr_pair_list_move(parent, list, &head);
1175                                 fr_pair_list_free(&head);
1176                         }
1177                         goto finish;
1178
1179                 default:
1180                         fr_pair_list_free(&head);
1181                         return -1;
1182                 }
1183         }
1184
1185         /*
1186          *      Find the destination attribute.  We leave with either
1187          *      the dst_list and vp pointing to the attribute or the VP
1188          *      being NULL (no attribute at that index).
1189          */
1190         num = map->lhs->tmpl_num;
1191         (void) fr_cursor_init(&dst_list, list);
1192         if (num != NUM_ANY) {
1193                 while ((dst = fr_cursor_next_by_da(&dst_list, map->lhs->tmpl_da, map->lhs->tmpl_tag))) {
1194                         if (num-- == 0) break;
1195                 }
1196         } else {
1197                 dst = fr_cursor_next_by_da(&dst_list, map->lhs->tmpl_da, map->lhs->tmpl_tag);
1198         }
1199         rad_assert(!dst || (map->lhs->tmpl_da == dst->da));
1200
1201         /*
1202          *      The destination is an attribute
1203          */
1204         switch (map->op) {
1205         default:
1206                 break;
1207         /*
1208          *      !* - Remove all attributes which match dst in the specified list.
1209          *      This doesn't use attributes returned by the func(), and immediately frees them.
1210          */
1211         case T_OP_CMP_FALSE:
1212                 /* We don't need the src VPs (should just be 'ANY') */
1213                 rad_assert(!head);
1214                 if (!dst) return 0;
1215
1216                 /*
1217                  *      Wildcard: delete all of the matching ones, based on tag.
1218                  */
1219                 if (map->lhs->tmpl_num == NUM_ANY) {
1220                         fr_pair_delete_by_num(list, map->lhs->tmpl_da->attr, map->lhs->tmpl_da->vendor, map->lhs->tmpl_tag);
1221                         dst = NULL;
1222                 /*
1223                  *      We've found the Nth one.  Delete it, and only it.
1224                  */
1225                 } else {
1226                         dst = fr_cursor_remove(&dst_list);
1227                         fr_pair_list_free(&dst);
1228                 }
1229
1230                 /*
1231                  *      Check that the User-Name and User-Password
1232                  *      caches point to the correct attribute.
1233                  */
1234                 goto finish;
1235
1236         /*
1237          *      -= - Delete attributes in the dst list which match any of the
1238          *      src_list attributes.
1239          *
1240          *      This operation has two modes:
1241          *      - If map->lhs->tmpl_num > 0, we check each of the src_list attributes against
1242          *        the dst attribute, to see if any of their values match.
1243          *      - If map->lhs->tmpl_num == NUM_ANY, we compare all instances of the dst attribute
1244          *        against each of the src_list attributes.
1245          */
1246         case T_OP_SUB:
1247                 /* We didn't find any attributes earlier */
1248                 if (!dst) {
1249                         fr_pair_list_free(&head);
1250                         return 0;
1251                 }
1252
1253                 /*
1254                  *      Instance specific[n] delete
1255                  */
1256                 if (map->lhs->tmpl_num != NUM_ANY) {
1257                         for (vp = fr_cursor_first(&src_list);
1258                              vp;
1259                              vp = fr_cursor_next(&src_list)) {
1260                                 head->op = T_OP_CMP_EQ;
1261                                 rcode = radius_compare_vps(request, vp, dst);
1262                                 if (rcode == 0) {
1263                                         dst = fr_cursor_remove(&dst_list);
1264                                         fr_pair_list_free(&dst);
1265                                         found = true;
1266                                 }
1267                         }
1268                         fr_pair_list_free(&head);
1269                         if (!found) return 0;
1270                         goto finish;
1271                 }
1272
1273                 /*
1274                  *      All instances[*] delete
1275                  */
1276                 for (dst = fr_cursor_current(&dst_list);
1277                      dst;
1278                      dst = fr_cursor_next_by_da(&dst_list, map->lhs->tmpl_da, map->lhs->tmpl_tag)) {
1279                         for (vp = fr_cursor_first(&src_list);
1280                              vp;
1281                              vp = fr_cursor_next(&src_list)) {
1282                                 head->op = T_OP_CMP_EQ;
1283                                 rcode = radius_compare_vps(request, vp, dst);
1284                                 if (rcode == 0) {
1285                                         dst = fr_cursor_remove(&dst_list);
1286                                         fr_pair_list_free(&dst);
1287                                         found = true;
1288                                 }
1289                         }
1290                 }
1291                 fr_pair_list_free(&head);
1292                 if (!found) return 0;
1293                 goto finish;
1294         }
1295
1296         /*
1297          *      Another fixup pass to set tags on attributes were about to insert
1298          */
1299         if (map->lhs->tmpl_tag != TAG_ANY) {
1300                 for (vp = fr_cursor_init(&src_list, &head);
1301                      vp;
1302                      vp = fr_cursor_next(&src_list)) {
1303                         vp->tag = map->lhs->tmpl_tag;
1304                 }
1305         }
1306
1307         switch (map->op) {
1308         /*
1309          *      = - Set only if not already set
1310          */
1311         case T_OP_EQ:
1312                 if (dst) {
1313                         RDEBUG3("Refusing to overwrite (use :=)");
1314                         fr_pair_list_free(&head);
1315                         return 0;
1316                 }
1317
1318                 /* Insert first instance (if multiple) */
1319                 fr_cursor_first(&src_list);
1320                 fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
1321                 /* Free any we didn't insert */
1322                 fr_pair_list_free(&head);
1323                 break;
1324
1325         /*
1326          *      := - Overwrite existing attribute with last src_list attribute
1327          */
1328         case T_OP_SET:
1329                 /* Wind to last instance */
1330                 fr_cursor_last(&src_list);
1331                 if (dst) {
1332                         DEBUG_OVERWRITE(dst, fr_cursor_current(&src_list));
1333                         dst = fr_cursor_replace(&dst_list, fr_cursor_remove(&src_list));
1334                         fr_pair_list_free(&dst);
1335                 } else {
1336                         fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
1337                 }
1338                 /* Free any we didn't insert */
1339                 fr_pair_list_free(&head);
1340                 break;
1341
1342         /*
1343          *      += - Add all src_list attributes to the destination
1344          */
1345         case T_OP_ADD:
1346                 /* Insert all the instances! (if multiple) */
1347                 fr_pair_add(list, head);
1348                 head = NULL;
1349                 break;
1350
1351         /*
1352          *      Filtering operators
1353          */
1354         default:
1355                 /*
1356                  *      If the dst doesn't exist, the filters will add
1357                  *      it with the given value.
1358                  */
1359                 if (!dst) {
1360                         RDEBUG3("No existing attribute to filter, adding instead");
1361                         fr_cursor_merge(&dst_list, head);
1362                         head = NULL;
1363                         goto finish;
1364                 }
1365
1366                 /*
1367                  *      The LHS exists.  We need to limit it's value based on
1368                  *      the operator, and the value of the RHS.
1369                  */
1370                 found = false;
1371                 for (vp = fr_cursor_first(&src_list);
1372                      vp;
1373                      vp = fr_cursor_next(&src_list)) {
1374                         vp->op = map->op;
1375                         rcode = radius_compare_vps(request, vp, dst);
1376                         vp->op = T_OP_SET;
1377
1378                         switch (map->op) {
1379                         case T_OP_CMP_EQ:
1380                                 if (rcode == 0) continue;
1381                         replace:
1382                                 dst = fr_cursor_remove(&dst_list);
1383                                 DEBUG_OVERWRITE(dst, fr_cursor_current(&src_list));
1384                                 fr_pair_list_free(&dst);
1385                                 fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
1386                                 found = true;
1387                                 continue;
1388
1389                         case T_OP_LE:
1390                                 if (rcode <= 0) continue;
1391                                 goto replace;
1392
1393                         case T_OP_GE:
1394                                 if (rcode >= 0) continue;
1395                                 goto replace;
1396
1397                         default:
1398                                 fr_pair_list_free(&head);
1399                                 return -1;
1400                         }
1401                 }
1402                 fr_pair_list_free(&head);
1403                 if (!found) return 0;
1404
1405                 break;
1406         }
1407
1408 finish:
1409         rad_assert(!head);
1410
1411         /*
1412          *      Update the cached username && password.  This is code
1413          *      we execute on EVERY update (sigh) so that SOME modules
1414          *      MIGHT NOT have to do the search themselves.
1415          *
1416          *      TBH, we should probably make each module just do the
1417          *      search themselves.
1418          */
1419         if (map->lhs->tmpl_list == PAIR_LIST_REQUEST) {
1420                 context->username = NULL;
1421                 context->password = NULL;
1422
1423                 for (vp = fr_cursor_init(&src_list, list);
1424                      vp;
1425                      vp = fr_cursor_next(&src_list)) {
1426
1427                         if (vp->da->vendor != 0) continue;
1428                         if (vp->da->flags.has_tag) continue;
1429
1430                         if (!context->username && (vp->da->attr == PW_USER_NAME)) {
1431                                 context->username = vp;
1432                                 continue;
1433                         }
1434
1435                         if (vp->da->attr == PW_STRIPPED_USER_NAME) {
1436                                 context->username = vp;
1437                                 continue;
1438                         }
1439
1440                         if (vp->da->attr == PW_USER_PASSWORD) {
1441                                 context->password = vp;
1442                                 continue;
1443                         }
1444                 }
1445         }
1446         return 0;
1447 }
1448
1449 /** Check whether the destination of a map is currently valid
1450  *
1451  * @param request The current request.
1452  * @param map to check.
1453  * @return true if the map resolves to a request and list else false.
1454  */
1455 bool map_dst_valid(REQUEST *request, vp_map_t const *map)
1456 {
1457         REQUEST *context = request;
1458
1459         VERIFY_MAP(map);
1460
1461         if (radius_request(&context, map->lhs->tmpl_request) < 0) return false;
1462         if (!radius_list(context, map->lhs->tmpl_list)) return false;
1463
1464         return true;
1465 }
1466
1467 /**  Print a map to a string
1468  *
1469  * @param[out] buffer for the output string
1470  * @param[in] bufsize of the buffer
1471  * @param[in] map to print
1472  * @return the size of the string printed
1473  */
1474 size_t map_prints(char *buffer, size_t bufsize, vp_map_t const *map)
1475 {
1476         size_t len;
1477         DICT_ATTR const *da = NULL;
1478         char *p = buffer;
1479         char *end = buffer + bufsize;
1480
1481         VERIFY_MAP(map);
1482
1483         if (map->lhs->type == TMPL_TYPE_ATTR) da = map->lhs->tmpl_da;
1484
1485         len = tmpl_prints(buffer, bufsize, map->lhs, da);
1486         p += len;
1487
1488         *(p++) = ' ';
1489         strlcpy(p, fr_token_name(map->op), end - p);
1490         p += strlen(p);
1491         *(p++) = ' ';
1492
1493         /*
1494          *      The RHS doesn't matter for many operators
1495          */
1496         if ((map->op == T_OP_CMP_TRUE) ||
1497             (map->op == T_OP_CMP_FALSE)) {
1498                 strlcpy(p, "ANY", (end - p));
1499                 p += strlen(p);
1500                 return p - buffer;
1501         }
1502
1503         rad_assert(map->rhs != NULL);
1504
1505         if ((map->lhs->type == TMPL_TYPE_ATTR) &&
1506             (map->lhs->tmpl_da->type == PW_TYPE_STRING) &&
1507             (map->rhs->type == TMPL_TYPE_LITERAL)) {
1508                 *(p++) = '\'';
1509                 len = tmpl_prints(p, end - p, map->rhs, da);
1510                 p += len;
1511                 *(p++) = '\'';
1512                 *p = '\0';
1513         } else {
1514                 len = tmpl_prints(p, end - p, map->rhs, da);
1515                 p += len;
1516         }
1517
1518         return p - buffer;
1519 }
1520
1521 /*
1522  *      Debug print a map / VP
1523  */
1524 void map_debug_log(REQUEST *request, vp_map_t const *map, VALUE_PAIR const *vp)
1525 {
1526         char *value;
1527         char buffer[1024];
1528
1529         VERIFY_MAP(map);
1530         rad_assert(map->lhs != NULL);
1531         rad_assert(map->rhs != NULL);
1532
1533         rad_assert(vp || (map->rhs->type == TMPL_TYPE_NULL));
1534
1535         switch (map->rhs->type) {
1536         /*
1537          *      Just print the value being assigned
1538          */
1539         default:
1540         case TMPL_TYPE_LITERAL:
1541                 vp_prints_value(buffer, sizeof(buffer), vp, map->rhs->quote);
1542                 value = buffer;
1543                 break;
1544
1545         case TMPL_TYPE_XLAT:
1546         case TMPL_TYPE_XLAT_STRUCT:
1547                 vp_prints_value(buffer, sizeof(buffer), vp, map->rhs->quote);
1548                 value = buffer;
1549                 break;
1550
1551         case TMPL_TYPE_DATA:
1552                 vp_prints_value(buffer, sizeof(buffer), vp, map->rhs->quote);
1553                 value = buffer;
1554                 break;
1555
1556         /*
1557          *      For the lists, we can't use the original name, and have to
1558          *      rebuild it using tmpl_prints, for each attribute we're
1559          *      copying.
1560          */
1561         case TMPL_TYPE_LIST:
1562         {
1563                 char            attr[256];
1564                 char            quote = '\0';
1565                 vp_tmpl_t       vpt;
1566                 /*
1567                  *      Fudge a temporary tmpl that describes the attribute we're copying
1568                  *      this is a combination of the original list tmpl, and values from
1569                  *      the VALUE_PAIR. This way, we get tag info included.
1570                  */
1571                 memcpy(&vpt, map->rhs, sizeof(vpt));
1572                 vpt.tmpl_da = vp->da;
1573                 vpt.tmpl_tag = vp->tag;
1574                 vpt.type = TMPL_TYPE_ATTR;
1575
1576                 /*
1577                  *      Not appropriate to use map->rhs->quote here, as that's the quoting
1578                  *      around the list ref. The attribute value has no quoting, so we choose
1579                  *      the quoting based on the data type, and whether it's printable.
1580                  */
1581                 if (vp->da->type == PW_TYPE_STRING) quote = is_printable(vp->vp_strvalue,
1582                                                                          vp->vp_length) ? '\'' : '"';
1583                 vp_prints_value(buffer, sizeof(buffer), vp, quote);
1584                 tmpl_prints(attr, sizeof(attr), &vpt, vp->da);
1585                 value = talloc_typed_asprintf(request, "%s -> %s", attr, buffer);
1586         }
1587                 break;
1588
1589         case TMPL_TYPE_ATTR:
1590         {
1591                 char quote = '\0';
1592
1593                 /*
1594                  *      Not appropriate to use map->rhs->quote here, as that's the quoting
1595                  *      around the attr ref. The attribute value has no quoting, so we choose
1596                  *      the quoting based on the data type, and whether it's printable.
1597                  */
1598                 if (vp->da->type == PW_TYPE_STRING) quote = is_printable(vp->vp_strvalue,
1599                                                                          vp->vp_length) ? '\'' : '"';
1600                 vp_prints_value(buffer, sizeof(buffer), vp, quote);
1601                 value = talloc_typed_asprintf(request, "%.*s -> %s", (int)map->rhs->len, map->rhs->name, buffer);
1602         }
1603                 break;
1604
1605         case TMPL_TYPE_NULL:
1606                 strcpy(buffer, "ANY");
1607                 value = buffer;
1608                 break;
1609         }
1610
1611         switch (map->lhs->type) {
1612         case TMPL_TYPE_LIST:
1613                 RDEBUG("%.*s:%s %s %s", (int)map->lhs->len, map->lhs->name, vp ? vp->da->name : "",
1614                        fr_int2str(fr_tokens, vp ? vp->op : map->op, "<INVALID>"), value);
1615                 break;
1616
1617         case TMPL_TYPE_ATTR:
1618                 RDEBUG("%s %s %s", map->lhs->name,
1619                        fr_int2str(fr_tokens, vp ? vp->op : map->op, "<INVALID>"), value);
1620                 break;
1621
1622         default:
1623                 break;
1624         }
1625
1626         if (value != buffer) talloc_free(value);
1627 }