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.
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.
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
20 * @brief Valuepair functions that are radiusd-specific and as such do not
21 * belong in the library.
26 * @copyright 2000,2006 The FreeRADIUS server project
27 * @copyright 2000 Alan DeKok <aland@ox.org>
34 #include <freeradius-devel/radiusd.h>
35 #include <freeradius-devel/rad_assert.h>
38 DICT_ATTR const *attribute;
39 DICT_ATTR const *from;
41 void *instance; /* module instance */
42 RAD_COMPARE_FUNC compare;
45 static struct cmp *cmp;
47 /** Compares check and vp by value.
49 * Does not call any per-attribute comparison function, but does honour
50 * check.operator. Basically does "vp.value check.op check.value".
52 * @param request Current request.
53 * @param check rvalue, and operator.
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
59 int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
61 int radius_compare_vps(UNUSED REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
67 * Check for =* and !* and return appropriately
69 if (check->op == T_OP_CMP_TRUE) return 0;
70 if (check->op == T_OP_CMP_FALSE) return 1;
73 if ((check->op == T_OP_REG_EQ) || (check->op == T_OP_REG_NE)) {
76 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* +1 for %{0} (whole match) capture group */
77 size_t nmatch = sizeof(rxmatch) / sizeof(regmatch_t);
79 char *expr = NULL, *value = NULL;
80 char const *expr_p, *value_p;
82 if (check->da->type == PW_TYPE_STRING) {
83 expr_p = check->vp_strvalue;
85 expr_p = expr = vp_aprints_value(check, check, '\0');
88 if (vp->da->type == PW_TYPE_STRING) {
89 value_p = vp->vp_strvalue;
91 value_p = value = vp_aprints_value(vp, vp, '\0');
94 if (!expr_p || !value_p) {
95 REDEBUG("Error stringifying operand for regular expression");
105 * Include substring matches.
107 slen = regex_compile(request, &preg, expr_p, talloc_array_length(expr_p) - 1, false, false, true, true);
109 REMARKER(expr_p, -slen, fr_strerror());
114 slen = regex_exec(preg, value_p, talloc_array_length(value_p) - 1, rxmatch, &nmatch);
116 RERROR("%s", fr_strerror());
121 if (check->op == T_OP_REG_EQ) {
123 * Add in %{0}. %{1}, etc.
125 regex_sub_to_request(request, &preg, value_p, talloc_array_length(value_p) - 1,
127 ret = (slen == 1) ? 0 : -1;
129 ret = (slen != 1) ? 0 : -1;
140 * Attributes must be of the same type.
142 * FIXME: deal with type mismatch properly if one side contain
143 * ABINARY, OCTETS or STRING by converting the other side to
147 if (vp->da->type != check->da->type) return -1;
150 * Tagged attributes are equal if and only if both the
151 * tag AND value match.
153 if (check->da->flags.has_tag && !TAG_EQ(check->tag, vp->tag)) {
154 ret = ((int) vp->tag) - ((int) check->tag);
155 if (ret != 0) goto finish;
159 * Not a regular expression, compare the types.
161 switch (check->da->type) {
162 #ifdef WITH_ASCEND_BINARY
164 * Ascend binary attributes can be treated
165 * as opaque objects, I guess...
167 case PW_TYPE_ABINARY:
170 if (vp->vp_length != check->vp_length) {
171 ret = 1; /* NOT equal */
174 ret = memcmp(vp->vp_strvalue, check->vp_strvalue,
179 ret = strcmp(vp->vp_strvalue,
184 ret = vp->vp_byte - check->vp_byte;
187 ret = vp->vp_short - check->vp_short;
189 case PW_TYPE_INTEGER:
190 ret = vp->vp_integer - check->vp_integer;
193 case PW_TYPE_INTEGER64:
195 * Don't want integer overflow!
197 if (vp->vp_integer64 < check->vp_integer64) {
199 } else if (vp->vp_integer64 > check->vp_integer64) {
207 if (vp->vp_signed < check->vp_signed) {
209 } else if (vp->vp_signed > check->vp_signed) {
217 ret = vp->vp_date - check->vp_date;
220 case PW_TYPE_IPV4_ADDR:
221 ret = ntohl(vp->vp_ipaddr) - ntohl(check->vp_ipaddr);
224 case PW_TYPE_IPV6_ADDR:
225 ret = memcmp(&vp->vp_ipv6addr, &check->vp_ipv6addr, sizeof(vp->vp_ipv6addr));
228 case PW_TYPE_IPV6_PREFIX:
229 ret = memcmp(vp->vp_ipv6prefix, check->vp_ipv6prefix, sizeof(vp->vp_ipv6prefix));
233 ret = memcmp(vp->vp_ifid, check->vp_ifid, sizeof(vp->vp_ifid));
241 if (ret > 0) return 1;
242 if (ret < 0) return -1;
247 /** Compare check and vp. May call the attribute comparison function.
249 * Unlike radius_compare_vps() this function will call any attribute-specific
250 * comparison functions registered.
252 * @param request Current request.
253 * @param req list pairs.
254 * @param check item to compare.
255 * @param check_pairs list.
256 * @param reply_pairs list.
257 * @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
260 int radius_callback_compare(REQUEST *request, VALUE_PAIR *req,
261 VALUE_PAIR *check, VALUE_PAIR *check_pairs,
262 VALUE_PAIR **reply_pairs)
267 * Check for =* and !* and return appropriately
269 if (check->op == T_OP_CMP_TRUE) return 0;
270 if (check->op == T_OP_CMP_FALSE) return 1;
273 * See if there is a special compare function.
275 * FIXME: use new RB-Tree code.
277 for (c = cmp; c; c = c->next) {
278 if (c->attribute == check->da) {
279 return (c->compare)(c->instance, request, req, check,
280 check_pairs, reply_pairs);
284 if (!req) return -1; /* doesn't exist, don't compare it */
286 return radius_compare_vps(request, check, req);
290 /** Find a comparison function for two attributes.
292 * @todo this should probably take DA's.
293 * @param attribute to find comparison function for.
294 * @return true if a comparison function was found, else false.
296 int radius_find_compare(DICT_ATTR const *attribute)
300 for (c = cmp; c; c = c->next) {
301 if (c->attribute == attribute) {
310 /** See what attribute we want to compare with.
312 * @param attribute to find comparison function for.
313 * @param from reference to compare with
314 * @return true if the comparison callback require a matching attribue in the request, else false.
316 static bool otherattr(DICT_ATTR const *attribute, DICT_ATTR const **from)
320 for (c = cmp; c; c = c->next) {
321 if (c->attribute == attribute) {
323 return c->first_only;
331 /** Register a function as compare function.
333 * @param name the attribute comparison to register
334 * @param from the attribute we want to compare with. Normally this is the same as attribute.
335 * If null call the comparison function on every attributes in the request if first_only is false
336 * @param first_only will decide if we loop over the request attributes or stop on the first one
337 * @param func comparison function
338 * @param instance argument to comparison function
341 int paircompare_register_byname(char const *name, DICT_ATTR const *from,
342 bool first_only, RAD_COMPARE_FUNC func, void *instance)
347 memset(&flags, 0, sizeof(flags));
350 da = dict_attrbyname(name);
352 if (!da->flags.compare) {
353 fr_strerror_printf("Attribute '%s' already exists.", name);
357 if (dict_addattr(name, -1, 0, from->type, flags) < 0) {
358 fr_strerror_printf("Failed creating attribute '%s'", name);
362 da = dict_attrbyname(name);
364 fr_strerror_printf("Failed finding attribute '%s'", name);
368 DEBUG("Creating attribute %s", name);
371 return paircompare_register(da, from, first_only, func, instance);
374 /** Register a function as compare function.
376 * @param attribute to register comparison function for.
377 * @param from the attribute we want to compare with. Normally this is the same as attribute.
378 * If null call the comparison function on every attributes in the request if first_only is false
379 * @param first_only will decide if we loop over the request attributes or stop on the first one
380 * @param func comparison function
381 * @param instance argument to comparison function
384 int paircompare_register(DICT_ATTR const *attribute, DICT_ATTR const *from,
385 bool first_only, RAD_COMPARE_FUNC func, void *instance)
389 rad_assert(attribute != NULL);
391 paircompare_unregister(attribute, func);
393 c = rad_malloc(sizeof(struct cmp));
396 c->attribute = attribute;
398 c->first_only = first_only;
399 c->instance = instance;
406 /** Unregister comparison function for an attribute
408 * @param attribute dict reference to unregister for.
409 * @param func comparison function to remove.
411 void paircompare_unregister(DICT_ATTR const *attribute, RAD_COMPARE_FUNC func)
413 struct cmp *c, *last;
416 for (c = cmp; c; c = c->next) {
417 if (c->attribute == attribute && c->compare == func) {
423 if (c == NULL) return;
426 last->next = c->next;
434 /** Unregister comparison function for a module
436 * All paircompare() functions for this module will be unregistered.
438 * @param instance the module instance
440 void paircompare_unregister_instance(void *instance)
442 struct cmp *c, **tail;
445 while ((c = *tail) != NULL) {
446 if (c->instance == instance) {
456 /** Compare two pair lists except for the password information.
458 * For every element in "check" at least one matching copy must be present
461 * @param[in] request Current request.
462 * @param[in] req_list request valuepairs.
463 * @param[in] check Check/control valuepairs.
464 * @param[in,out] rep_list Reply value pairs.
466 * @return 0 on match.
468 int paircompare(REQUEST *request, VALUE_PAIR *req_list, VALUE_PAIR *check,
469 VALUE_PAIR **rep_list)
472 VALUE_PAIR *check_item;
473 VALUE_PAIR *auth_item = NULL;
474 DICT_ATTR const *from;
480 for (check_item = fr_cursor_init(&cursor, &check);
482 check_item = fr_cursor_next(&cursor)) {
484 * If the user is setting a configuration value,
485 * then don't bother comparing it to any attributes
486 * sent to us by the user. It ALWAYS matches.
488 if ((check_item->op == T_OP_SET) ||
489 (check_item->op == T_OP_ADD)) {
493 if (!check_item->da->vendor) switch (check_item->da->attr) {
495 * Attributes we skip during comparison.
496 * These are "server" check items.
498 case PW_CRYPT_PASSWORD:
502 case PW_SESSION_TYPE:
503 case PW_STRIP_USER_NAME:
507 * IF the password attribute exists, THEN
508 * we can do comparisons against it. If not,
509 * then the request did NOT contain a
510 * User-Password attribute, so we CANNOT do
511 * comparisons against it.
513 * This hack makes CHAP-Password work..
515 case PW_USER_PASSWORD:
516 if (check_item->op == T_OP_CMP_EQ) {
517 WARN("Found User-Password == \"...\"");
518 WARN("Are you sure you don't mean Cleartext-Password?");
519 WARN("See \"man rlm_pap\" for more information");
521 if (fr_pair_find_by_num(req_list, PW_USER_PASSWORD, 0, TAG_ANY) == NULL) {
528 * See if this item is present in the request.
530 first_only = otherattr(check_item->da, &from);
532 auth_item = req_list;
535 while (auth_item != NULL) {
536 VERIFY_VP(auth_item);
537 if ((auth_item->da == from) || (!from)) {
540 auth_item = auth_item->next;
545 * Not found, it's not a match.
547 if (auth_item == NULL) {
549 * Didn't find it. If we were *trying*
550 * to not find it, then we succeeded.
552 if (check_item->op == T_OP_CMP_FALSE) {
560 * Else we found it, but we were trying to not
561 * find it, so we failed.
563 if (check_item->op == T_OP_CMP_FALSE) {
568 * We've got to xlat the string before doing
571 radius_xlat_do(request, check_item);
574 * OK it is present now compare them.
576 compare = radius_callback_compare(request, auth_item,
577 check_item, check, rep_list);
579 switch (check_item->op) {
582 RWDEBUG("Invalid operator '%s' for item %s: reverting to '=='",
583 fr_int2str(fr_tokens, check_item->op, "<INVALID>"), check_item->da->name);
588 if (compare != 0) result = -1;
592 if (compare == 0) result = -1;
596 if (compare >= 0) result = -1;
600 if (compare <= 0) result = -1;
604 if (compare > 0) result = -1;
608 if (compare < 0) result = -1;
614 if (compare != 0) result = -1;
617 } /* switch over the operator of the check item */
620 * This attribute didn't match, but maybe there's
621 * another of the same attribute, which DOES match.
623 if ((result != 0) && (!first_only)) {
624 fr_assert(auth_item != NULL);
625 auth_item = auth_item->next;
630 } /* for every entry in the check item list */
635 /** Expands an attribute marked with fr_pair_mark_xlat
637 * Writes the new value to the vp.
639 * @param request Current request.
640 * @param vp to expand.
641 * @return 0 if successful else -1 (on xlat failure) or -2 (on parse failure).
642 * On failure pair will still no longer be marked for xlat expansion.
644 int radius_xlat_do(REQUEST *request, VALUE_PAIR *vp)
648 char *expanded = NULL;
649 if (vp->type != VT_XLAT) return 0;
653 slen = radius_axlat(&expanded, request, vp->value.xlat, NULL, NULL);
654 rad_const_free(vp->value.xlat);
655 vp->value.xlat = NULL;
661 * Parse the string into a new value.
663 * If the VALUE_PAIR is being used in a regular expression
664 * then we just want to copy the new value in unmolested.
666 if ((vp->op == T_OP_REG_EQ) || (vp->op == T_OP_REG_NE)) {
667 fr_pair_value_strsteal(vp, expanded);
671 if (fr_pair_value_from_str(vp, expanded, -1) < 0){
672 talloc_free(expanded);
676 talloc_free(expanded);
681 /** Create a VALUE_PAIR and add it to a list of VALUE_PAIR s
683 * @note This function ALWAYS returns. If we're OOM, then it causes the
684 * @note server to exit, so you don't need to check the return value.
686 * @param[in] ctx for talloc
687 * @param[out] vps List to add new VALUE_PAIR to, if NULL will just
689 * @param[in] attribute number.
690 * @param[in] vendor number.
691 * @return a new VLAUE_PAIR or causes server to exit on error.
693 VALUE_PAIR *radius_pair_create(TALLOC_CTX *ctx, VALUE_PAIR **vps,
694 unsigned int attribute, unsigned int vendor)
698 vp = fr_pair_afrom_num(ctx, attribute, vendor);
701 rad_assert("No memory" == NULL);
705 if (vps) fr_pair_add(vps, vp);
710 /** Print a single valuepair to stderr or error log.
712 * @param[in] vp list to print.
714 void debug_pair(VALUE_PAIR *vp)
716 if (!vp || !rad_debug_lvl || !fr_log_fp) return;
718 vp_print(fr_log_fp, vp);
721 /** Print a single valuepair to stderr or error log.
723 * @param[in] level Debug level (1-4).
724 * @param[in] request to read logging params from.
725 * @param[in] vp to print.
726 * @param[in] prefix (optional).
728 void rdebug_pair(log_lvl_t level, REQUEST *request, VALUE_PAIR *vp, char const *prefix)
731 if (!vp || !request || !request->log.func) return;
733 if (!radlog_debug_enabled(L_DBG, level, request)) return;
735 vp_prints(buffer, sizeof(buffer), vp);
736 RDEBUGX(level, "%s%s", prefix ? prefix : "", buffer);
739 /** Print a list of VALUE_PAIRs.
741 * @param[in] level Debug level (1-4).
742 * @param[in] request to read logging params from.
743 * @param[in] vp to print.
744 * @param[in] prefix (optional).
746 void rdebug_pair_list(log_lvl_t level, REQUEST *request, VALUE_PAIR *vp, char const *prefix)
750 if (!vp || !request || !request->log.func) return;
752 if (!radlog_debug_enabled(L_DBG, level, request)) return;
755 for (vp = fr_cursor_init(&cursor, &vp);
757 vp = fr_cursor_next(&cursor)) {
760 vp_prints(buffer, sizeof(buffer), vp);
761 RDEBUGX(level, "%s%s", prefix ? prefix : "", buffer);
766 /** Print a list of protocol VALUE_PAIRs.
768 * @param[in] level Debug level (1-4).
769 * @param[in] request to read logging params from.
770 * @param[in] vp to print.
772 void rdebug_proto_pair_list(log_lvl_t level, REQUEST *request, VALUE_PAIR *vp)
776 if (!vp || !request || !request->log.func) return;
778 if (!radlog_debug_enabled(L_DBG, level, request)) return;
781 for (vp = fr_cursor_init(&cursor, &vp);
783 vp = fr_cursor_next(&cursor)) {
785 if ((vp->da->vendor == 0) &&
786 ((vp->da->attr & 0xFFFF) > 0xff)) continue;
787 vp_prints(buffer, sizeof(buffer), vp);
788 RDEBUGX(level, "%s", buffer);
793 /** Return a VP from the specified request.
795 * @param out where to write the pointer to the resolved VP.
796 * Will be NULL if the attribute couldn't be resolved.
797 * @param request current request.
798 * @param name attribute name including qualifiers.
799 * @return -4 if either the attribute or qualifier were invalid, and the same error codes as tmpl_find_vp for other
802 int radius_get_vp(VALUE_PAIR **out, REQUEST *request, char const *name)
809 if (tmpl_from_attr_str(&vpt, name, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) <= 0) {
813 rcode = tmpl_find_vp(out, request, &vpt);
818 /** Copy VP(s) from the specified request.
820 * @param ctx to alloc new VALUE_PAIRs in.
821 * @param out where to write the pointer to the copied 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
828 int radius_copy_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, char const *name)
835 if (tmpl_from_attr_str(&vpt, name, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false) <= 0) {
839 rcode = tmpl_copy_vps(ctx, out, request, &vpt);
844 void module_failure_msg(REQUEST *request, char const *fmt, ...)
849 vmodule_failure_msg(request, fmt, ap);
853 /** Add a module failure message VALUE_PAIR to the request
855 void vmodule_failure_msg(REQUEST *request, char const *fmt, va_list ap)
861 if (!fmt || !request || !request->packet) {
866 * If we don't copy the original ap we get a segfault from vasprintf. This is apparently
867 * due to ap sometimes being implemented with a stack offset which is invalidated if
868 * ap is passed into another function. See here:
869 * http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html
871 * I don't buy that explanation, but doing a va_copy here does prevent SEGVs seen when
872 * running unit tests which generate errors under CI.
875 p = talloc_vasprintf(request, fmt, aq);
878 MEM(vp = pair_make_request("Module-Failure-Message", NULL, T_OP_ADD));
879 if (request->module && (request->module[0] != '\0')) {
880 fr_pair_value_sprintf(vp, "%s: %s", request->module, p);
882 fr_pair_value_sprintf(vp, "%s", p);