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