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