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$";
33 #ifdef HAVE_NETINET_IN_H
34 # include <netinet/in.h>
41 * For POSIX Regular expressions.
42 * (0) Means no extended regular expressions.
43 * REG_EXTENDED means use extended regular expressions.
46 #define REG_EXTENDED (0)
59 void *instance; /* module instance */
60 RAD_COMPARE_FUNC compare;
63 static struct cmp *cmp;
67 * Compare 2 attributes. May call the attribute compare function.
69 static int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
70 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
79 if (request->attribute != check->attribute)
84 * Check for =* and !* and return appropriately
86 if( check->operator == T_OP_CMP_TRUE )
87 return 0; /* always return 0/EQUAL */
88 if( check->operator == T_OP_CMP_FALSE )
89 return 1; /* always return 1/NOT EQUAL */
92 * See if there is a special compare function.
94 * FIXME: use new RB-Tree code.
96 for (c = cmp; c; c = c->next)
97 if (c->attribute == check->attribute)
98 return (c->compare)(c->instance, req, request, check,
99 check_pairs, reply_pairs);
101 if (!request) return -1; /* doesn't exist, don't compare it */
103 switch(check->type) {
106 * Ascend binary attributes can be treated
107 * as opaque objects, I guess...
109 case PW_TYPE_ABINARY:
112 if (request->length != check->length) {
113 ret = 1; /* NOT equal */
116 ret = memcmp(request->strvalue, check->strvalue,
120 if (check->flags.caseless) {
121 ret = strcasecmp((char *)request->strvalue,
122 (char *)check->strvalue);
124 ret = strcmp((char *)request->strvalue,
125 (char *)check->strvalue);
128 case PW_TYPE_INTEGER:
130 ret = request->lvalue - check->lvalue;
133 ret = ntohl(request->lvalue) - ntohl(check->lvalue);
144 * See what attribute we want to compare with.
146 static int otherattr(int attr)
150 for (c = cmp; c; c = c->next) {
151 if (c->attribute == attr)
159 * Register a function as compare function.
160 * compare_attr is the attribute in the request we want to
161 * compare with. Normally this is the same as "attr".
162 * You can set this to:
164 * -1 the same as "attr"
165 * 0 always call compare function, not tied to request attribute
166 * >0 Attribute to compare with.
168 * For example, PW_GROUP in a check item needs to be compared
169 * with PW_USER_NAME in the incoming request.
171 int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
175 paircompare_unregister(attr, fun);
177 c = rad_malloc(sizeof(struct cmp));
179 if (compare_attr < 0)
183 c->otherattr = compare_attr;
184 c->instance = instance;
192 * Unregister a function.
194 void paircompare_unregister(int attr, RAD_COMPARE_FUNC fun)
196 struct cmp *c, *last;
199 for (c = cmp; c; c = c->next) {
200 if (c->attribute == attr && c->compare == fun)
205 if (c == NULL) return;
208 last->next = c->next;
216 * Compare two pair lists except for the password information.
217 * For every element in "check" at least one matching copy must
218 * be present in "reply".
222 int paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
224 VALUE_PAIR *check_item;
225 VALUE_PAIR *auth_item;
233 for (check_item = check; check_item != NULL; check_item = check_item->next) {
235 * If the user is setting a configuration value,
236 * then don't bother comparing it to any attributes
237 * sent to us by the user. It ALWAYS matches.
239 if ((check_item->operator == T_OP_SET) ||
240 (check_item->operator == T_OP_ADD)) {
244 switch (check_item->attribute) {
246 * Attributes we skip during comparison.
247 * These are "server" check items.
249 case PW_CRYPT_PASSWORD:
253 case PW_SESSION_TYPE:
254 case PW_STRIP_USER_NAME:
259 * IF the password attribute exists, THEN
260 * we can do comparisons against it. If not,
261 * then the request did NOT contain a
262 * User-Password attribute, so we CANNOT do
263 * comparisons against it.
265 * This hack makes CHAP-Password work..
268 if (pairfind(request, PW_PASSWORD) == NULL) {
275 * See if this item is present in the request.
277 other = otherattr(check_item->attribute);
281 for (; auth_item != NULL; auth_item = auth_item->next) {
282 if (auth_item->attribute == other || other == 0)
287 * Not found, it's not a match.
289 if (auth_item == NULL) {
291 * Didn't find it. If we were *trying*
292 * to not find it, then we succeeded.
294 if (check_item->operator == T_OP_CMP_FALSE)
301 * Else we found it, but we were trying to not
302 * find it, so we failed.
304 if (check_item->operator == T_OP_CMP_FALSE)
309 * We've got to xlat the string before doing
312 if (check_item->flags.do_xlat) {
314 char buffer[sizeof(check_item->strvalue)];
316 check_item->flags.do_xlat = 0;
317 rcode = radius_xlat(buffer, sizeof(buffer),
318 check_item->strvalue,
322 * Parse the string into a new value.
324 pairparsevalue(check_item, buffer);
328 * OK it is present now compare them.
330 compare = paircompare(req, auth_item, check_item, check, reply);
332 switch (check_item->operator) {
335 radlog(L_INFO, "Invalid operator for item %s: "
336 "reverting to '=='", check_item->name);
338 case T_OP_CMP_TRUE: /* compare always == 0 */
339 case T_OP_CMP_FALSE: /* compare always == 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;
357 if (compare > 0) result = -1;
361 if (compare < 0) result = -1;
368 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
371 * Include substring matches.
373 regcomp(®, (char *)check_item->strvalue,
375 compare = regexec(®,
376 (char *)auth_item->strvalue,
377 REQUEST_MAX_REGEX + 1,
382 * Add %{0}, %{1}, etc.
384 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
386 char buffer[sizeof(check_item->strvalue)];
389 * Didn't match: delete old
390 * match, if it existed.
392 if ((compare != 0) ||
393 (rxmatch[i].rm_so == -1)) {
394 p = request_data_get(req, req,
395 REQUEST_DATA_REGEX | i);
409 * Copy substring into buffer.
412 auth_item->strvalue + rxmatch[i].rm_so,
413 rxmatch[i].rm_eo - rxmatch[i].rm_so);
414 buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
417 * Copy substring, and add it to
420 * Note that we don't check
421 * for out of memory, which is
422 * the only error we can get...
425 request_data_add(req,
427 REQUEST_DATA_REGEX | i,
431 if (compare != 0) result = -1;
435 regcomp(®, (char *)check_item->strvalue, REG_EXTENDED|REG_NOSUB);
436 compare = regexec(®, (char *)auth_item->strvalue,
439 if (compare == 0) result = -1;
443 } /* switch over the operator of the check item */
446 * This attribute didn't match, but maybe there's
447 * another of the same attribute, which DOES match.
450 auth_item = auth_item->next;
455 } /* for every entry in the check item list */
457 return 0; /* it matched */
461 * Compare two attributes simply. Calls paircompare.
464 int simplepaircmp(REQUEST *req, VALUE_PAIR *first, VALUE_PAIR *second)
466 return paircompare( req, first, second, NULL, NULL );
471 * Move pairs, replacing/over-writing them, and doing xlat.
474 * Move attributes from one list to the other
475 * if not already present.
477 void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
479 VALUE_PAIR **tailto, *i, *j, *next;
480 VALUE_PAIR *tailfrom = NULL;
484 * Point "tailto" to the end of the "to" list.
487 for(i = *to; i; i = i->next) {
492 * Loop over the "from" list.
494 for(i = *from; i; i = next) {
498 * Don't move 'fallthrough' over.
500 if (i->attribute == PW_FALL_THROUGH) {
505 * We've got to xlat the string before moving
508 if (i->flags.do_xlat) {
510 char buffer[sizeof(i->strvalue)];
512 i->flags.do_xlat = 0;
513 rcode = radius_xlat(buffer, sizeof(buffer),
518 * Parse the string into a new value.
520 pairparsevalue(i, buffer);
523 found = pairfind(*to, i->attribute);
524 switch (i->operator) {
527 * If a similar attribute is found,
530 case T_OP_SUB: /* -= */
532 if (!i->strvalue[0] ||
533 (strcmp((char *)found->strvalue,
534 (char *)i->strvalue) == 0)){
535 pairdelete(to, found->attribute);
538 * 'tailto' may have been
542 for(j = *to; j; j = j->next) {
552 * Add it, if it's not already there.
554 case T_OP_EQ: /* = */
557 continue; /* with the loop */
562 * If a similar attribute is found,
563 * replace it with the new one. Otherwise,
564 * add the new one to the list.
566 case T_OP_SET: /* := */
571 memcpy(found, i, sizeof(*found));
578 * FIXME: Add support for <=, >=, <, >
580 * which will mean (for integers)
581 * 'make the attribute the smaller, etc'
585 * Add the new element to the list, even
586 * if similar ones already exist.
589 case T_OP_ADD: /* += */
594 tailfrom->next = next;
599 * If ALL of the 'to' attributes have been deleted,
600 * then ensure that the 'tail' is updated to point
611 } /* loop over the 'from' list */