2 * valuepair.c Valuepair functions that are radiusd-specific
3 * and as such do not belong in the library.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * Copyright 2000,2006 The FreeRADIUS server project
22 * Copyright 2000 Alan DeKok <aland@ox.org>
25 #include <freeradius-devel/ident.h>
28 #include <freeradius-devel/radiusd.h>
29 #include <freeradius-devel/rad_assert.h>
35 * For POSIX Regular expressions.
36 * (0) Means no extended regular expressions.
37 * REG_EXTENDED means use extended regular expressions.
40 #define REG_EXTENDED (0)
51 void *instance; /* module instance */
52 RAD_COMPARE_FUNC compare;
55 static struct cmp *cmp;
57 int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
62 * Check for =* and !* and return appropriately
64 if( check->operator == T_OP_CMP_TRUE )
66 if( check->operator == T_OP_CMP_FALSE )
70 if (check->operator == T_OP_REG_EQ) {
75 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
77 snprintf(name, sizeof(name), "%%{%s}", check->name);
78 radius_xlat(value, sizeof(value), name, request, NULL);
81 * Include substring matches.
83 compare = regcomp(®, check->vp_strvalue, REG_EXTENDED);
86 regerror(compare, ®, buffer, sizeof(buffer));
88 RDEBUG("Invalid regular expression %s: %s",
89 check->vp_strvalue, buffer);
92 compare = regexec(®, value, REQUEST_MAX_REGEX + 1,
97 * Add %{0}, %{1}, etc.
99 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
101 char buffer[sizeof(check->vp_strvalue)];
104 * Didn't match: delete old
105 * match, if it existed.
107 if ((compare != 0) ||
108 (rxmatch[i].rm_so == -1)) {
109 p = request_data_get(request, request,
110 REQUEST_DATA_REGEX | i);
124 * Copy substring into buffer.
126 memcpy(buffer, value + rxmatch[i].rm_so,
127 rxmatch[i].rm_eo - rxmatch[i].rm_so);
128 buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
131 * Copy substring, and add it to
134 * Note that we don't check
135 * for out of memory, which is
136 * the only error we can get...
139 request_data_add(request, request,
140 REQUEST_DATA_REGEX | i,
143 if (compare == 0) return 0;
147 if (check->operator == T_OP_REG_NE) {
152 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
154 snprintf(name, sizeof(name), "%%{%s}", check->name);
155 radius_xlat(value, sizeof(value), name, request, NULL);
158 * Include substring matches.
160 compare = regcomp(®, (char *)check->vp_strvalue,
164 regerror(compare, ®, buffer, sizeof(buffer));
166 RDEBUG("Invalid regular expression %s: %s",
167 check->vp_strvalue, buffer);
170 compare = regexec(®, value, REQUEST_MAX_REGEX + 1,
174 if (compare != 0) return 0;
181 * Tagged attributes are equal if and only if both the
182 * tag AND value match.
184 if (check->flags.has_tag) {
185 ret = ((int) vp->flags.tag) - ((int) check->flags.tag);
186 if (ret != 0) return ret;
190 * Not a regular expression, compare the types.
192 switch(check->type) {
195 * Ascend binary attributes can be treated
196 * as opaque objects, I guess...
198 case PW_TYPE_ABINARY:
201 if (vp->length != check->length) {
202 ret = 1; /* NOT equal */
205 ret = memcmp(vp->vp_strvalue, check->vp_strvalue,
209 ret = strcmp((char *)vp->vp_strvalue,
210 (char *)check->vp_strvalue);
214 case PW_TYPE_INTEGER:
215 ret = vp->vp_integer - check->vp_integer;
218 ret = vp->vp_date - check->vp_date;
221 ret = ntohl(vp->vp_ipaddr) - ntohl(check->vp_ipaddr);
223 case PW_TYPE_IPV6ADDR:
224 ret = memcmp(&vp->vp_ipv6addr, &check->vp_ipv6addr,
225 sizeof(vp->vp_ipv6addr));
228 case PW_TYPE_IPV6PREFIX:
229 ret = memcmp(&vp->vp_ipv6prefix, &check->vp_ipv6prefix,
230 sizeof(vp->vp_ipv6prefix));
234 ret = memcmp(&vp->vp_ifid, &check->vp_ifid,
235 sizeof(vp->vp_ifid));
247 * Compare 2 attributes. May call the attribute compare function.
249 int radius_callback_compare(REQUEST *req, VALUE_PAIR *request,
250 VALUE_PAIR *check, VALUE_PAIR *check_pairs,
251 VALUE_PAIR **reply_pairs)
256 * Check for =* and !* and return appropriately
258 if( check->operator == T_OP_CMP_TRUE )
259 return 0; /* always return 0/EQUAL */
260 if( check->operator == T_OP_CMP_FALSE )
261 return 1; /* always return 1/NOT EQUAL */
264 * See if there is a special compare function.
266 * FIXME: use new RB-Tree code.
268 for (c = cmp; c; c = c->next)
269 if ((c->attribute == check->attribute) &&
270 (check->vendor == 0)) {
271 return (c->compare)(c->instance, req, request, check,
272 check_pairs, reply_pairs);
275 if (!request) return -1; /* doesn't exist, don't compare it */
277 return radius_compare_vps(req, check, request);
282 * Find a comparison function for two attributes.
284 int radius_find_compare(int attribute)
288 for (c = cmp; c; c = c->next) {
289 if (c->attribute == attribute) {
299 * See what attribute we want to compare with.
301 static int otherattr(int attr)
305 for (c = cmp; c; c = c->next) {
306 if (c->attribute == attr)
314 * Register a function as compare function.
315 * compare_attr is the attribute in the request we want to
316 * compare with. Normally this is the same as "attr".
317 * You can set this to:
319 * -1 the same as "attr"
320 * 0 always call compare function, not tied to request attribute
321 * >0 Attribute to compare with.
323 * For example, PW_GROUP in a check item needs to be compared
324 * with PW_USER_NAME in the incoming request.
326 int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
330 paircompare_unregister(attr, fun);
332 c = rad_malloc(sizeof(struct cmp));
336 c->otherattr = compare_attr;
337 c->instance = instance;
345 * Unregister a function.
347 void paircompare_unregister(int attr, RAD_COMPARE_FUNC fun)
349 struct cmp *c, *last;
352 for (c = cmp; c; c = c->next) {
353 if (c->attribute == attr && c->compare == fun)
358 if (c == NULL) return;
361 last->next = c->next;
369 * Compare two pair lists except for the password information.
370 * For every element in "check" at least one matching copy must
371 * be present in "reply".
375 int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
377 VALUE_PAIR *check_item;
378 VALUE_PAIR *auth_item;
383 for (check_item = check; check_item != NULL; check_item = check_item->next) {
385 * If the user is setting a configuration value,
386 * then don't bother comparing it to any attributes
387 * sent to us by the user. It ALWAYS matches.
389 if ((check_item->operator == T_OP_SET) ||
390 (check_item->operator == T_OP_ADD)) {
394 switch (check_item->attribute) {
396 * Attributes we skip during comparison.
397 * These are "server" check items.
399 case PW_CRYPT_PASSWORD:
403 case PW_SESSION_TYPE:
404 case PW_STRIP_USER_NAME:
409 * IF the password attribute exists, THEN
410 * we can do comparisons against it. If not,
411 * then the request did NOT contain a
412 * User-Password attribute, so we CANNOT do
413 * comparisons against it.
415 * This hack makes CHAP-Password work..
417 case PW_USER_PASSWORD:
418 if (check_item->operator == T_OP_CMP_EQ) {
419 DEBUG("WARNING: Found User-Password == \"...\".");
420 DEBUG("WARNING: Are you sure you don't mean Cleartext-Password?");
421 DEBUG("WARNING: See \"man rlm_pap\" for more information.");
423 if (pairfind(request, PW_USER_PASSWORD, 0) == NULL) {
430 * See if this item is present in the request.
432 other = otherattr(check_item->attribute);
437 for (; auth_item != NULL; auth_item = auth_item->next) {
438 if (auth_item->attribute == other || other == 0)
444 * Not found, it's not a match.
446 if (auth_item == NULL) {
448 * Didn't find it. If we were *trying*
449 * to not find it, then we succeeded.
451 if (check_item->operator == T_OP_CMP_FALSE)
458 * Else we found it, but we were trying to not
459 * find it, so we failed.
461 if (check_item->operator == T_OP_CMP_FALSE)
466 * We've got to xlat the string before doing
469 if (check_item->flags.do_xlat) {
471 char buffer[sizeof(check_item->vp_strvalue)];
473 check_item->flags.do_xlat = 0;
474 rcode = radius_xlat(buffer, sizeof(buffer),
475 check_item->vp_strvalue,
479 * Parse the string into a new value.
481 pairparsevalue(check_item, buffer);
485 * OK it is present now compare them.
487 compare = radius_callback_compare(req, auth_item, check_item,
490 switch (check_item->operator) {
493 radlog(L_INFO, "Invalid operator for item %s: "
494 "reverting to '=='", check_item->name);
496 case T_OP_CMP_TRUE: /* compare always == 0 */
497 case T_OP_CMP_FALSE: /* compare always == 1 */
499 if (compare != 0) result = -1;
503 if (compare == 0) result = -1;
507 if (compare >= 0) result = -1;
511 if (compare <= 0) result = -1;
515 if (compare > 0) result = -1;
519 if (compare < 0) result = -1;
528 } /* switch over the operator of the check item */
531 * This attribute didn't match, but maybe there's
532 * another of the same attribute, which DOES match.
534 if ((result != 0) && (other >= 0)) {
535 auth_item = auth_item->next;
540 } /* for every entry in the check item list */
546 * Move pairs, replacing/over-writing them, and doing xlat.
549 * Move attributes from one list to the other
550 * if not already present.
552 void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
554 VALUE_PAIR **tailto, *i, *j, *next;
555 VALUE_PAIR *tailfrom = NULL;
559 * Point "tailto" to the end of the "to" list.
562 for(i = *to; i; i = i->next) {
567 * Loop over the "from" list.
569 for(i = *from; i; i = next) {
573 * Don't move 'fallthrough' over.
575 if (i->attribute == PW_FALL_THROUGH) {
580 * We've got to xlat the string before moving
583 if (i->flags.do_xlat) {
585 char buffer[sizeof(i->vp_strvalue)];
587 i->flags.do_xlat = 0;
588 rcode = radius_xlat(buffer, sizeof(buffer),
593 * Parse the string into a new value.
595 pairparsevalue(i, buffer);
598 found = pairfind(*to, i->attribute, i->vendor);
599 switch (i->operator) {
602 * If a similar attribute is found,
605 case T_OP_SUB: /* -= */
607 if (!i->vp_strvalue[0] ||
608 (strcmp((char *)found->vp_strvalue,
609 (char *)i->vp_strvalue) == 0)){
610 pairdelete(to, found->attribute, found->vendor);
613 * 'tailto' may have been
617 for(j = *to; j; j = j->next) {
627 * Add it, if it's not already there.
629 case T_OP_EQ: /* = */
632 continue; /* with the loop */
637 * If a similar attribute is found,
638 * replace it with the new one. Otherwise,
639 * add the new one to the list.
641 case T_OP_SET: /* := */
646 memcpy(found, i, sizeof(*found));
653 * FIXME: Add support for <=, >=, <, >
655 * which will mean (for integers)
656 * 'make the attribute the smaller, etc'
660 * Add the new element to the list, even
661 * if similar ones already exist.
664 case T_OP_ADD: /* += */
669 tailfrom->next = next;
674 * If ALL of the 'to' attributes have been deleted,
675 * then ensure that the 'tail' is updated to point
686 } /* loop over the 'from' list */
690 * Create a pair, and add it to a particular list of VPs
692 * Note that this function ALWAYS returns. If we're OOM, then
693 * it causes the server to exit!
695 VALUE_PAIR *radius_paircreate(REQUEST *request, VALUE_PAIR **vps,
696 int attribute, int vendor, int type)
700 request = request; /* -Wunused */
702 vp = paircreate(attribute, vendor, type);
704 radlog(L_ERR, "No memory!");
705 rad_assert("No memory" == NULL);
709 if (vps) pairadd(vps, vp);
715 * Create a pair, and add it to a particular list of VPs
717 * Note that this function ALWAYS returns. If we're OOM, then
718 * it causes the server to exit!
720 VALUE_PAIR *radius_pairmake(REQUEST *request, VALUE_PAIR **vps,
721 const char *attribute, const char *value,
726 request = request; /* -Wunused */
728 vp = pairmake(attribute, value, operator);
729 if (!vp) return NULL;
731 if (vps) pairadd(vps, vp);
736 void debug_pair(VALUE_PAIR *vp)
738 if (!vp || !debug_flag || !fr_log_fp) return;
740 fputc('\t', fr_log_fp);
741 vp_print(fr_log_fp, vp);
742 fputc('\n', fr_log_fp);
745 void debug_pair_list(VALUE_PAIR *vp)
747 if (!vp || !debug_flag || !fr_log_fp) return;
750 fputc('\t', fr_log_fp);
751 vp_print(fr_log_fp, vp);
752 fputc('\n', fr_log_fp);