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;
56 int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
61 * Check for =* and !* and return appropriately
63 if( check->operator == T_OP_CMP_TRUE )
65 if( check->operator == T_OP_CMP_FALSE )
69 if (check->operator == T_OP_REG_EQ) {
74 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
76 snprintf(name, sizeof(name), "%%{%s}", check->name);
77 radius_xlat(value, sizeof(value), name, request, NULL);
80 * Include substring matches.
82 regcomp(®, (char *)check->vp_strvalue,
84 compare = regexec(®, value, REQUEST_MAX_REGEX + 1,
89 * Add %{0}, %{1}, etc.
91 for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
93 char buffer[sizeof(check->vp_strvalue)];
96 * Didn't match: delete old
97 * match, if it existed.
100 (rxmatch[i].rm_so == -1)) {
101 p = request_data_get(request, request,
102 REQUEST_DATA_REGEX | i);
116 * Copy substring into buffer.
118 memcpy(buffer, value + rxmatch[i].rm_so,
119 rxmatch[i].rm_eo - rxmatch[i].rm_so);
120 buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
123 * Copy substring, and add it to
126 * Note that we don't check
127 * for out of memory, which is
128 * the only error we can get...
131 request_data_add(request, request,
132 REQUEST_DATA_REGEX | i,
135 if (compare == 0) return 0;
139 if (check->operator == T_OP_REG_NE) {
144 regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
146 snprintf(name, sizeof(name), "%%{%s}", check->name);
147 radius_xlat(value, sizeof(value), name, request, NULL);
150 * Include substring matches.
152 regcomp(®, (char *)check->vp_strvalue,
154 compare = regexec(®, value, REQUEST_MAX_REGEX + 1,
158 if (compare != 0) return 0;
165 * Not a regular expression, compare the types.
167 switch(check->type) {
170 * Ascend binary attributes can be treated
171 * as opaque objects, I guess...
173 case PW_TYPE_ABINARY:
176 if (vp->length != check->length) {
177 ret = 1; /* NOT equal */
180 ret = memcmp(vp->vp_strvalue, check->vp_strvalue,
184 if (check->flags.caseless) {
185 ret = strcasecmp((char *)vp->vp_strvalue,
186 (char *)check->vp_strvalue);
188 ret = strcmp((char *)vp->vp_strvalue,
189 (char *)check->vp_strvalue);
194 case PW_TYPE_INTEGER:
195 ret = vp->vp_integer - check->vp_integer;
198 ret = vp->vp_date - check->vp_date;
201 ret = ntohl(vp->vp_ipaddr) - ntohl(check->vp_ipaddr);
203 case PW_TYPE_IPV6ADDR:
204 ret = memcmp(&vp->vp_ipv6addr, &check->vp_ipv6addr,
205 sizeof(vp->vp_ipv6addr));
208 case PW_TYPE_IPV6PREFIX:
209 ret = memcmp(&vp->vp_ipv6prefix, &check->vp_ipv6prefix,
210 sizeof(vp->vp_ipv6prefix));
214 ret = memcmp(&vp->vp_ifid, &check->vp_ifid,
215 sizeof(vp->vp_ifid));
227 * Compare 2 attributes. May call the attribute compare function.
229 int radius_callback_compare(REQUEST *req, VALUE_PAIR *request,
230 VALUE_PAIR *check, VALUE_PAIR *check_pairs,
231 VALUE_PAIR **reply_pairs)
236 * Check for =* and !* and return appropriately
238 if( check->operator == T_OP_CMP_TRUE )
239 return 0; /* always return 0/EQUAL */
240 if( check->operator == T_OP_CMP_FALSE )
241 return 1; /* always return 1/NOT EQUAL */
244 * See if there is a special compare function.
246 * FIXME: use new RB-Tree code.
248 for (c = cmp; c; c = c->next)
249 if (c->attribute == check->attribute)
250 return (c->compare)(c->instance, req, request, check,
251 check_pairs, reply_pairs);
253 if (!request) return -1; /* doesn't exist, don't compare it */
255 return radius_compare_vps(req, check, request);
260 * See what attribute we want to compare with.
262 static int otherattr(int attr)
266 for (c = cmp; c; c = c->next) {
267 if (c->attribute == attr)
275 * Register a function as compare function.
276 * compare_attr is the attribute in the request we want to
277 * compare with. Normally this is the same as "attr".
278 * You can set this to:
280 * -1 the same as "attr"
281 * 0 always call compare function, not tied to request attribute
282 * >0 Attribute to compare with.
284 * For example, PW_GROUP in a check item needs to be compared
285 * with PW_USER_NAME in the incoming request.
287 int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
291 paircompare_unregister(attr, fun);
293 c = rad_malloc(sizeof(struct cmp));
297 c->otherattr = compare_attr;
298 c->instance = instance;
306 * Unregister a function.
308 void paircompare_unregister(int attr, RAD_COMPARE_FUNC fun)
310 struct cmp *c, *last;
313 for (c = cmp; c; c = c->next) {
314 if (c->attribute == attr && c->compare == fun)
319 if (c == NULL) return;
322 last->next = c->next;
330 * Compare two pair lists except for the password information.
331 * For every element in "check" at least one matching copy must
332 * be present in "reply".
336 int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
338 VALUE_PAIR *check_item;
339 VALUE_PAIR *auth_item;
344 for (check_item = check; check_item != NULL; check_item = check_item->next) {
346 * If the user is setting a configuration value,
347 * then don't bother comparing it to any attributes
348 * sent to us by the user. It ALWAYS matches.
350 if ((check_item->operator == T_OP_SET) ||
351 (check_item->operator == T_OP_ADD)) {
355 switch (check_item->attribute) {
357 * Attributes we skip during comparison.
358 * These are "server" check items.
360 case PW_CRYPT_PASSWORD:
364 case PW_SESSION_TYPE:
365 case PW_STRIP_USER_NAME:
370 * IF the password attribute exists, THEN
371 * we can do comparisons against it. If not,
372 * then the request did NOT contain a
373 * User-Password attribute, so we CANNOT do
374 * comparisons against it.
376 * This hack makes CHAP-Password work..
378 case PW_USER_PASSWORD:
379 if (pairfind(request, PW_USER_PASSWORD) == NULL) {
386 * See if this item is present in the request.
388 other = otherattr(check_item->attribute);
393 for (; auth_item != NULL; auth_item = auth_item->next) {
394 if (auth_item->attribute == other || other == 0)
400 * Not found, it's not a match.
402 if (auth_item == NULL) {
404 * Didn't find it. If we were *trying*
405 * to not find it, then we succeeded.
407 if (check_item->operator == T_OP_CMP_FALSE)
414 * Else we found it, but we were trying to not
415 * find it, so we failed.
417 if (check_item->operator == T_OP_CMP_FALSE)
422 * We've got to xlat the string before doing
425 if (check_item->flags.do_xlat) {
427 char buffer[sizeof(check_item->vp_strvalue)];
429 check_item->flags.do_xlat = 0;
430 rcode = radius_xlat(buffer, sizeof(buffer),
431 check_item->vp_strvalue,
435 * Parse the string into a new value.
437 pairparsevalue(check_item, buffer);
441 * OK it is present now compare them.
443 compare = radius_callback_compare(req, auth_item, check_item,
446 switch (check_item->operator) {
449 radlog(L_INFO, "Invalid operator for item %s: "
450 "reverting to '=='", check_item->name);
452 case T_OP_CMP_TRUE: /* compare always == 0 */
453 case T_OP_CMP_FALSE: /* compare always == 1 */
455 if (compare != 0) result = -1;
459 if (compare == 0) result = -1;
463 if (compare >= 0) result = -1;
467 if (compare <= 0) result = -1;
471 if (compare > 0) result = -1;
475 if (compare < 0) result = -1;
484 } /* switch over the operator of the check item */
487 * This attribute didn't match, but maybe there's
488 * another of the same attribute, which DOES match.
490 if ((result != 0) && (other >= 0)) {
491 auth_item = auth_item->next;
496 } /* for every entry in the check item list */
502 * Move pairs, replacing/over-writing them, and doing xlat.
505 * Move attributes from one list to the other
506 * if not already present.
508 void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
510 VALUE_PAIR **tailto, *i, *j, *next;
511 VALUE_PAIR *tailfrom = NULL;
515 * Point "tailto" to the end of the "to" list.
518 for(i = *to; i; i = i->next) {
523 * Loop over the "from" list.
525 for(i = *from; i; i = next) {
529 * Don't move 'fallthrough' over.
531 if (i->attribute == PW_FALL_THROUGH) {
536 * We've got to xlat the string before moving
539 if (i->flags.do_xlat) {
541 char buffer[sizeof(i->vp_strvalue)];
543 i->flags.do_xlat = 0;
544 rcode = radius_xlat(buffer, sizeof(buffer),
549 * Parse the string into a new value.
551 pairparsevalue(i, buffer);
554 found = pairfind(*to, i->attribute);
555 switch (i->operator) {
558 * If a similar attribute is found,
561 case T_OP_SUB: /* -= */
563 if (!i->vp_strvalue[0] ||
564 (strcmp((char *)found->vp_strvalue,
565 (char *)i->vp_strvalue) == 0)){
566 pairdelete(to, found->attribute);
569 * 'tailto' may have been
573 for(j = *to; j; j = j->next) {
583 * Add it, if it's not already there.
585 case T_OP_EQ: /* = */
588 continue; /* with the loop */
593 * If a similar attribute is found,
594 * replace it with the new one. Otherwise,
595 * add the new one to the list.
597 case T_OP_SET: /* := */
602 memcpy(found, i, sizeof(*found));
609 * FIXME: Add support for <=, >=, <, >
611 * which will mean (for integers)
612 * 'make the attribute the smaller, etc'
616 * Add the new element to the list, even
617 * if similar ones already exist.
620 case T_OP_ADD: /* += */
625 tailfrom->next = next;
630 * If ALL of the 'to' attributes have been deleted,
631 * then ensure that the 'tail' is updated to point
642 } /* loop over the 'from' list */