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>
34 * For POSIX Regular expressions.
35 * (0) Means no extended regular expressions.
36 * REG_EXTENDED means use extended regular expressions.
39 #define REG_EXTENDED (0)
50 void *instance; /* module instance */
51 RAD_COMPARE_FUNC compare;
54 static struct cmp *cmp;
58 * Compare 2 attributes. May call the attribute compare function.
60 static int compare_pair(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
61 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
67 * Check for =* and !* and return appropriately
69 if( check->operator == T_OP_CMP_TRUE )
70 return 0; /* always return 0/EQUAL */
71 if( check->operator == T_OP_CMP_FALSE )
72 return 1; /* always return 1/NOT EQUAL */
75 * See if there is a special compare function.
77 * FIXME: use new RB-Tree code.
79 for (c = cmp; c; c = c->next)
80 if (c->attribute == check->attribute)
81 return (c->compare)(c->instance, req, request, check,
82 check_pairs, reply_pairs);
84 if (!request) return -1; /* doesn't exist, don't compare it */
89 * Ascend binary attributes can be treated
90 * as opaque objects, I guess...
95 if (request->length != check->length) {
96 ret = 1; /* NOT equal */
99 ret = memcmp(request->vp_strvalue, check->vp_strvalue,
103 if (check->flags.caseless) {
104 ret = strcasecmp((char *)request->vp_strvalue,
105 (char *)check->vp_strvalue);
107 ret = strcmp((char *)request->vp_strvalue,
108 (char *)check->vp_strvalue);
111 case PW_TYPE_INTEGER:
113 ret = request->lvalue - check->lvalue;
116 ret = ntohl(request->vp_ipaddr) - ntohl(check->vp_ipaddr);
118 case PW_TYPE_IPV6ADDR:
119 ret = memcmp(&request->vp_ipv6addr, &check->vp_ipv6addr,
120 sizeof(request->vp_ipv6addr));
123 case PW_TYPE_IPV6PREFIX:
124 ret = memcmp(&request->vp_ipv6prefix, &check->vp_ipv6prefix,
125 sizeof(request->vp_ipv6prefix));
129 ret = memcmp(&request->vp_ifid, &check->vp_ifid,
130 sizeof(request->vp_ifid));
142 * See what attribute we want to compare with.
144 static int otherattr(int attr)
148 for (c = cmp; c; c = c->next) {
149 if (c->attribute == attr)
157 * Register a function as compare function.
158 * compare_attr is the attribute in the request we want to
159 * compare with. Normally this is the same as "attr".
160 * You can set this to:
162 * -1 the same as "attr"
163 * 0 always call compare function, not tied to request attribute
164 * >0 Attribute to compare with.
166 * For example, PW_GROUP in a check item needs to be compared
167 * with PW_USER_NAME in the incoming request.
169 int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
173 paircompare_unregister(attr, fun);
175 c = rad_malloc(sizeof(struct cmp));
177 if (compare_attr < 0)
181 c->otherattr = compare_attr;
182 c->instance = instance;
190 * Unregister a function.
192 void paircompare_unregister(int attr, RAD_COMPARE_FUNC fun)
194 struct cmp *c, *last;
197 for (c = cmp; c; c = c->next) {
198 if (c->attribute == attr && c->compare == fun)
203 if (c == NULL) return;
206 last->next = c->next;
214 * Compare two pair lists except for the password information.
215 * For every element in "check" at least one matching copy must
216 * be present in "reply".
220 int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
222 VALUE_PAIR *check_item;
223 VALUE_PAIR *auth_item;
231 for (check_item = check; check_item != NULL; check_item = check_item->next) {
233 * If the user is setting a configuration value,
234 * then don't bother comparing it to any attributes
235 * sent to us by the user. It ALWAYS matches.
237 if ((check_item->operator == T_OP_SET) ||
238 (check_item->operator == T_OP_ADD)) {
242 switch (check_item->attribute) {
244 * Attributes we skip during comparison.
245 * These are "server" check items.
247 case PW_CRYPT_PASSWORD:
251 case PW_SESSION_TYPE:
252 case PW_STRIP_USER_NAME:
257 * IF the password attribute exists, THEN
258 * we can do comparisons against it. If not,
259 * then the request did NOT contain a
260 * User-Password attribute, so we CANNOT do
261 * comparisons against it.
263 * This hack makes CHAP-Password work..
265 case PW_USER_PASSWORD:
266 if (pairfind(request, PW_USER_PASSWORD) == NULL) {
273 * See if this item is present in the request.
275 other = otherattr(check_item->attribute);
279 for (; auth_item != NULL; auth_item = auth_item->next) {
280 if (auth_item->attribute == other || other == 0)
285 * Not found, it's not a match.
287 if (auth_item == NULL) {
289 * Didn't find it. If we were *trying*
290 * to not find it, then we succeeded.
292 if (check_item->operator == T_OP_CMP_FALSE)
299 * Else we found it, but we were trying to not
300 * find it, so we failed.
302 if (check_item->operator == T_OP_CMP_FALSE)
307 * We've got to xlat the string before doing
310 if (check_item->flags.do_xlat) {
312 char buffer[sizeof(check_item->vp_strvalue)];
314 check_item->flags.do_xlat = 0;
315 rcode = radius_xlat(buffer, sizeof(buffer),
316 check_item->vp_strvalue,
320 * Parse the string into a new value.
322 pairparsevalue(check_item, buffer);
326 * OK it is present now compare them.
328 compare = compare_pair(req, auth_item, check_item, check, reply);
330 switch (check_item->operator) {
333 radlog(L_INFO, "Invalid operator for item %s: "
334 "reverting to '=='", check_item->name);
336 case T_OP_CMP_TRUE: /* compare always == 0 */
337 case T_OP_CMP_FALSE: /* compare always == 1 */
339 if (compare != 0) result = -1;
343 if (compare == 0) result = -1;
347 if (compare >= 0) result = -1;
351 if (compare <= 0) result = -1;
355 if (compare > 0) result = -1;
359 if (compare < 0) result = -1;
366 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
368 if ((auth_item->type == PW_TYPE_IPADDR) &&
369 (auth_item->vp_strvalue[0] == '\0')) {
370 inet_ntop(AF_INET, &(auth_item->lvalue),
371 auth_item->vp_strvalue,
372 sizeof(auth_item->vp_strvalue));
376 * Include substring matches.
378 regcomp(®, (char *)check_item->vp_strvalue,
380 compare = regexec(®,
381 (char *)auth_item->vp_strvalue,
382 REQUEST_MAX_REGEX + 1,
387 * Add %{0}, %{1}, etc.
389 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
391 char buffer[sizeof(check_item->vp_strvalue)];
394 * Didn't match: delete old
395 * match, if it existed.
397 if ((compare != 0) ||
398 (rxmatch[i].rm_so == -1)) {
399 p = request_data_get(req, req,
400 REQUEST_DATA_REGEX | i);
414 * Copy substring into buffer.
417 auth_item->vp_strvalue + rxmatch[i].rm_so,
418 rxmatch[i].rm_eo - rxmatch[i].rm_so);
419 buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
422 * Copy substring, and add it to
425 * Note that we don't check
426 * for out of memory, which is
427 * the only error we can get...
430 request_data_add(req,
432 REQUEST_DATA_REGEX | i,
436 if (compare != 0) result = -1;
440 if ((auth_item->type == PW_TYPE_IPADDR) &&
441 (auth_item->vp_strvalue[0] == '\0')) {
442 inet_ntop(AF_INET, &(auth_item->lvalue),
443 auth_item->vp_strvalue,
444 sizeof(auth_item->vp_strvalue));
447 regcomp(®, (char *)check_item->vp_strvalue, REG_EXTENDED|REG_NOSUB);
448 compare = regexec(®, (char *)auth_item->vp_strvalue,
451 if (compare == 0) result = -1;
455 } /* switch over the operator of the check item */
458 * This attribute didn't match, but maybe there's
459 * another of the same attribute, which DOES match.
462 auth_item = auth_item->next;
467 } /* for every entry in the check item list */
469 return 0; /* it matched */
473 * Compare two attributes simply. Calls compare_pair.
476 int simplepaircmp(REQUEST *req, VALUE_PAIR *first, VALUE_PAIR *second)
478 return compare_pair( req, first, second, NULL, NULL );
483 * Move pairs, replacing/over-writing them, and doing xlat.
486 * Move attributes from one list to the other
487 * if not already present.
489 void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
491 VALUE_PAIR **tailto, *i, *j, *next;
492 VALUE_PAIR *tailfrom = NULL;
496 * Point "tailto" to the end of the "to" list.
499 for(i = *to; i; i = i->next) {
504 * Loop over the "from" list.
506 for(i = *from; i; i = next) {
510 * Don't move 'fallthrough' over.
512 if (i->attribute == PW_FALL_THROUGH) {
517 * We've got to xlat the string before moving
520 if (i->flags.do_xlat) {
522 char buffer[sizeof(i->vp_strvalue)];
524 i->flags.do_xlat = 0;
525 rcode = radius_xlat(buffer, sizeof(buffer),
530 * Parse the string into a new value.
532 pairparsevalue(i, buffer);
535 found = pairfind(*to, i->attribute);
536 switch (i->operator) {
539 * If a similar attribute is found,
542 case T_OP_SUB: /* -= */
544 if (!i->vp_strvalue[0] ||
545 (strcmp((char *)found->vp_strvalue,
546 (char *)i->vp_strvalue) == 0)){
547 pairdelete(to, found->attribute);
550 * 'tailto' may have been
554 for(j = *to; j; j = j->next) {
564 * Add it, if it's not already there.
566 case T_OP_EQ: /* = */
569 continue; /* with the loop */
574 * If a similar attribute is found,
575 * replace it with the new one. Otherwise,
576 * add the new one to the list.
578 case T_OP_SET: /* := */
583 memcpy(found, i, sizeof(*found));
590 * FIXME: Add support for <=, >=, <, >
592 * which will mean (for integers)
593 * 'make the attribute the smaller, etc'
597 * Add the new element to the list, even
598 * if similar ones already exist.
601 case T_OP_ADD: /* += */
606 tailfrom->next = next;
611 * If ALL of the 'to' attributes have been deleted,
612 * then ensure that the 'tail' is updated to point
623 } /* loop over the 'from' list */