#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/rad_assert.h>
#ifdef HAVE_REGEX_H
# include <regex.h>
#ifndef REG_EXTENDED
#define REG_EXTENDED (0)
#endif
#ifndef REG_NOSUB
#define REG_NOSUB (0)
#endif
#endif
struct cmp {
int attribute;
int otherattr;
void *instance;
RAD_COMPARE_FUNC compare;
struct cmp *next;
};
static struct cmp *cmp;
int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
{
int ret = -2;
if( check->operator == T_OP_CMP_TRUE )
return 0;
if( check->operator == T_OP_CMP_FALSE )
return 1;
#ifdef HAVE_REGEX_H
if (check->operator == T_OP_REG_EQ) {
int i, compare;
regex_t reg;
char name[1024];
char value[1024];
regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
snprintf(name, sizeof(name), "%%{%s}", check->name);
radius_xlat(value, sizeof(value), name, request, NULL);
compare = regcomp(®, check->vp_strvalue, REG_EXTENDED);
if (compare != 0) {
char buffer[256];
regerror(compare, ®, buffer, sizeof(buffer));
RDEBUG("Invalid regular expression %s: %s",
check->vp_strvalue, buffer);
return -1;
}
compare = regexec(®, value, REQUEST_MAX_REGEX + 1,
rxmatch, 0);
regfree(®);
for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
char *p;
char buffer[sizeof(check->vp_strvalue)];
if ((compare != 0) ||
(rxmatch[i].rm_so == -1)) {
p = request_data_get(request, request,
REQUEST_DATA_REGEX | i);
if (p) {
free(p);
continue;
}
break;
}
memcpy(buffer, value + rxmatch[i].rm_so,
rxmatch[i].rm_eo - rxmatch[i].rm_so);
buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';
p = strdup(buffer);
request_data_add(request, request,
REQUEST_DATA_REGEX | i,
p, free);
}
if (compare == 0) return 0;
return -1;
}
if (check->operator == T_OP_REG_NE) {
int compare;
regex_t reg;
char name[1024];
char value[1024];
regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
snprintf(name, sizeof(name), "%%{%s}", check->name);
radius_xlat(value, sizeof(value), name, request, NULL);
compare = regcomp(®, (char *)check->vp_strvalue,
REG_EXTENDED);
if (compare != 0) {
char buffer[256];
regerror(compare, ®, buffer, sizeof(buffer));
RDEBUG("Invalid regular expression %s: %s",
check->vp_strvalue, buffer);
return -1;
}
compare = regexec(®, value, REQUEST_MAX_REGEX + 1,
rxmatch, 0);
regfree(®);
if (compare != 0) return 0;
return -1;
}
#endif
if (check->flags.has_tag) {
ret = ((int) vp->flags.tag) - ((int) check->flags.tag);
if (ret != 0) return ret;
}
switch(check->type) {
#ifdef ASCEND_BINARY
case PW_TYPE_ABINARY:
#endif
case PW_TYPE_OCTETS:
if (vp->length != check->length) {
ret = 1;
break;
}
ret = memcmp(vp->vp_strvalue, check->vp_strvalue,
vp->length);
break;
case PW_TYPE_STRING:
ret = strcmp((char *)vp->vp_strvalue,
(char *)check->vp_strvalue);
break;
case PW_TYPE_BYTE:
case PW_TYPE_SHORT:
case PW_TYPE_INTEGER:
ret = vp->vp_integer - check->vp_integer;
break;
case PW_TYPE_DATE:
ret = vp->vp_date - check->vp_date;
break;
case PW_TYPE_IPADDR:
ret = ntohl(vp->vp_ipaddr) - ntohl(check->vp_ipaddr);
break;
case PW_TYPE_IPV6ADDR:
ret = memcmp(&vp->vp_ipv6addr, &check->vp_ipv6addr,
sizeof(vp->vp_ipv6addr));
break;
case PW_TYPE_IPV6PREFIX:
ret = memcmp(&vp->vp_ipv6prefix, &check->vp_ipv6prefix,
sizeof(vp->vp_ipv6prefix));
break;
case PW_TYPE_IFID:
ret = memcmp(&vp->vp_ifid, &check->vp_ifid,
sizeof(vp->vp_ifid));
break;
default:
break;
}
return ret;
}
int radius_callback_compare(REQUEST *req, VALUE_PAIR *request,
VALUE_PAIR *check, VALUE_PAIR *check_pairs,
VALUE_PAIR **reply_pairs)
{
struct cmp *c;
if( check->operator == T_OP_CMP_TRUE )
return 0;
if( check->operator == T_OP_CMP_FALSE )
return 1;
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;
return radius_compare_vps(req, check, request);
}
int radius_find_compare(int attribute)
{
struct cmp *c;
for (c = cmp; c; c = c->next) {
if (c->attribute == attribute) {
return TRUE;
}
}
return FALSE;
}
static int otherattr(int attr)
{
struct cmp *c;
for (c = cmp; c; c = c->next) {
if (c->attribute == attr)
return c->otherattr;
}
return attr;
}
int paircompare_register(int attr, int compare_attr, RAD_COMPARE_FUNC fun, void *instance)
{
struct cmp *c;
paircompare_unregister(attr, fun);
c = rad_malloc(sizeof(struct cmp));
c->compare = fun;
c->attribute = attr;
c->otherattr = compare_attr;
c->instance = instance;
c->next = cmp;
cmp = c;
return 0;
}
void paircompare_unregister(int attr, RAD_COMPARE_FUNC fun)
{
struct cmp *c, *last;
last = NULL;
for (c = cmp; c; c = c->next) {
if (c->attribute == attr && c->compare == fun)
break;
last = c;
}
if (c == NULL) return;
if (last != NULL)
last->next = c->next;
else
cmp = c->next;
free(c);
}
int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply)
{
VALUE_PAIR *check_item;
VALUE_PAIR *auth_item;
int result = 0;
int compare;
int other;
for (check_item = check; check_item != NULL; check_item = check_item->next) {
if ((check_item->operator == T_OP_SET) ||
(check_item->operator == T_OP_ADD)) {
continue;
}
switch (check_item->attribute) {
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;
case PW_USER_PASSWORD:
if (check_item->operator == T_OP_CMP_EQ) {
DEBUG("WARNING: Found User-Password == \"...\".");
DEBUG("WARNING: Are you sure you don't mean Cleartext-Password?");
DEBUG("WARNING: See \"man rlm_pap\" for more information.");
}
if (pairfind(request, PW_USER_PASSWORD) == NULL) {
continue;
}
break;
}
other = otherattr(check_item->attribute);
auth_item = request;
try_again:
if (other >= 0) {
for (; auth_item != NULL; auth_item = auth_item->next) {
if (auth_item->attribute == other || other == 0)
break;
}
}
if (auth_item == NULL) {
if (check_item->operator == T_OP_CMP_FALSE)
continue;
else
return -1;
}
if (check_item->operator == T_OP_CMP_FALSE)
return -1;
if (check_item->flags.do_xlat) {
int rcode;
char buffer[sizeof(check_item->vp_strvalue)];
check_item->flags.do_xlat = 0;
rcode = radius_xlat(buffer, sizeof(buffer),
check_item->vp_strvalue,
req, NULL);
pairparsevalue(check_item, buffer);
}
compare = radius_callback_compare(req, auth_item, check_item,
check, reply);
switch (check_item->operator) {
case T_OP_EQ:
default:
radlog(L_INFO, "Invalid operator for item %s: "
"reverting to '=='", check_item->name);
case T_OP_CMP_TRUE:
case T_OP_CMP_FALSE:
case T_OP_CMP_EQ:
if (compare != 0) result = -1;
break;
case T_OP_NE:
if (compare == 0) result = -1;
break;
case T_OP_LT:
if (compare >= 0) result = -1;
break;
case T_OP_GT:
if (compare <= 0) result = -1;
break;
case T_OP_LE:
if (compare > 0) result = -1;
break;
case T_OP_GE:
if (compare < 0) result = -1;
break;
#ifdef HAVE_REGEX_H
case T_OP_REG_EQ:
case T_OP_REG_NE:
if (compare != 0) result = -1;
break;
#endif
}
if ((result != 0) && (other >= 0)) {
auth_item = auth_item->next;
result = 0;
goto try_again;
}
}
return result;
}
void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from)
{
VALUE_PAIR **tailto, *i, *j, *next;
VALUE_PAIR *tailfrom = NULL;
VALUE_PAIR *found;
tailto = to;
for(i = *to; i; i = i->next) {
tailto = &i->next;
}
for(i = *from; i; i = next) {
next = i->next;
if (i->attribute == PW_FALL_THROUGH) {
tailfrom = i;
continue;
}
if (i->flags.do_xlat) {
int rcode;
char buffer[sizeof(i->vp_strvalue)];
i->flags.do_xlat = 0;
rcode = radius_xlat(buffer, sizeof(buffer),
i->vp_strvalue,
req, NULL);
pairparsevalue(i, buffer);
}
found = pairfind(*to, i->attribute);
switch (i->operator) {
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 = to;
for(j = *to; j; j = j->next) {
tailto = &j->next;
}
}
}
tailfrom = i;
continue;
break;
case T_OP_EQ:
if (found) {
tailfrom = i;
continue;
}
break;
case T_OP_SET:
if (found) {
VALUE_PAIR *vp;
vp = found->next;
memcpy(found, i, sizeof(*found));
found->next = vp;
tailfrom = i;
continue;
}
break;
default:
case T_OP_ADD:
break;
}
if (tailfrom)
tailfrom->next = next;
else
*from = next;
if (!*to) {
tailto = to;
}
*tailto = i;
if (i) {
i->next = NULL;
tailto = &i->next;
}
}
}
VALUE_PAIR *radius_paircreate(REQUEST *request, VALUE_PAIR **vps,
int attribute, int type)
{
VALUE_PAIR *vp;
request = request;
vp = paircreate(attribute, type);
if (!vp) {
radlog(L_ERR, "No memory!");
rad_assert("No memory" == NULL);
_exit(1);
}
if (vps) pairadd(vps, vp);
return vp;
}
VALUE_PAIR *radius_pairmake(REQUEST *request, VALUE_PAIR **vps,
const char *attribute, const char *value,
int operator)
{
VALUE_PAIR *vp;
request = request;
vp = pairmake(attribute, value, operator);
if (!vp) return NULL;
if (vps) pairadd(vps, vp);
return vp;
}
void debug_pair(VALUE_PAIR *vp)
{
if (!vp || !debug_flag || !fr_log_fp) return;
vp_print(fr_log_fp, vp);
}
void debug_pair_list(VALUE_PAIR *vp)
{
if (!vp || !debug_flag || !fr_log_fp) return;
while (vp) {
vp_print(fr_log_fp, vp);
vp = vp->next;
}
fflush(fr_log_fp);
}