#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <freeradius-devel/rad_assert.h>
#include <sys/stat.h>
#include <ctype.h>
#include <fcntl.h>
#include <limits.h>
struct attr_filter_instance {
char *attrsfile;
char *key;
int relaxed;
PAIR_LIST *attrs;
};
static const CONF_PARSER module_config[] = {
{ "attrsfile", PW_TYPE_FILENAME,
offsetof(struct attr_filter_instance,attrsfile), NULL, "${raddbdir}/attrs" },
{ "key", PW_TYPE_STRING_PTR,
offsetof(struct attr_filter_instance,key), NULL, "%{Realm}" },
{ "relaxed", PW_TYPE_BOOLEAN,
offsetof(struct attr_filter_instance,relaxed), NULL, "no" },
{ NULL, -1, 0, NULL, NULL }
};
static void check_pair(VALUE_PAIR *check_item, VALUE_PAIR *reply_item,
int *pass, int *fail)
{
int compare;
if (check_item->operator == T_OP_SET) return;
compare = paircmp(check_item, reply_item);
if (compare == 1) {
++*(pass);
} else {
++*(fail);
}
return;
}
static int getattrsfile(const char *filename, PAIR_LIST **pair_list)
{
int rcode;
PAIR_LIST *attrs = NULL;
PAIR_LIST *entry;
VALUE_PAIR *vp;
rcode = pairlist_read(filename, &attrs, 1);
if (rcode < 0) {
return -1;
}
entry = attrs;
while (entry) {
entry->check = entry->reply;
entry->reply = NULL;
for (vp = entry->check; vp != NULL; vp = vp->next) {
if (!(vp->attribute & ~0xffff) &&
(vp->attribute > 0xff) &&
(vp->attribute > 1000)) {
log_debug("[%s]:%d WARNING! Check item \"%s\"\n"
"\tfound in filter list for realm \"%s\".\n",
filename, entry->lineno, vp->name,
entry->name);
}
}
entry = entry->next;
}
*pair_list = attrs;
return 0;
}
static int attr_filter_detach(void *instance)
{
struct attr_filter_instance *inst = instance;
pairlist_free(&inst->attrs);
free(inst);
return 0;
}
static int attr_filter_instantiate(CONF_SECTION *conf, void **instance)
{
struct attr_filter_instance *inst;
int rcode;
inst = rad_malloc(sizeof *inst);
if (!inst) {
return -1;
}
memset(inst, 0, sizeof(*inst));
if (cf_section_parse(conf, inst, module_config) < 0) {
attr_filter_detach(inst);
return -1;
}
rcode = getattrsfile(inst->attrsfile, &inst->attrs);
if (rcode != 0) {
radlog(L_ERR|L_CONS, "Errors reading %s", inst->attrsfile);
attr_filter_detach(inst);
return -1;
}
*instance = inst;
return 0;
}
static int attr_filter_common(void *instance, REQUEST *request,
RADIUS_PACKET *packet)
{
struct attr_filter_instance *inst = instance;
VALUE_PAIR *vp;
VALUE_PAIR *output;
VALUE_PAIR **output_tail;
VALUE_PAIR *check_item;
PAIR_LIST *pl;
int found = 0;
int pass, fail = 0;
char *keyname = NULL;
VALUE_PAIR **input;
char buffer[256];
if (!packet) return RLM_MODULE_NOOP;
input = &(packet->vps);
if (!inst->key) {
VALUE_PAIR *namepair;
namepair = pairfind(request->packet->vps, PW_REALM);
if (!namepair) {
return (RLM_MODULE_NOOP);
}
keyname = namepair->vp_strvalue;
} else {
int len;
len = radius_xlat(buffer, sizeof(buffer), inst->key,
request, NULL);
if (!len) {
return RLM_MODULE_NOOP;
}
keyname = buffer;
}
output = NULL;
output_tail = &output;
for (pl = inst->attrs; pl; pl = pl->next) {
int fall_through = 0;
int relax_filter = inst->relaxed;
if ((strcmp(pl->name, "DEFAULT") != 0) &&
(strcmp(keyname, pl->name) != 0)) {
continue;
}
DEBUG2("attr_filter: Matched entry %s at line %d", pl->name,
pl->lineno);
found = 1;
for (check_item = pl->check;
check_item != NULL;
check_item = check_item->next) {
if ((check_item->attribute == PW_FALL_THROUGH) &&
(check_item->vp_integer == 1)) {
fall_through = 1;
continue;
}
else if (check_item->attribute == PW_RELAX_FILTER) {
relax_filter = check_item->vp_integer;
continue;
}
if (check_item->operator == T_OP_SET ) {
vp = paircopyvp(check_item);
if (!vp) {
pairfree(&output);
return RLM_MODULE_FAIL;
}
pairxlatmove(request, output_tail, &vp);
output_tail = &((*output_tail)->next);
}
}
for (vp = *input; vp != NULL; vp = vp->next ) {
pass = fail = 0;
for (check_item = pl->check;
check_item != NULL;
check_item = check_item->next) {
if ((check_item->attribute == PW_VENDOR_SPECIFIC) &&
(VENDOR(vp->attribute) != 0) &&
(check_item->operator == T_OP_CMP_TRUE)) {
pass++;
continue;
}
if (vp->attribute == check_item->attribute) {
check_pair(check_item, vp,
&pass, &fail);
}
}
if (fail == 0 && (pass > 0 || relax_filter)) {
if (!pass) {
DEBUG3("attr_filter: Attribute (%s) allowed by relaxed mode", vp->name);
}
*output_tail = paircopyvp(vp);
if (!*output_tail) {
pairfree(&output);
return RLM_MODULE_FAIL;
}
output_tail = &((*output_tail)->next);
}
}
if (!fall_through)
break;
}
if (!found) {
rad_assert(output == NULL);
return RLM_MODULE_NOOP;
}
pairfree(input);
*input = output;
if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
request->username = pairfind(request->packet->vps,
PW_STRIPPED_USER_NAME);
if (!request->username)
request->username = pairfind(request->packet->vps,
PW_USER_NAME);
request->password = pairfind(request->packet->vps,
PW_USER_PASSWORD);
}
return RLM_MODULE_UPDATED;
}
static int attr_filter_preacct(void *instance, REQUEST *request)
{
return attr_filter_common(instance, request, request->packet);
}
static int attr_filter_accounting(void *instance, REQUEST *request)
{
return attr_filter_common(instance, request, request->reply);
}
static int attr_filter_preproxy(void *instance, REQUEST *request)
{
return attr_filter_common(instance, request, request->proxy);
}
static int attr_filter_postproxy(void *instance, REQUEST *request)
{
return attr_filter_common(instance, request, request->proxy_reply);
}
static int attr_filter_postauth(void *instance, REQUEST *request)
{
return attr_filter_common(instance, request, request->reply);
}
static int attr_filter_authorize(void *instance, REQUEST *request)
{
return attr_filter_common(instance, request, request->packet);
}
module_t rlm_attr_filter = {
RLM_MODULE_INIT,
"attr_filter",
RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,
attr_filter_instantiate,
attr_filter_detach,
{
NULL,
attr_filter_authorize,
attr_filter_preacct,
attr_filter_accounting,
NULL,
attr_filter_preproxy,
attr_filter_postproxy,
attr_filter_postauth
},
};