*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
- * Copyright 2000 The FreeRADIUS server project
+ * Copyright 2000,2006 The FreeRADIUS server project
* Copyright 2000 Alan DeKok <aland@ox.org>
*/
-static const char rcsid[] = "$Id$";
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
-#include "autoconf.h"
-#include "libradius.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#if HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
+#include <freeradius-devel/radiusd.h>
#ifdef HAVE_REGEX_H
# include <regex.h>
#ifndef REG_EXTENDED
#define REG_EXTENDED (0)
#endif
-#endif
-#include "radiusd.h"
+#ifndef REG_NOSUB
+#define REG_NOSUB (0)
+#endif
+#endif
struct cmp {
int attribute;
/*
* Compare 2 attributes. May call the attribute compare function.
*/
-static int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
- VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
+static int compare_pair(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
+ VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
{
int ret = -2;
struct cmp *c;
/*
- * Sanity check.
- */
-#if 0
- if (request->attribute != check->attribute)
- return -2;
-#endif
-
- /*
* Check for =* and !* and return appropriately
*/
if( check->operator == T_OP_CMP_TRUE )
/*
* See if there is a special compare function.
+ *
+ * FIXME: use new RB-Tree code.
*/
for (c = cmp; c; c = c->next)
if (c->attribute == check->attribute)
return (c->compare)(c->instance, req, request, check,
check_pairs, reply_pairs);
+ if (!request) return -1; /* doesn't exist, don't compare it */
+
switch(check->type) {
#ifdef ASCEND_BINARY
/*
ret = 1; /* NOT equal */
break;
}
- ret = memcmp(request->strvalue, check->strvalue,
+ ret = memcmp(request->vp_strvalue, check->vp_strvalue,
request->length);
break;
case PW_TYPE_STRING:
- ret = strcmp((char *)request->strvalue,
- (char *)check->strvalue);
+ if (check->flags.caseless) {
+ ret = strcasecmp((char *)request->vp_strvalue,
+ (char *)check->vp_strvalue);
+ } else {
+ ret = strcmp((char *)request->vp_strvalue,
+ (char *)check->vp_strvalue);
+ }
break;
case PW_TYPE_INTEGER:
case PW_TYPE_DATE:
ret = request->lvalue - check->lvalue;
break;
case PW_TYPE_IPADDR:
- ret = ntohl(request->lvalue) - ntohl(check->lvalue);
+ ret = ntohl(request->vp_ipaddr) - ntohl(check->vp_ipaddr);
+ break;
+ case PW_TYPE_IPV6ADDR:
+ ret = memcmp(&request->vp_ipv6addr, &check->vp_ipv6addr,
+ sizeof(request->vp_ipv6addr));
break;
+
+ case PW_TYPE_IPV6PREFIX:
+ ret = memcmp(&request->vp_ipv6prefix, &check->vp_ipv6prefix,
+ sizeof(request->vp_ipv6prefix));
+ break;
+
+ case PW_TYPE_IFID:
+ ret = memcmp(&request->vp_ifid, &check->vp_ifid,
+ sizeof(request->vp_ifid));
+ break;
+
default:
break;
}
c = rad_malloc(sizeof(struct cmp));
- if (compare_attr < 0)
+ if (compare_attr < 0)
compare_attr = attr;
c->compare = fun;
c->attribute = attr;
*
* Return 0 on match.
*/
-int paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
+int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
{
VALUE_PAIR *check_item;
VALUE_PAIR *auth_item;
* These are "server" check items.
*/
case PW_CRYPT_PASSWORD:
+ case PW_AUTH_TYPE:
+ case PW_AUTZ_TYPE:
+ case PW_ACCT_TYPE:
+ case PW_SESSION_TYPE:
+ case PW_STRIP_USER_NAME:
continue;
break;
*
* This hack makes CHAP-Password work..
*/
- case PW_PASSWORD:
- if (pairfind(request, PW_PASSWORD) == NULL) {
+ case PW_USER_PASSWORD:
+ if (pairfind(request, PW_USER_PASSWORD) == NULL) {
continue;
}
break;
* See if this item is present in the request.
*/
other = otherattr(check_item->attribute);
+
auth_item = request;
try_again:
for (; auth_item != NULL; auth_item = auth_item->next) {
* Not found, it's not a match.
*/
if (auth_item == NULL) {
- return -1;
+ /*
+ * Didn't find it. If we were *trying*
+ * to not find it, then we succeeded.
+ */
+ if (check_item->operator == T_OP_CMP_FALSE)
+ return 0;
+ else
+ return -1;
}
-#if 0
- if ((check_item->strvalue[0] == '%') &&
- (check_item->strvalue[1] == '{')) {
+ /*
+ * Else we found it, but we were trying to not
+ * find it, so we failed.
+ */
+ if (check_item->operator == T_OP_CMP_FALSE)
+ return -1;
+
+
+ /*
+ * We've got to xlat the string before doing
+ * the comparison.
+ */
+ if (check_item->flags.do_xlat) {
int rcode;
- char buffer[sizeof(check_item->strvalue)];
+ char buffer[sizeof(check_item->vp_strvalue)];
+ check_item->flags.do_xlat = 0;
rcode = radius_xlat(buffer, sizeof(buffer),
- check_item->strvalue,
+ check_item->vp_strvalue,
req, NULL);
- strNcpy(check_item->strvalue, buffer,
- sizeof(check_item->strvalue));
/*
- * Parse it into an ipaddr, integer, or date.
+ * Parse the string into a new value.
*/
+ pairparsevalue(check_item, buffer);
}
-#endif
/*
* OK it is present now compare them.
*/
- compare = paircompare(req, auth_item, check_item, check, reply);
+ compare = compare_pair(req, auth_item, check_item, check, reply);
switch (check_item->operator) {
case T_OP_EQ:
default:
- radlog(L_ERR, "Invalid operator for item %s: "
+ radlog(L_INFO, "Invalid operator for item %s: "
"reverting to '=='", check_item->name);
/*FALLTHRU*/
case T_OP_CMP_TRUE: /* compare always == 0 */
case T_OP_GT:
if (compare <= 0) result = -1;
break;
-
+
case T_OP_LE:
if (compare > 0) result = -1;
break;
#ifdef HAVE_REGEX_H
case T_OP_REG_EQ:
- regcomp(®, (char *)check_item->strvalue, REG_EXTENDED);
- compare = regexec(®, (char *)auth_item->strvalue,
- 0, NULL, 0);
+ {
+ int i;
+ regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
+
+ if ((auth_item->type == PW_TYPE_IPADDR) &&
+ (auth_item->vp_strvalue[0] == '\0')) {
+ inet_ntop(AF_INET, &(auth_item->lvalue),
+ auth_item->vp_strvalue,
+ sizeof(auth_item->vp_strvalue));
+ }
+
+ /*
+ * Include substring matches.
+ */
+ regcomp(®, (char *)check_item->vp_strvalue,
+ REG_EXTENDED);
+ compare = regexec(®,
+ (char *)auth_item->vp_strvalue,
+ REQUEST_MAX_REGEX + 1,
+ rxmatch, 0);
regfree(®);
+
+ /*
+ * Add %{0}, %{1}, etc.
+ */
+ for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
+ char *p;
+ char buffer[sizeof(check_item->vp_strvalue)];
+
+ /*
+ * Didn't match: delete old
+ * match, if it existed.
+ */
+ if ((compare != 0) ||
+ (rxmatch[i].rm_so == -1)) {
+ p = request_data_get(req, req,
+ REQUEST_DATA_REGEX | i);
+ if (p) {
+ free(p);
+ continue;
+ }
+
+ /*
+ * No previous match
+ * to delete, stop.
+ */
+ break;
+ }
+
+ /*
+ * Copy substring into buffer.
+ */
+ memcpy(buffer,
+ auth_item->vp_strvalue + rxmatch[i].rm_so,
+ rxmatch[i].rm_eo - rxmatch[i].rm_so);
+ buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
+
+ /*
+ * Copy substring, and add it to
+ * the request.
+ *
+ * Note that we don't check
+ * for out of memory, which is
+ * the only error we can get...
+ */
+ p = strdup(buffer);
+ request_data_add(req,
+ req,
+ REQUEST_DATA_REGEX | i,
+ p, free);
+ }
+ }
if (compare != 0) result = -1;
break;
case T_OP_REG_NE:
- regcomp(®, (char *)check_item->strvalue, REG_EXTENDED);
- compare = regexec(®, (char *)auth_item->strvalue,
+ if ((auth_item->type == PW_TYPE_IPADDR) &&
+ (auth_item->vp_strvalue[0] == '\0')) {
+ inet_ntop(AF_INET, &(auth_item->lvalue),
+ auth_item->vp_strvalue,
+ sizeof(auth_item->vp_strvalue));
+ }
+
+ regcomp(®, (char *)check_item->vp_strvalue, REG_EXTENDED|REG_NOSUB);
+ compare = regexec(®, (char *)auth_item->vp_strvalue,
0, NULL, 0);
regfree(®);
if (compare == 0) result = -1;
}
/*
- * Compare two attributes simply. Calls paircompare.
+ * Compare two attributes simply. Calls compare_pair.
*/
int simplepaircmp(REQUEST *req, VALUE_PAIR *first, VALUE_PAIR *second)
{
- return paircompare( req, first, second, NULL, NULL );
+ return compare_pair( req, first, second, NULL, NULL );
}
/*
- * Compare a Connect-Info and a Connect-Rate
+ * Move pairs, replacing/over-writing them, and doing xlat.
*/
-static int connectcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
- VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
-{
- int rate;
-
- instance = instance;
- check_pairs = check_pairs; /* shut the compiler up */
- reply_pairs = reply_pairs;
-
- rate = atoi((char *)request->strvalue);
- return rate - check->lvalue;
-}
-
-
/*
- * Compare a portno with a range.
+ * Move attributes from one list to the other
+ * if not already present.
*/
-static int portcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
- VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
+void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
{
- char buf[MAX_STRING_LEN];
- char *s, *p;
- uint32_t lo, hi;
- uint32_t port = request->lvalue;
-
- instance = instance;
- check_pairs = check_pairs; /* shut the compiler up */
- reply_pairs = reply_pairs;
-
- if ((strchr((char *)check->strvalue, ',') == NULL) &&
- (strchr((char *)check->strvalue, '-') == NULL)) {
- return (request->lvalue - check->lvalue);
+ VALUE_PAIR **tailto, *i, *j, *next;
+ VALUE_PAIR *tailfrom = NULL;
+ VALUE_PAIR *found;
+
+ /*
+ * Point "tailto" to the end of the "to" list.
+ */
+ tailto = to;
+ for(i = *to; i; i = i->next) {
+ tailto = &i->next;
}
- /* Same size */
- strcpy(buf, (char *)check->strvalue);
- s = strtok(buf, ",");
+ /*
+ * Loop over the "from" list.
+ */
+ for(i = *from; i; i = next) {
+ next = i->next;
- while (s != NULL) {
- if ((p = strchr(s, '-')) != NULL)
- p++;
- else
- p = s;
- lo = strtoul(s, NULL, 10);
- hi = strtoul(p, NULL, 10);
- if (lo <= port && port <= hi) {
- return 0;
+ /*
+ * Don't move 'fallthrough' over.
+ */
+ if (i->attribute == PW_FALL_THROUGH) {
+ continue;
}
- s = strtok(NULL, ",");
- }
-
- return -1;
-}
-
-/*
- * Compare prefix/suffix.
- *
- * If they compare:
- * - if PW_STRIP_USER_NAME is present in check_pairs,
- * strip the username of prefix/suffix.
- * - if PW_STRIP_USER_NAME is not present in check_pairs,
- * add a PW_STRIPPED_USER_NAME to the request.
- */
-static int presufcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
- VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
-{
- VALUE_PAIR *vp;
- char *name = (char *)request->strvalue;
- char rest[MAX_STRING_LEN];
- int len, namelen;
- int ret = -1;
-
- instance = instance;
- reply_pairs = reply_pairs; /* shut the compiler up */
-
-#if 0 /* DEBUG */
- printf("Comparing %s and %s, check->attr is %d\n",
- name, check->strvalue, check->attribute);
-#endif
-
- len = strlen((char *)check->strvalue);
- switch (check->attribute) {
- case PW_PREFIX:
- ret = strncmp(name, (char *)check->strvalue, len);
- if (ret == 0 && rest)
- strcpy(rest, name + len);
- break;
- case PW_SUFFIX:
- namelen = strlen(name);
- if (namelen < len)
- break;
- ret = strcmp(name + namelen - len,
- (char *)check->strvalue);
- if (ret == 0 && rest) {
- strncpy(rest, name, namelen - len);
- rest[namelen - len] = 0;
- }
- break;
- }
- if (ret != 0)
- return ret;
- if (pairfind(check_pairs, PW_STRIP_USER_NAME)) {
/*
- * I don't think we want to update the User-Name
- * attribute in place... - atd
+ * We've got to xlat the string before moving
+ * it over.
*/
- strcpy((char *)request->strvalue, rest);
- request->length = strlen(rest);
- } else {
- if ((vp = pairfind(check_pairs, PW_STRIPPED_USER_NAME)) != NULL){
- strcpy((char *)vp->strvalue, rest);
- vp->length = strlen(rest);
- } else if ((vp = paircreate(PW_STRIPPED_USER_NAME,
- PW_TYPE_STRING)) != NULL) {
- strcpy((char *)vp->strvalue, rest);
- vp->length = strlen(rest);
- pairadd(&request, vp);
- } /* else no memory! Die, die!: FIXME!! */
- }
-
- return ret;
-}
-
-
-/*
- * Compare the current time to a range.
- * Hmm... it would save work, and probably be better,
- * if we were passed the REQUEST data structure, so we
- * could use it's 'timestamp' element. That way, we could
- * do the comparison against when the packet came in, not now,
- * and have one less system call to do.
- */
-static int timecmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
- VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
-{
- instance = instance;
- request = request; /* shut the compiler up */
- check_pairs = check_pairs;
- reply_pairs = reply_pairs;
-
- if (timestr_match((char *)check->strvalue, time(NULL)) >= 0) {
- return 0;
- }
- return -1;
-}
-
-/*
- * Matches if there is NO SUCH ATTRIBUTE as the one named
- * in check->strvalue. If there IS such an attribute, it
- * doesn't match.
- *
- * This is ugly, and definitely non-optimal. We should be
- * doing the lookup only ONCE, and storing the result
- * in check->lvalue...
- */
-static int attrcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
- VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
-{
- VALUE_PAIR *pair;
- DICT_ATTR *dict;
- int attr;
+ if (i->flags.do_xlat) {
+ int rcode;
+ char buffer[sizeof(i->vp_strvalue)];
- instance = instance;
- check_pairs = check_pairs; /* shut the compiler up */
- reply_pairs = reply_pairs;
+ i->flags.do_xlat = 0;
+ rcode = radius_xlat(buffer, sizeof(buffer),
+ i->vp_strvalue,
+ req, NULL);
- if (check->lvalue == 0) {
- dict = dict_attrbyname((char *)check->strvalue);
- if (dict == NULL) {
- return -1;
+ /*
+ * Parse the string into a new value.
+ */
+ pairparsevalue(i, buffer);
}
- attr = dict->attr;
- } else {
- attr = check->lvalue;
- }
- /*
- * If there's no such attribute, then return MATCH,
- * else FAILURE.
- */
- pair = pairfind(request, attr);
- if (pair == NULL) {
- return 0;
- }
+ found = pairfind(*to, i->attribute);
+ switch (i->operator) {
- return -1;
-}
+ /*
+ * If a similar attribute is found,
+ * delete it.
+ */
+ case T_OP_SUB: /* -= */
+ if (found) {
+ if (!i->vp_strvalue[0] ||
+ (strcmp((char *)found->vp_strvalue,
+ (char *)i->vp_strvalue) == 0)){
+ pairdelete(to, found->attribute);
+
+ /*
+ * 'tailto' may have been
+ * deleted...
+ */
+ tailto = to;
+ for(j = *to; j; j = j->next) {
+ tailto = &j->next;
+ }
+ }
+ }
+ tailfrom = i;
+ continue;
+ break;
-/*
- * Compare the expiration date.
- */
-static int expirecmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
- VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
-{
- time_t now;
+ /*
+ * Add it, if it's not already there.
+ */
+ case T_OP_EQ: /* = */
+ if (found) {
+ tailfrom = i;
+ continue; /* with the loop */
+ }
+ break;
- instance = instance;
- request = request; /* shut the compiler up */
- check_pairs = check_pairs;
- reply_pairs = reply_pairs;
+ /*
+ * If a similar attribute is found,
+ * replace it with the new one. Otherwise,
+ * add the new one to the list.
+ */
+ case T_OP_SET: /* := */
+ if (found) {
+ VALUE_PAIR *vp;
- /*
- * FIXME! This should be request->timestamp!
- */
- now = time(NULL);
+ vp = found->next;
+ memcpy(found, i, sizeof(*found));
+ found->next = vp;
+ continue;
+ }
+ break;
- if (now <= check->lvalue) {
- return 0;
- }
+ /*
+ * FIXME: Add support for <=, >=, <, >
+ *
+ * which will mean (for integers)
+ * 'make the attribute the smaller, etc'
+ */
- return +1;
-}
+ /*
+ * Add the new element to the list, even
+ * if similar ones already exist.
+ */
+ default:
+ case T_OP_ADD: /* += */
+ break;
+ }
-/*
- * Register server-builtin special attributes.
- */
-void pair_builtincompare_init(void)
-{
- paircompare_register(PW_NAS_PORT_ID, -1, portcmp, NULL);
- paircompare_register(PW_PREFIX, PW_USER_NAME, presufcmp, NULL);
- paircompare_register(PW_SUFFIX, PW_USER_NAME, presufcmp, NULL);
- paircompare_register(PW_CONNECT_RATE, PW_CONNECT_INFO, connectcmp, NULL);
- paircompare_register(PW_CURRENT_TIME, 0, timecmp, NULL);
- paircompare_register(PW_NO_SUCH_ATTRIBUTE, 0, attrcmp, NULL);
- paircompare_register(PW_EXPIRATION, 0, expirecmp, NULL);
+ if (tailfrom)
+ tailfrom->next = next;
+ else
+ *from = next;
+
+ /*
+ * If ALL of the 'to' attributes have been deleted,
+ * then ensure that the 'tail' is updated to point
+ * to the head.
+ */
+ if (!*to) {
+ tailto = to;
+ }
+ *tailto = i;
+ if (i) {
+ i->next = NULL;
+ tailto = &i->next;
+ }
+ } /* loop over the 'from' list */
}