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: 3725ba1e10ba5d8c8c58cf2fcc13df000e1cc0d1 $
19  *
20  * @brief Valuepair functions that are radiusd-specific and as such do not
21  * 	  belong in the library.
22  * @file main/pair.c
23  *
24  * @ingroup AVP
25  *
26  * @copyright 2000,2006  The FreeRADIUS server project
27  * @copyright 2000  Alan DeKok <aland@ox.org>
28  */
29 
30 RCSID("$Id: 3725ba1e10ba5d8c8c58cf2fcc13df000e1cc0d1 $")
31 
32 #include <ctype.h>
33 
34 #include <freeradius-devel/radiusd.h>
35 #include <freeradius-devel/rad_assert.h>
36 
37 struct cmp {
38 	DICT_ATTR const *attribute;
39 	DICT_ATTR const *from;
40 	bool	first_only;
41 	void *instance; /* module instance */
42 	RAD_COMPARE_FUNC compare;
43 	struct cmp *next;
44 };
45 static struct cmp *cmp;
46 
47 /** Compares check and vp by value.
48  *
49  * Does not call any per-attribute comparison function, but does honour
50  * check.operator. Basically does "vp.value check.op check.value".
51  *
52  * @param request Current request.
53  * @param check rvalue, and operator.
54  * @param vp lvalue.
55  * @return 0 if check and vp are equal, -1 if vp value is less than check value, 1 is vp value is more than check
56  *	value, -2 on error.
57  */
58 #ifdef HAVE_REGEX
radius_compare_vps(REQUEST * request,VALUE_PAIR * check,VALUE_PAIR * vp)59 int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
60 #else
61 int radius_compare_vps(UNUSED REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
62 #endif
63 {
64 	int ret = 0;
65 
66 	/*
67 	 *      Check for =* and !* and return appropriately
68 	 */
69 	if (check->op == T_OP_CMP_TRUE)  return 0;
70 	if (check->op == T_OP_CMP_FALSE) return 1;
71 
72 #ifdef HAVE_REGEX
73 	if ((check->op == T_OP_REG_EQ) || (check->op == T_OP_REG_NE)) {
74 		ssize_t		slen;
75 		regex_t		*preg = NULL;
76 		regmatch_t	rxmatch[REQUEST_MAX_REGEX + 1];	/* +1 for %{0} (whole match) capture group */
77 		size_t		nmatch = sizeof(rxmatch) / sizeof(regmatch_t);
78 
79 		char *expr = NULL, *value = NULL;
80 		char const *expr_p, *value_p;
81 
82 		if (!vp) return -2;
83 
84 		if (check->da->type == PW_TYPE_STRING) {
85 			expr_p = check->vp_strvalue;
86 		} else {
87 			expr_p = expr = vp_aprints_value(request, check, '\0');
88 		}
89 
90 		if (vp->da->type == PW_TYPE_STRING) {
91 			value_p = vp->vp_strvalue;
92 		} else {
93 			value_p = value = vp_aprints_value(request, vp, '\0');
94 		}
95 
96 		if (!expr_p || !value_p) {
97 			REDEBUG("Error stringifying operand for regular expression");
98 
99 		regex_error:
100 			talloc_free(preg);
101 			talloc_free(expr);
102 			talloc_free(value);
103 			return -2;
104 		}
105 
106 		/*
107 		 *	Include substring matches.
108 		 */
109 		slen = regex_compile(request, &preg, expr_p, talloc_array_length(expr_p) - 1, false, false, true, true);
110 		if (slen <= 0) {
111 			REMARKER(expr_p, -slen, fr_strerror());
112 
113 			goto regex_error;
114 		}
115 
116 		slen = regex_exec(preg, value_p, talloc_array_length(value_p) - 1, rxmatch, &nmatch);
117 		if (slen < 0) {
118 			RERROR("%s", fr_strerror());
119 
120 			goto regex_error;
121 		}
122 
123 		if (check->op == T_OP_REG_EQ) {
124 			/*
125 			 *	Add in %{0}. %{1}, etc.
126 			 */
127 			regex_sub_to_request(request, &preg, value_p, talloc_array_length(value_p) - 1,
128 					     rxmatch, nmatch);
129 			ret = (slen == 1) ? 0 : -1;
130 		} else {
131 			ret = (slen != 1) ? 0 : -1;
132 		}
133 
134 		talloc_free(preg);
135 		talloc_free(expr);
136 		talloc_free(value);
137 		goto finish;
138 	}
139 #endif
140 
141 	/*
142 	 *	Attributes must be of the same type.
143 	 *
144 	 *	FIXME: deal with type mismatch properly if one side contain
145 	 *	ABINARY, OCTETS or STRING by converting the other side to
146 	 *	a string
147 	 *
148 	 */
149 	if (vp->da->type != check->da->type) return -1;
150 
151 	/*
152 	 *	Tagged attributes are equal if and only if both the
153 	 *	tag AND value match.
154 	 */
155 	if (check->da->flags.has_tag && !TAG_EQ(check->tag, vp->tag)) {
156 		ret = ((int) vp->tag) - ((int) check->tag);
157 		if (ret != 0) goto finish;
158 	}
159 
160 	/*
161 	 *	Not a regular expression, compare the types.
162 	 */
163 	switch (check->da->type) {
164 #ifdef WITH_ASCEND_BINARY
165 		/*
166 		 *	Ascend binary attributes can be treated
167 		 *	as opaque objects, I guess...
168 		 */
169 		case PW_TYPE_ABINARY:
170 #endif
171 		case PW_TYPE_OCTETS:
172 			if (vp->vp_length != check->vp_length) {
173 				ret = 1; /* NOT equal */
174 				break;
175 			}
176 			ret = memcmp(vp->vp_strvalue, check->vp_strvalue,
177 				     vp->vp_length);
178 			break;
179 
180 		case PW_TYPE_STRING:
181 			ret = strcmp(vp->vp_strvalue,
182 				     check->vp_strvalue);
183 			break;
184 
185 		case PW_TYPE_BYTE:
186 			ret = vp->vp_byte - check->vp_byte;
187 			break;
188 		case PW_TYPE_SHORT:
189 			ret = vp->vp_short - check->vp_short;
190 			break;
191 		case PW_TYPE_INTEGER:
192 			ret = vp->vp_integer - check->vp_integer;
193 			break;
194 
195 		case PW_TYPE_INTEGER64:
196 			/*
197 			 *	Don't want integer overflow!
198 			 */
199 			if (vp->vp_integer64 < check->vp_integer64) {
200 				ret = -1;
201 			} else if (vp->vp_integer64 > check->vp_integer64) {
202 				ret = +1;
203 			} else {
204 				ret = 0;
205 			}
206 			break;
207 
208 		case PW_TYPE_SIGNED:
209 			if (vp->vp_signed < check->vp_signed) {
210 				ret = -1;
211 			} else if (vp->vp_signed > check->vp_signed) {
212 				ret = +1;
213 			} else {
214 				ret = 0;
215 			}
216 			break;
217 
218 		case PW_TYPE_DATE:
219 			ret = vp->vp_date - check->vp_date;
220 			break;
221 
222 		case PW_TYPE_IPV4_ADDR:
223 			ret = ntohl(vp->vp_ipaddr) - ntohl(check->vp_ipaddr);
224 			break;
225 
226 		case PW_TYPE_IPV6_ADDR:
227 			ret = memcmp(&vp->vp_ipv6addr, &check->vp_ipv6addr, sizeof(vp->vp_ipv6addr));
228 			break;
229 
230 		case PW_TYPE_IPV4_PREFIX:
231 		case PW_TYPE_IPV6_PREFIX:
232 			ret = fr_pair_cmp_op(check->op, vp, check);
233 			if (ret == -1) return -2;   // error
234 			if (check->op == T_OP_LT || check->op == T_OP_LE)
235 				ret = (ret == 1) ? -1 : 1;
236 			else if (check->op == T_OP_GT || check->op == T_OP_GE)
237 				ret = (ret == 1) ? 1 : -1;
238 			else if (check->op == T_OP_CMP_EQ)
239 				ret = (ret == 1) ? 0 : -1;
240 			break;
241 
242 		case PW_TYPE_IFID:
243 			ret = memcmp(vp->vp_ifid, check->vp_ifid, sizeof(vp->vp_ifid));
244 			break;
245 
246 		default:
247 			break;
248 	}
249 
250 finish:
251 	if (ret > 0) return 1;
252 	if (ret < 0) return -1;
253 	return 0;
254 }
255 
256 
257 /** Compare check and vp. May call the attribute comparison function.
258  *
259  * Unlike radius_compare_vps() this function will call any attribute-specific
260  * comparison functions registered.
261  *
262  * @param request Current request.
263  * @param req list pairs.
264  * @param check item to compare.
265  * @param check_pairs list.
266  * @param reply_pairs list.
267  * @return 0 if check and vp are equal, -1 if vp value is less than check value, 1 is vp value is more than check
268  *	value.
269  */
radius_callback_compare(REQUEST * request,VALUE_PAIR * req,VALUE_PAIR * check,VALUE_PAIR * check_pairs,VALUE_PAIR ** reply_pairs)270 int radius_callback_compare(REQUEST *request, VALUE_PAIR *req,
271 			    VALUE_PAIR *check, VALUE_PAIR *check_pairs,
272 			    VALUE_PAIR **reply_pairs)
273 {
274 	struct cmp *c;
275 
276 	/*
277 	 *      Check for =* and !* and return appropriately
278 	 */
279 	if (check->op == T_OP_CMP_TRUE)  return 0;
280 	if (check->op == T_OP_CMP_FALSE) return 1;
281 
282 	/*
283 	 *	See if there is a special compare function.
284 	 *
285 	 *	FIXME: use new RB-Tree code.
286 	 */
287 	for (c = cmp; c; c = c->next) {
288 		if (c->attribute == check->da) {
289 			return (c->compare)(c->instance, request, req, check,
290 				check_pairs, reply_pairs);
291 		}
292 	}
293 
294 	if (!req) return -1; /* doesn't exist, don't compare it */
295 
296 	return radius_compare_vps(request, check, req);
297 }
298 
299 
300 /** Find a comparison function for two attributes.
301  *
302  * @todo this should probably take DA's.
303  * @param attribute to find comparison function for.
304  * @return true if a comparison function was found, else false.
305  */
radius_find_compare(DICT_ATTR const * attribute)306 int radius_find_compare(DICT_ATTR const *attribute)
307 {
308 	struct cmp *c;
309 
310 	for (c = cmp; c; c = c->next) {
311 		if (c->attribute == attribute) {
312 			return true;
313 		}
314 	}
315 
316 	return false;
317 }
318 
319 
320 /** See what attribute we want to compare with.
321  *
322  * @param attribute to find comparison function for.
323  * @param from reference to compare with
324  * @return true if the comparison callback require a matching attribue in the request, else false.
325  */
otherattr(DICT_ATTR const * attribute,DICT_ATTR const ** from)326 static bool otherattr(DICT_ATTR const *attribute, DICT_ATTR const **from)
327 {
328 	struct cmp *c;
329 
330 	for (c = cmp; c; c = c->next) {
331 		if (c->attribute == attribute) {
332 			*from = c->from;
333 			return c->first_only;
334 		}
335 	}
336 
337 	*from = attribute;
338 	return false;
339 }
340 
341 /** Register a function as compare function.
342  *
343  * @param name the attribute comparison to register
344  * @param from the attribute we want to compare with. Normally this is the same as attribute.
345  *  If null call the comparison function on every attributes in the request if first_only is false
346  * @param first_only will decide if we loop over the request attributes or stop on the first one
347  * @param func comparison function
348  * @param instance argument to comparison function
349  * @return 0
350  */
paircompare_register_byname(char const * name,DICT_ATTR const * from,bool first_only,RAD_COMPARE_FUNC func,void * instance)351 int paircompare_register_byname(char const *name, DICT_ATTR const *from,
352 				bool first_only, RAD_COMPARE_FUNC func, void *instance)
353 {
354 	ATTR_FLAGS flags;
355 	DICT_ATTR const *da;
356 
357 	memset(&flags, 0, sizeof(flags));
358 	flags.compare = 1;
359 
360 	da = dict_attrbyname(name);
361 	if (da) {
362 		if (!da->flags.compare) {
363 			fr_strerror_printf("Attribute '%s' already exists.", name);
364 			return -1;
365 		}
366 	} else if (from) {
367 		if (dict_addattr(name, -1, 0, from->type, flags) < 0) {
368 			fr_strerror_printf("Failed creating attribute '%s'", name);
369 			return -1;
370 		}
371 
372 		da = dict_attrbyname(name);
373 		if (!da) {
374 			fr_strerror_printf("Failed finding attribute '%s'", name);
375 			return -1;
376 		}
377 
378 		DEBUG("Creating attribute %s", name);
379 	}
380 
381 	return paircompare_register(da, from, first_only, func, instance);
382 }
383 
384 /** Register a function as compare function.
385  *
386  * @param attribute to register comparison function for.
387  * @param from the attribute we want to compare with. Normally this is the same as attribute.
388  *  If null call the comparison function on every attributes in the request if first_only is false
389  * @param first_only will decide if we loop over the request attributes or stop on the first one
390  * @param func comparison function
391  * @param instance argument to comparison function
392  * @return 0
393  */
paircompare_register(DICT_ATTR const * attribute,DICT_ATTR const * from,bool first_only,RAD_COMPARE_FUNC func,void * instance)394 int paircompare_register(DICT_ATTR const *attribute, DICT_ATTR const *from,
395 			 bool first_only, RAD_COMPARE_FUNC func, void *instance)
396 {
397 	struct cmp *c;
398 
399 	rad_assert(attribute != NULL);
400 
401 	paircompare_unregister(attribute, func);
402 
403 	c = rad_malloc(sizeof(struct cmp));
404 
405 	c->compare   = func;
406 	c->attribute = attribute;
407 	c->from = from;
408 	c->first_only = first_only;
409 	c->instance  = instance;
410 	c->next      = cmp;
411 	cmp = c;
412 
413 	return 0;
414 }
415 
416 /** Unregister comparison function for an attribute
417  *
418  * @param attribute dict reference to unregister for.
419  * @param func comparison function to remove.
420  */
paircompare_unregister(DICT_ATTR const * attribute,RAD_COMPARE_FUNC func)421 void paircompare_unregister(DICT_ATTR const *attribute, RAD_COMPARE_FUNC func)
422 {
423 	struct cmp *c, *last;
424 
425 	last = NULL;
426 	for (c = cmp; c; c = c->next) {
427 		if (c->attribute == attribute && c->compare == func) {
428 			break;
429 		}
430 		last = c;
431 	}
432 
433 	if (c == NULL) return;
434 
435 	if (last != NULL) {
436 		last->next = c->next;
437 	} else {
438 		cmp = c->next;
439 	}
440 
441 	free(c);
442 }
443 
444 /** Unregister comparison function for a module
445  *
446  *  All paircompare() functions for this module will be unregistered.
447  *
448  * @param instance the module instance
449  */
paircompare_unregister_instance(void * instance)450 void paircompare_unregister_instance(void *instance)
451 {
452 	struct cmp *c, **tail;
453 
454 	tail = &cmp;
455 	while ((c = *tail) != NULL) {
456 		if (c->instance == instance) {
457 			*tail = c->next;
458 			free(c);
459 			continue;
460 		}
461 
462 		tail = &(c->next);
463 	}
464 }
465 
466 /** Compare two pair lists except for the password information.
467  *
468  * For every element in "check" at least one matching copy must be present
469  * in "reply".
470  *
471  * @param[in] request Current request.
472  * @param[in] req_list request valuepairs.
473  * @param[in] check Check/control valuepairs.
474  * @param[in,out] rep_list Reply value pairs.
475  *
476  * @return 0 on match.
477  */
paircompare(REQUEST * request,VALUE_PAIR * req_list,VALUE_PAIR * check,VALUE_PAIR ** rep_list)478 int paircompare(REQUEST *request, VALUE_PAIR *req_list, VALUE_PAIR *check,
479 		VALUE_PAIR **rep_list)
480 {
481 	vp_cursor_t cursor;
482 	VALUE_PAIR *check_item;
483 	VALUE_PAIR *auth_item = NULL;
484 	DICT_ATTR const *from;
485 
486 	int result = 0;
487 	int compare;
488 	bool first_only;
489 
490 	for (check_item = fr_cursor_init(&cursor, &check);
491 	     check_item;
492 	     check_item = fr_cursor_next(&cursor)) {
493 		/*
494 		 *	If the user is setting a configuration value,
495 		 *	then don't bother comparing it to any attributes
496 		 *	sent to us by the user.  It ALWAYS matches.
497 		 */
498 		if ((check_item->op == T_OP_SET) ||
499 		    (check_item->op == T_OP_ADD)) {
500 			continue;
501 		}
502 
503 		if (!check_item->da->vendor) switch (check_item->da->attr) {
504 		/*
505 		 *	Attributes we skip during comparison.
506 		 *	These are "server" check items.
507 		 */
508 		case PW_CRYPT_PASSWORD:
509 		case PW_AUTH_TYPE:
510 		case PW_AUTZ_TYPE:
511 		case PW_ACCT_TYPE:
512 		case PW_SESSION_TYPE:
513 		case PW_STRIP_USER_NAME:
514 			continue;
515 
516 		/*
517 		 *	IF the password attribute exists, THEN
518 		 *	we can do comparisons against it.  If not,
519 		 *	then the request did NOT contain a
520 		 *	User-Password attribute, so we CANNOT do
521 		 *	comparisons against it.
522 		 *
523 		 *	This hack makes CHAP-Password work..
524 		 */
525 		case PW_USER_PASSWORD:
526 			if (check_item->op == T_OP_CMP_EQ) {
527 				WARN("Found User-Password == \"...\"");
528 				WARN("Are you sure you don't mean Cleartext-Password?");
529 				WARN("See \"man rlm_pap\" for more information");
530 			}
531 			if (fr_pair_find_by_num(req_list, PW_USER_PASSWORD, 0, TAG_ANY) == NULL) {
532 				continue;
533 			}
534 			break;
535 		}
536 
537 		/*
538 		 *	See if this item is present in the request.
539 		 */
540 		first_only = otherattr(check_item->da, &from);
541 
542 		auth_item = req_list;
543 	try_again:
544 		if (!first_only) {
545 			while (auth_item != NULL) {
546 				VERIFY_VP(auth_item);
547 				if ((auth_item->da == from) || (!from)) {
548 					break;
549 				}
550 				auth_item = auth_item->next;
551 			}
552 		}
553 
554 		/*
555 		 *	Not found, it's not a match.
556 		 */
557 		if (auth_item == NULL) {
558 			/*
559 			 *	Didn't find it.  If we were *trying*
560 			 *	to not find it, then we succeeded.
561 			 */
562 			if (check_item->op == T_OP_CMP_FALSE) {
563 				continue;
564 			} else {
565 				return -1;
566 			}
567 		}
568 
569 		/*
570 		 *	Else we found it, but we were trying to not
571 		 *	find it, so we failed.
572 		 */
573 		if (check_item->op == T_OP_CMP_FALSE) {
574 			return -1;
575 		}
576 
577 		/*
578 		 *	We've got to xlat the string before doing
579 		 *	the comparison.
580 		 */
581 		radius_xlat_do(request, check_item);
582 
583 		/*
584 		 *	OK it is present now compare them.
585 		 */
586 		compare = radius_callback_compare(request, auth_item,
587 						  check_item, check, rep_list);
588 
589 		switch (check_item->op) {
590 		case T_OP_EQ:
591 		default:
592 			RWDEBUG("Invalid operator '%s' for item %s: reverting to '=='",
593 				fr_int2str(fr_tokens, check_item->op, "<INVALID>"), check_item->da->name);
594 			/* FALL-THROUGH */
595 		case T_OP_CMP_TRUE:
596 		case T_OP_CMP_FALSE:
597 		case T_OP_CMP_EQ:
598 			if (compare != 0) result = -1;
599 			break;
600 
601 		case T_OP_NE:
602 			if (compare == 0) result = -1;
603 			break;
604 
605 		case T_OP_LT:
606 			if (compare >= 0) result = -1;
607 			break;
608 
609 		case T_OP_GT:
610 			if (compare <= 0) result = -1;
611 			break;
612 
613 		case T_OP_LE:
614 			if (compare > 0) result = -1;
615 			break;
616 
617 		case T_OP_GE:
618 			if (compare < 0) result = -1;
619 			break;
620 
621 #ifdef HAVE_REGEX
622 		case T_OP_REG_EQ:
623 		case T_OP_REG_NE:
624 			if (compare != 0) result = -1;
625 			break;
626 #endif
627 		} /* switch over the operator of the check item */
628 
629 		/*
630 		 *	This attribute didn't match, but maybe there's
631 		 *	another of the same attribute, which DOES match.
632 		 */
633 		if ((result != 0) && (!first_only)) {
634 			fr_assert(auth_item != NULL);
635 			auth_item = auth_item->next;
636 			result = 0;
637 			goto try_again;
638 		}
639 
640 	} /* for every entry in the check item list */
641 
642 	return result;
643 }
644 
645 /** Expands an attribute marked with fr_pair_mark_xlat
646  *
647  * Writes the new value to the vp.
648  *
649  * @param request Current request.
650  * @param vp to expand.
651  * @return 0 if successful else -1 (on xlat failure) or -2 (on parse failure).
652  *	On failure pair will still no longer be marked for xlat expansion.
653  */
radius_xlat_do(REQUEST * request,VALUE_PAIR * vp)654 int radius_xlat_do(REQUEST *request, VALUE_PAIR *vp)
655 {
656 	ssize_t slen;
657 
658 	char *expanded = NULL;
659 	if (vp->type != VT_XLAT) return 0;
660 
661 	vp->type = VT_DATA;
662 
663 	slen = radius_axlat(&expanded, request, vp->value.xlat, NULL, NULL);
664 	rad_const_free(vp->value.xlat);
665 	vp->value.xlat = NULL;
666 	if (slen < 0) {
667 		return -1;
668 	}
669 
670 	/*
671 	 *	Parse the string into a new value.
672 	 *
673 	 *	If the VALUE_PAIR is being used in a regular expression
674 	 *	then we just want to copy the new value in unmolested.
675 	 */
676 	if ((vp->op == T_OP_REG_EQ) || (vp->op == T_OP_REG_NE)) {
677 		fr_pair_value_strsteal(vp, expanded);
678 		return 0;
679 	}
680 
681 	if (fr_pair_value_from_str(vp, expanded, -1) < 0){
682 		talloc_free(expanded);
683 		return -2;
684 	}
685 
686 	talloc_free(expanded);
687 
688 	return 0;
689 }
690 
691 /** Create a VALUE_PAIR and add it to a list of VALUE_PAIR s
692  *
693  * @note This function ALWAYS returns. If we're OOM, then it causes the
694  * @note server to exit, so you don't need to check the return value.
695  *
696  * @param[in] ctx for talloc
697  * @param[out] vps List to add new VALUE_PAIR to, if NULL will just
698  *	return VALUE_PAIR.
699  * @param[in] attribute number.
700  * @param[in] vendor number.
701  * @return a new VLAUE_PAIR or causes server to exit on error.
702  */
radius_pair_create(TALLOC_CTX * ctx,VALUE_PAIR ** vps,unsigned int attribute,unsigned int vendor)703 VALUE_PAIR *radius_pair_create(TALLOC_CTX *ctx, VALUE_PAIR **vps,
704 			      unsigned int attribute, unsigned int vendor)
705 {
706 	VALUE_PAIR *vp;
707 
708 	vp = fr_pair_afrom_num(ctx, attribute, vendor);
709 	if (!vp) {
710 		ERROR("No memory!");
711 		rad_assert("No memory" == NULL);
712 		fr_exit_now(1);
713 	}
714 
715 	if (vps) fr_pair_add(vps, vp);
716 
717 	return vp;
718 }
719 
720 /** Print a single valuepair to stderr or error log.
721  *
722  * @param[in] vp list to print.
723  */
debug_pair(VALUE_PAIR * vp)724 void debug_pair(VALUE_PAIR *vp)
725 {
726 	if (!vp || !rad_debug_lvl || !fr_log_fp) return;
727 
728 	vp_print(fr_log_fp, vp);
729 }
730 
731 /** Print a single valuepair to stderr or error log.
732  *
733  * @param[in] level Debug level (1-4).
734  * @param[in] request to read logging params from.
735  * @param[in] vp to print.
736  * @param[in] prefix (optional).
737  */
rdebug_pair(log_lvl_t level,REQUEST * request,VALUE_PAIR * vp,char const * prefix)738 void rdebug_pair(log_lvl_t level, REQUEST *request, VALUE_PAIR *vp, char const *prefix)
739 {
740 	char buffer[768];
741 	if (!vp || !request || !request->log.func) return;
742 
743 	if (!radlog_debug_enabled(L_DBG, level, request)) return;
744 
745 	if (vp->da->flags.secret && request->root && request->root->suppress_secrets && (rad_debug_lvl < 3)) {
746 		RDEBUGX(level, "%s%s = <<< secret >>>", prefix ? prefix : "", vp->da->name);
747 		return;
748 	}
749 
750 	vp_prints(buffer, sizeof(buffer), vp);
751 	RDEBUGX(level, "%s%s", prefix ? prefix : "",  buffer);
752 }
753 
754 /** Print a list of VALUE_PAIRs.
755  *
756  * @param[in] level Debug level (1-4).
757  * @param[in] request to read logging params from.
758  * @param[in] vp to print.
759  * @param[in] prefix (optional).
760  */
rdebug_pair_list(log_lvl_t level,REQUEST * request,VALUE_PAIR * vp,char const * prefix)761 void rdebug_pair_list(log_lvl_t level, REQUEST *request, VALUE_PAIR *vp, char const *prefix)
762 {
763 	vp_cursor_t cursor;
764 	char buffer[768];
765 	if (!vp || !request || !request->log.func) return;
766 
767 	if (!radlog_debug_enabled(L_DBG, level, request)) return;
768 
769 	RINDENT();
770 	for (vp = fr_cursor_init(&cursor, &vp);
771 	     vp;
772 	     vp = fr_cursor_next(&cursor)) {
773 		VERIFY_VP(vp);
774 
775 		if (vp->da->flags.secret && request->root && request->root->suppress_secrets && (rad_debug_lvl < 3)) {
776 			RDEBUGX(level, "%s%s = <<< secret >>>", prefix ? prefix : "", vp->da->name);
777 			continue;
778 		}
779 
780 		vp_prints(buffer, sizeof(buffer), vp);
781 		RDEBUGX(level, "%s%s", prefix ? prefix : "",  buffer);
782 	}
783 	REXDENT();
784 }
785 
786 /** Print a list of protocol VALUE_PAIRs.
787  *
788  * @param[in] level Debug level (1-4).
789  * @param[in] request to read logging params from.
790  * @param[in] vp to print.
791  */
rdebug_proto_pair_list(log_lvl_t level,REQUEST * request,VALUE_PAIR * vp)792 void rdebug_proto_pair_list(log_lvl_t level, REQUEST *request, VALUE_PAIR *vp)
793 {
794 	vp_cursor_t cursor;
795 	char buffer[768];
796 	if (!vp || !request || !request->log.func) return;
797 
798 	if (!radlog_debug_enabled(L_DBG, level, request)) return;
799 
800 	RINDENT();
801 	for (vp = fr_cursor_init(&cursor, &vp);
802 	     vp;
803 	     vp = fr_cursor_next(&cursor)) {
804 		VERIFY_VP(vp);
805 		if ((vp->da->vendor == 0) &&
806 		    ((vp->da->attr & 0xFFFF) > 0xff)) continue;
807 
808 		if (vp->da->flags.secret && request->root && request->root->suppress_secrets && (rad_debug_lvl < 3)) {
809 			RDEBUGX(level, "%s = <<< secret >>>", vp->da->name);
810 			continue;
811 		}
812 
813 		vp_prints(buffer, sizeof(buffer), vp);
814 		RDEBUGX(level, "%s", buffer);
815 	}
816 	REXDENT();
817 }
818 
819 /** Return a VP from the specified request.
820  *
821  * @param out where to write the pointer to the resolved VP.
822  *	Will be NULL if the attribute couldn't be resolved.
823  * @param request current request.
824  * @param name attribute name including qualifiers.
825  * @return -4 if either the attribute or qualifier were invalid, and the same error codes as tmpl_find_vp for other
826  *	error conditions.
827  */
radius_get_vp(VALUE_PAIR ** out,REQUEST * request,char const * name)828 int radius_get_vp(VALUE_PAIR **out, REQUEST *request, char const *name)
829 {
830 	int rcode;
831 	vp_tmpl_t vpt;
832 
833 	*out = NULL;
834 
835 	if (tmpl_from_attr_str(&vpt, name, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) <= 0) {
836 		return -4;
837 	}
838 
839 	rcode = tmpl_find_vp(out, request, &vpt);
840 
841 	return rcode;
842 }
843 
844 /** Copy VP(s) from the specified request.
845  *
846  * @param ctx to alloc new VALUE_PAIRs in.
847  * @param out where to write the pointer to the copied VP.
848  *	Will be NULL if the attribute couldn't be resolved.
849  * @param request current request.
850  * @param name attribute name including qualifiers.
851  * @return -4 if either the attribute or qualifier were invalid, and the same error codes as tmpl_find_vp for other
852  *	error conditions.
853  */
radius_copy_vp(TALLOC_CTX * ctx,VALUE_PAIR ** out,REQUEST * request,char const * name)854 int radius_copy_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, char const *name)
855 {
856 	int rcode;
857 	vp_tmpl_t vpt;
858 
859 	*out = NULL;
860 
861 	if (tmpl_from_attr_str(&vpt, name, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) <= 0) {
862 		return -4;
863 	}
864 
865 	rcode = tmpl_copy_vps(ctx, out, request, &vpt);
866 
867 	return rcode;
868 }
869 
module_failure_msg(REQUEST * request,char const * fmt,...)870 void module_failure_msg(REQUEST *request, char const *fmt, ...)
871 {
872 	va_list ap;
873 
874 	va_start(ap, fmt);
875 	vmodule_failure_msg(request, fmt, ap);
876 	va_end(ap);
877 }
878 
879 /** Add a module failure message VALUE_PAIR to the request
880  */
vmodule_failure_msg(REQUEST * request,char const * fmt,va_list ap)881 void vmodule_failure_msg(REQUEST *request, char const *fmt, va_list ap)
882 {
883 	char *p;
884 	VALUE_PAIR *vp;
885 	va_list aq;
886 
887 	if (!fmt || !request || !request->packet) {
888 		return;
889 	}
890 
891 	/*
892 	 *  If we don't copy the original ap we get a segfault from vasprintf. This is apparently
893 	 *  due to ap sometimes being implemented with a stack offset which is invalidated if
894 	 *  ap is passed into another function. See here:
895 	 *  http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html
896 	 *
897 	 *  I don't buy that explanation, but doing a va_copy here does prevent SEGVs seen when
898 	 *  running unit tests which generate errors under CI.
899 	 */
900 	va_copy(aq, ap);
901 	p = talloc_vasprintf(request, fmt, aq);
902 	va_end(aq);
903 
904 	MEM(vp = pair_make_request("Module-Failure-Message", NULL, T_OP_ADD));
905 	if (request->module && (request->module[0] != '\0')) {
906 		fr_pair_value_sprintf(vp, "%s: %s", request->module, p);
907 	} else {
908 		fr_pair_value_sprintf(vp, "%s", p);
909 	}
910 	talloc_free(p);
911 }
912