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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * Copyright 2000 The FreeRADIUS server project
22 * Copyright 2000 Alan DeKok <aland@ox.org>
25 static const char rcsid[] = "$Id$";
28 #include "libradius.h"
34 #ifdef HAVE_NETINET_IN_H
35 # include <netinet/in.h>
42 * For POSIX Regular expressions.
43 * (0) Means no extended regular expressions.
44 * REG_EXTENDED means use extended regular expressions.
47 #define REG_EXTENDED (0)
60 void *instance; /* module instance */
61 RAD_COMPARE_FUNC compare;
64 static struct cmp *cmp;
68 * Compare 2 attributes. May call the attribute compare function.
70 static int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
71 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
80 if (request->attribute != check->attribute)
85 * Check for =* and !* and return appropriately
87 if( check->operator == T_OP_CMP_TRUE )
88 return 0; /* always return 0/EQUAL */
89 if( check->operator == T_OP_CMP_FALSE )
90 return 1; /* always return 1/NOT EQUAL */
93 * See if there is a special compare function.
95 for (c = cmp; c; c = c->next)
96 if (c->attribute == check->attribute)
97 return (c->compare)(c->instance, req, request, check,
98 check_pairs, reply_pairs);
100 switch(check->type) {
103 * Ascend binary attributes can be treated
104 * as opaque objects, I guess...
106 case PW_TYPE_ABINARY:
109 if (request->length != check->length) {
110 ret = 1; /* NOT equal */
113 ret = memcmp(request->strvalue, check->strvalue,
117 ret = strcmp((char *)request->strvalue,
118 (char *)check->strvalue);
120 case PW_TYPE_INTEGER:
122 ret = request->lvalue - check->lvalue;
125 ret = ntohl(request->lvalue) - ntohl(check->lvalue);
136 * See what attribute we want to compare with.
138 static int otherattr(int attr)
142 for (c = cmp; c; c = c->next) {
143 if (c->attribute == attr)
151 * Register a function as compare function.
152 * compare_attr is the attribute in the request we want to
153 * compare with. Normally this is the same as "attr".
154 * You can set this to:
156 * -1 the same as "attr"
157 * 0 always call compare function, not tied to request attribute
158 * >0 Attribute to compare with.
160 * For example, PW_GROUP in a check item needs to be compared
161 * with PW_USER_NAME in the incoming request.
163 int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
167 paircompare_unregister(attr, fun);
169 c = rad_malloc(sizeof(struct cmp));
171 if (compare_attr < 0)
175 c->otherattr = compare_attr;
176 c->instance = instance;
184 * Unregister a function.
186 void paircompare_unregister(int attr, RAD_COMPARE_FUNC fun)
188 struct cmp *c, *last;
191 for (c = cmp; c; c = c->next) {
192 if (c->attribute == attr && c->compare == fun)
197 if (c == NULL) return;
200 last->next = c->next;
208 * Compare two pair lists except for the password information.
209 * For every element in "check" at least one matching copy must
210 * be present in "reply".
214 int paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
216 VALUE_PAIR *check_item;
217 VALUE_PAIR *auth_item;
225 for (check_item = check; check_item != NULL; check_item = check_item->next) {
227 * If the user is setting a configuration value,
228 * then don't bother comparing it to any attributes
229 * sent to us by the user. It ALWAYS matches.
231 if ((check_item->operator == T_OP_SET) ||
232 (check_item->operator == T_OP_ADD)) {
236 switch (check_item->attribute) {
238 * Attributes we skip during comparison.
239 * These are "server" check items.
241 case PW_CRYPT_PASSWORD:
245 case PW_SESSION_TYPE:
246 case PW_STRIP_USER_NAME:
251 * IF the password attribute exists, THEN
252 * we can do comparisons against it. If not,
253 * then the request did NOT contain a
254 * User-Password attribute, so we CANNOT do
255 * comparisons against it.
257 * This hack makes CHAP-Password work..
260 if (pairfind(request, PW_PASSWORD) == NULL) {
267 * See if this item is present in the request.
269 other = otherattr(check_item->attribute);
273 for (; auth_item != NULL; auth_item = auth_item->next) {
274 if (auth_item->attribute == other || other == 0)
279 * Not found, it's not a match.
281 if (auth_item == NULL) {
283 * Didn't find it. If we were *trying*
284 * to not find it, then we succeeded.
286 if (check_item->operator == T_OP_CMP_FALSE)
293 * Else we found it, but we were trying to not
294 * find it, so we failed.
296 if (check_item->operator == T_OP_CMP_FALSE)
301 * We've got to xlat the string before doing
304 if (check_item->flags.do_xlat) {
306 char buffer[sizeof(check_item->strvalue)];
308 check_item->flags.do_xlat = 0;
309 rcode = radius_xlat(buffer, sizeof(buffer),
310 check_item->strvalue,
314 * Parse the string into a new value.
316 pairparsevalue(check_item, buffer);
320 * OK it is present now compare them.
322 compare = paircompare(req, auth_item, check_item, check, reply);
324 switch (check_item->operator) {
327 radlog(L_ERR, "Invalid operator for item %s: "
328 "reverting to '=='", check_item->name);
330 case T_OP_CMP_TRUE: /* compare always == 0 */
331 case T_OP_CMP_FALSE: /* compare always == 1 */
333 if (compare != 0) result = -1;
337 if (compare == 0) result = -1;
341 if (compare >= 0) result = -1;
345 if (compare <= 0) result = -1;
349 if (compare > 0) result = -1;
353 if (compare < 0) result = -1;
360 regmatch_t rxmatch[9];
363 * Include substring matches.
365 regcomp(®, (char *)check_item->strvalue,
367 compare = regexec(®,
368 (char *)auth_item->strvalue,
369 REQUEST_MAX_REGEX + 1,
374 * Add %{0}, %{1}, etc.
376 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
378 char buffer[sizeof(check_item->strvalue)];
381 * Didn't match: delete old
382 * match, if it existed.
384 if ((compare != 0) ||
385 (rxmatch[i].rm_so == -1)) {
386 p = request_data_get(req, req,
387 REQUEST_DATA_REGEX | i);
401 * Copy substring into buffer.
404 auth_item->strvalue + rxmatch[i].rm_so,
405 rxmatch[i].rm_eo - rxmatch[i].rm_so);
406 buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
409 * Copy substring, and add it to
412 * Note that we don't check
413 * for out of memory, which is
414 * the only error we can get...
417 request_data_add(req,
419 REQUEST_DATA_REGEX | i,
423 if (compare != 0) result = -1;
427 regcomp(®, (char *)check_item->strvalue, REG_EXTENDED|REG_NOSUB);
428 compare = regexec(®, (char *)auth_item->strvalue,
431 if (compare == 0) result = -1;
435 } /* switch over the operator of the check item */
438 * This attribute didn't match, but maybe there's
439 * another of the same attribute, which DOES match.
442 auth_item = auth_item->next;
447 } /* for every entry in the check item list */
449 return 0; /* it matched */
453 * Compare two attributes simply. Calls paircompare.
456 int simplepaircmp(REQUEST *req, VALUE_PAIR *first, VALUE_PAIR *second)
458 return paircompare( req, first, second, NULL, NULL );
463 * Compare a Connect-Info and a Connect-Rate
465 static int connectcmp(void *instance,
469 VALUE_PAIR *check_pairs,
470 VALUE_PAIR **reply_pairs)
475 check_pairs = check_pairs; /* shut the compiler up */
476 reply_pairs = reply_pairs;
478 rate = atoi((char *)request->strvalue);
479 return rate - check->lvalue;
484 * Compare a portno with a range.
486 static int portcmp(void *instance,
487 REQUEST *req UNUSED, VALUE_PAIR *request, VALUE_PAIR *check,
488 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
490 char buf[MAX_STRING_LEN];
493 uint32_t port = request->lvalue;
496 check_pairs = check_pairs; /* shut the compiler up */
497 reply_pairs = reply_pairs;
499 if ((strchr((char *)check->strvalue, ',') == NULL) &&
500 (strchr((char *)check->strvalue, '-') == NULL)) {
501 return (request->lvalue - check->lvalue);
505 strcpy(buf, (char *)check->strvalue);
506 s = strtok(buf, ",");
509 if ((p = strchr(s, '-')) != NULL)
513 lo = strtoul(s, NULL, 10);
514 hi = strtoul(p, NULL, 10);
515 if (lo <= port && port <= hi) {
518 s = strtok(NULL, ",");
525 * Compare prefix/suffix.
528 * - if PW_STRIP_USER_NAME is present in check_pairs,
529 * strip the username of prefix/suffix.
530 * - if PW_STRIP_USER_NAME is not present in check_pairs,
531 * add a PW_STRIPPED_USER_NAME to the request.
533 static int presufcmp(void *instance,
535 VALUE_PAIR *request, VALUE_PAIR *check,
536 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
539 char *name = (char *)request->strvalue;
540 char rest[MAX_STRING_LEN];
545 reply_pairs = reply_pairs; /* shut the compiler up */
548 printf("Comparing %s and %s, check->attr is %d\n",
549 name, check->strvalue, check->attribute);
552 len = strlen((char *)check->strvalue);
553 switch (check->attribute) {
555 ret = strncmp(name, (char *)check->strvalue, len);
556 if (ret == 0 && rest)
557 strcpy(rest, name + len);
560 namelen = strlen(name);
563 ret = strcmp(name + namelen - len,
564 (char *)check->strvalue);
565 if (ret == 0 && rest) {
566 strNcpy(rest, name, namelen - len + 1);
573 if ((vp = pairfind(check_pairs, PW_STRIP_USER_NAME)) != NULL) {
574 if (vp->lvalue == 1) {
576 * I don't think we want to update the User-Name
577 * attribute in place... - atd
579 strcpy((char *)request->strvalue, rest);
580 request->length = strlen(rest);
585 if ((vp = pairfind(check_pairs, PW_STRIPPED_USER_NAME)) != NULL){
586 strcpy((char *)vp->strvalue, rest);
587 vp->length = strlen(rest);
588 } else if ((vp = paircreate(PW_STRIPPED_USER_NAME,
589 PW_TYPE_STRING)) != NULL) {
590 strcpy((char *)vp->strvalue, rest);
591 vp->length = strlen(rest);
592 pairadd(&request, vp);
593 } /* else no memory! Die, die!: FIXME!! */
601 * Compare the current time to a range.
603 static int timecmp(void *instance,
605 VALUE_PAIR *request, VALUE_PAIR *check,
606 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
609 request = request; /* shut the compiler up */
610 check_pairs = check_pairs;
611 reply_pairs = reply_pairs;
613 if (timestr_match((char *)check->strvalue,
614 req ? req->timestamp : time(NULL)) >= 0) {
621 * Matches if there is NO SUCH ATTRIBUTE as the one named
622 * in check->strvalue. If there IS such an attribute, it
625 * This is ugly, and definitely non-optimal. We should be
626 * doing the lookup only ONCE, and storing the result
627 * in check->lvalue...
629 static int attrcmp(void *instance,
631 VALUE_PAIR *request, VALUE_PAIR *check,
632 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
639 check_pairs = check_pairs; /* shut the compiler up */
640 reply_pairs = reply_pairs;
642 if (check->lvalue == 0) {
643 dict = dict_attrbyname((char *)check->strvalue);
649 attr = check->lvalue;
653 * If there's no such attribute, then return MATCH,
656 pair = pairfind(request, attr);
665 * Compare the expiration date.
667 static int expirecmp(void *instance, REQUEST *req UNUSED,
668 VALUE_PAIR *request, VALUE_PAIR *check,
669 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
674 request = request; /* shut the compiler up */
675 check_pairs = check_pairs;
676 reply_pairs = reply_pairs;
679 * FIXME! This should be request->timestamp!
683 if (now <= (signed)check->lvalue) {
691 * Compare the request packet type.
693 static int packetcmp(void *instance UNUSED, REQUEST *req,
694 VALUE_PAIR *request UNUSED,
696 VALUE_PAIR *check_pairs UNUSED,
697 VALUE_PAIR **reply_pairs UNUSED)
699 if (req->packet->code == check->lvalue) {
707 * Compare the response packet type.
709 static int responsecmp(void *instance UNUSED,
711 VALUE_PAIR *request UNUSED,
713 VALUE_PAIR *check_pairs UNUSED,
714 VALUE_PAIR **reply_pairs UNUSED)
716 if (req->reply->code == check->lvalue) {
724 * Register server-builtin special attributes.
726 void pair_builtincompare_init(void)
728 paircompare_register(PW_NAS_PORT, -1, portcmp, NULL);
729 paircompare_register(PW_PREFIX, PW_USER_NAME, presufcmp, NULL);
730 paircompare_register(PW_SUFFIX, PW_USER_NAME, presufcmp, NULL);
731 paircompare_register(PW_CONNECT_RATE, PW_CONNECT_INFO, connectcmp, NULL);
732 paircompare_register(PW_CURRENT_TIME, 0, timecmp, NULL);
733 paircompare_register(PW_NO_SUCH_ATTRIBUTE, 0, attrcmp, NULL);
734 paircompare_register(PW_EXPIRATION, 0, expirecmp, NULL);
735 paircompare_register(PW_PACKET_TYPE, 0, packetcmp, NULL);
736 paircompare_register(PW_RESPONSE_PACKET_TYPE, 0, responsecmp, NULL);
739 void paircompare_builtin_free(void)
741 struct cmp *c, *next;
743 for (c = cmp; c != NULL; c = next) {
752 * Move pairs, replacing/over-writing them, and doing xlat.
755 * Move attributes from one list to the other
756 * if not already present.
758 void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
760 VALUE_PAIR **tailto, *i, *j, *next;
761 VALUE_PAIR *tailfrom = NULL;
765 * Point "tailto" to the end of the "to" list.
768 for(i = *to; i; i = i->next) {
773 * Loop over the "from" list.
775 for(i = *from; i; i = next) {
779 * Don't move 'fallthrough' over.
781 if (i->attribute == PW_FALL_THROUGH) {
786 * We've got to xlat the string before moving
789 if (i->flags.do_xlat) {
791 char buffer[sizeof(i->strvalue)];
793 i->flags.do_xlat = 0;
794 rcode = radius_xlat(buffer, sizeof(buffer),
799 * Parse the string into a new value.
801 pairparsevalue(i, buffer);
804 found = pairfind(*to, i->attribute);
805 switch (i->operator) {
808 * If a similar attribute is found,
811 case T_OP_SUB: /* -= */
813 if (!i->strvalue[0] ||
814 (strcmp((char *)found->strvalue,
815 (char *)i->strvalue) == 0)){
816 pairdelete(to, found->attribute);
819 * 'tailto' may have been
823 for(j = *to; j; j = j->next) {
833 * Add it, if it's not already there.
835 case T_OP_EQ: /* = */
838 continue; /* with the loop */
843 * If a similar attribute is found,
844 * replace it with the new one. Otherwise,
845 * add the new one to the list.
847 case T_OP_SET: /* := */
852 memcpy(found, i, sizeof(*found));
859 * FIXME: Add support for <=, >=, <, >
861 * which will mean (for integers)
862 * 'make the attribute the smaller, etc'
866 * Add the new element to the list, even
867 * if similar ones already exist.
870 case T_OP_ADD: /* += */
875 tailfrom->next = next;
880 * If ALL of the 'to' attributes have been deleted,
881 * then ensure that the 'tail' is updated to point
892 } /* loop over the 'from' list */