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