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"
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)
56 void *instance; /* module instance */
57 RAD_COMPARE_FUNC compare;
60 static struct cmp *cmp;
64 * Compare 2 attributes. May call the attribute compare function.
66 static int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
67 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
76 if (request->attribute != check->attribute)
81 * Check for =* and !* and return appropriately
83 if( check->operator == T_OP_CMP_TRUE )
84 return 0; /* always return 0/EQUAL */
85 if( check->operator == T_OP_CMP_FALSE )
86 return 1; /* always return 1/NOT EQUAL */
89 * See if there is a special compare function.
91 for (c = cmp; c; c = c->next)
92 if (c->attribute == check->attribute)
93 return (c->compare)(c->instance, req, request, check,
94 check_pairs, reply_pairs);
99 * Ascend binary attributes can be treated
100 * as opaque objects, I guess...
102 case PW_TYPE_ABINARY:
105 if (request->length != check->length) {
106 ret = 1; /* NOT equal */
109 ret = memcmp(request->strvalue, check->strvalue,
113 ret = strcmp((char *)request->strvalue,
114 (char *)check->strvalue);
116 case PW_TYPE_INTEGER:
118 ret = request->lvalue - check->lvalue;
121 ret = ntohl(request->lvalue) - ntohl(check->lvalue);
132 * See what attribute we want to compare with.
134 static int otherattr(int attr)
138 for (c = cmp; c; c = c->next) {
139 if (c->attribute == attr)
147 * Register a function as compare function.
148 * compare_attr is the attribute in the request we want to
149 * compare with. Normally this is the same as "attr".
150 * You can set this to:
152 * -1 the same as "attr"
153 * 0 always call compare function, not tied to request attribute
154 * >0 Attribute to compare with.
156 * For example, PW_GROUP in a check item needs to be compared
157 * with PW_USER_NAME in the incoming request.
159 int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
163 paircompare_unregister(attr, fun);
165 c = rad_malloc(sizeof(struct cmp));
167 if (compare_attr < 0)
171 c->otherattr = compare_attr;
172 c->instance = instance;
180 * Unregister a function.
182 void paircompare_unregister(int attr, RAD_COMPARE_FUNC fun)
184 struct cmp *c, *last;
187 for (c = cmp; c; c = c->next) {
188 if (c->attribute == attr && c->compare == fun)
193 if (c == NULL) return;
196 last->next = c->next;
204 * Compare two pair lists except for the password information.
205 * For every element in "check" at least one matching copy must
206 * be present in "reply".
210 int paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
212 VALUE_PAIR *check_item;
213 VALUE_PAIR *auth_item;
221 for (check_item = check; check_item != NULL; check_item = check_item->next) {
223 * If the user is setting a configuration value,
224 * then don't bother comparing it to any attributes
225 * sent to us by the user. It ALWAYS matches.
227 if ((check_item->operator == T_OP_SET) ||
228 (check_item->operator == T_OP_ADD)) {
232 switch (check_item->attribute) {
234 * Attributes we skip during comparison.
235 * These are "server" check items.
237 case PW_CRYPT_PASSWORD:
242 * IF the password attribute exists, THEN
243 * we can do comparisons against it. If not,
244 * then the request did NOT contain a
245 * User-Password attribute, so we CANNOT do
246 * comparisons against it.
248 * This hack makes CHAP-Password work..
251 if (pairfind(request, PW_PASSWORD) == NULL) {
258 * See if this item is present in the request.
260 other = otherattr(check_item->attribute);
263 for (; auth_item != NULL; auth_item = auth_item->next) {
264 if (auth_item->attribute == other || other == 0)
269 * Not found, it's not a match.
271 if (auth_item == NULL) {
276 * We've got to xlat the string before doing
279 if (check_item->flags.do_xlat) {
281 char buffer[sizeof(check_item->strvalue)];
283 check_item->flags.do_xlat = 0;
284 rcode = radius_xlat(buffer, sizeof(buffer),
285 check_item->strvalue,
289 * Parse the string into a new value.
291 pairparsevalue(check_item, buffer);
295 * OK it is present now compare them.
297 compare = paircompare(req, auth_item, check_item, check, reply);
299 switch (check_item->operator) {
302 radlog(L_ERR, "Invalid operator for item %s: "
303 "reverting to '=='", check_item->name);
305 case T_OP_CMP_TRUE: /* compare always == 0 */
306 case T_OP_CMP_FALSE: /* compare always == 1 */
308 if (compare != 0) result = -1;
312 if (compare == 0) result = -1;
316 if (compare >= 0) result = -1;
320 if (compare <= 0) result = -1;
324 if (compare > 0) result = -1;
328 if (compare < 0) result = -1;
333 regcomp(®, (char *)check_item->strvalue, REG_EXTENDED);
334 compare = regexec(®, (char *)auth_item->strvalue,
337 if (compare != 0) result = -1;
341 regcomp(®, (char *)check_item->strvalue, REG_EXTENDED);
342 compare = regexec(®, (char *)auth_item->strvalue,
345 if (compare == 0) result = -1;
349 } /* switch over the operator of the check item */
352 * This attribute didn't match, but maybe there's
353 * another of the same attribute, which DOES match.
356 auth_item = auth_item->next;
361 } /* for every entry in the check item list */
363 return 0; /* it matched */
367 * Compare two attributes simply. Calls paircompare.
370 int simplepaircmp(REQUEST *req, VALUE_PAIR *first, VALUE_PAIR *second)
372 return paircompare( req, first, second, NULL, NULL );
377 * Compare a Connect-Info and a Connect-Rate
379 static int connectcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
380 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
385 check_pairs = check_pairs; /* shut the compiler up */
386 reply_pairs = reply_pairs;
388 rate = atoi((char *)request->strvalue);
389 return rate - check->lvalue;
394 * Compare a portno with a range.
396 static int portcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
397 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
399 char buf[MAX_STRING_LEN];
402 uint32_t port = request->lvalue;
405 check_pairs = check_pairs; /* shut the compiler up */
406 reply_pairs = reply_pairs;
408 if ((strchr((char *)check->strvalue, ',') == NULL) &&
409 (strchr((char *)check->strvalue, '-') == NULL)) {
410 return (request->lvalue - check->lvalue);
414 strcpy(buf, (char *)check->strvalue);
415 s = strtok(buf, ",");
418 if ((p = strchr(s, '-')) != NULL)
422 lo = strtoul(s, NULL, 10);
423 hi = strtoul(p, NULL, 10);
424 if (lo <= port && port <= hi) {
427 s = strtok(NULL, ",");
434 * Compare prefix/suffix.
437 * - if PW_STRIP_USER_NAME is present in check_pairs,
438 * strip the username of prefix/suffix.
439 * - if PW_STRIP_USER_NAME is not present in check_pairs,
440 * add a PW_STRIPPED_USER_NAME to the request.
442 static int presufcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
443 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
446 char *name = (char *)request->strvalue;
447 char rest[MAX_STRING_LEN];
452 reply_pairs = reply_pairs; /* shut the compiler up */
455 printf("Comparing %s and %s, check->attr is %d\n",
456 name, check->strvalue, check->attribute);
459 len = strlen((char *)check->strvalue);
460 switch (check->attribute) {
462 ret = strncmp(name, (char *)check->strvalue, len);
463 if (ret == 0 && rest)
464 strcpy(rest, name + len);
467 namelen = strlen(name);
470 ret = strcmp(name + namelen - len,
471 (char *)check->strvalue);
472 if (ret == 0 && rest) {
473 strncpy(rest, name, namelen - len);
474 rest[namelen - len] = 0;
481 if (pairfind(check_pairs, PW_STRIP_USER_NAME)) {
483 * I don't think we want to update the User-Name
484 * attribute in place... - atd
486 strcpy((char *)request->strvalue, rest);
487 request->length = strlen(rest);
489 if ((vp = pairfind(check_pairs, PW_STRIPPED_USER_NAME)) != NULL){
490 strcpy((char *)vp->strvalue, rest);
491 vp->length = strlen(rest);
492 } else if ((vp = paircreate(PW_STRIPPED_USER_NAME,
493 PW_TYPE_STRING)) != NULL) {
494 strcpy((char *)vp->strvalue, rest);
495 vp->length = strlen(rest);
496 pairadd(&request, vp);
497 } /* else no memory! Die, die!: FIXME!! */
505 * Compare the current time to a range.
506 * Hmm... it would save work, and probably be better,
507 * if we were passed the REQUEST data structure, so we
508 * could use it's 'timestamp' element. That way, we could
509 * do the comparison against when the packet came in, not now,
510 * and have one less system call to do.
512 static int timecmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
513 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
516 request = request; /* shut the compiler up */
517 check_pairs = check_pairs;
518 reply_pairs = reply_pairs;
520 if (timestr_match((char *)check->strvalue, time(NULL)) >= 0) {
527 * Matches if there is NO SUCH ATTRIBUTE as the one named
528 * in check->strvalue. If there IS such an attribute, it
531 * This is ugly, and definitely non-optimal. We should be
532 * doing the lookup only ONCE, and storing the result
533 * in check->lvalue...
535 static int attrcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
536 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
543 check_pairs = check_pairs; /* shut the compiler up */
544 reply_pairs = reply_pairs;
546 if (check->lvalue == 0) {
547 dict = dict_attrbyname((char *)check->strvalue);
553 attr = check->lvalue;
557 * If there's no such attribute, then return MATCH,
560 pair = pairfind(request, attr);
569 * Compare the expiration date.
571 static int expirecmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
572 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
577 request = request; /* shut the compiler up */
578 check_pairs = check_pairs;
579 reply_pairs = reply_pairs;
582 * FIXME! This should be request->timestamp!
586 if (now <= check->lvalue) {
594 * Register server-builtin special attributes.
596 void pair_builtincompare_init(void)
598 paircompare_register(PW_NAS_PORT_ID, -1, portcmp, NULL);
599 paircompare_register(PW_PREFIX, PW_USER_NAME, presufcmp, NULL);
600 paircompare_register(PW_SUFFIX, PW_USER_NAME, presufcmp, NULL);
601 paircompare_register(PW_CONNECT_RATE, PW_CONNECT_INFO, connectcmp, NULL);
602 paircompare_register(PW_CURRENT_TIME, 0, timecmp, NULL);
603 paircompare_register(PW_NO_SUCH_ATTRIBUTE, 0, attrcmp, NULL);
604 paircompare_register(PW_EXPIRATION, 0, expirecmp, NULL);