#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <ctype.h>
#include <fcntl.h>
#include <limits.h>
struct file_instance {
char *compat_mode;
char *key;
char *usersfile;
fr_hash_table_t *users;
char *acctusersfile;
fr_hash_table_t *acctusers;
char *preproxy_usersfile;
fr_hash_table_t *preproxy_users;
char *auth_usersfile;
fr_hash_table_t *auth_users;
char *postproxy_usersfile;
fr_hash_table_t *postproxy_users;
char *postauth_usersfile;
fr_hash_table_t *postauth_users;
};
static int fallthrough(VALUE_PAIR *vp)
{
VALUE_PAIR *tmp;
tmp = pairfind(vp, PW_FALL_THROUGH);
return tmp ? tmp->vp_integer : 0;
}
static const CONF_PARSER module_config[] = {
{ "usersfile", PW_TYPE_FILENAME,
offsetof(struct file_instance,usersfile), NULL, NULL },
{ "acctusersfile", PW_TYPE_FILENAME,
offsetof(struct file_instance,acctusersfile), NULL, NULL },
{ "preproxy_usersfile", PW_TYPE_FILENAME,
offsetof(struct file_instance,preproxy_usersfile), NULL, NULL },
{ "auth_usersfile", PW_TYPE_FILENAME,
offsetof(struct file_instance,auth_usersfile), NULL, NULL },
{ "postproxy_usersfile", PW_TYPE_FILENAME,
offsetof(struct file_instance,postproxy_usersfile), NULL, NULL },
{ "postauth_usersfile", PW_TYPE_FILENAME,
offsetof(struct file_instance,postauth_usersfile), NULL, NULL },
{ "compat", PW_TYPE_STRING_PTR,
offsetof(struct file_instance,compat_mode), NULL, "cistron" },
{ "key", PW_TYPE_STRING_PTR,
offsetof(struct file_instance,key), NULL, NULL },
{ NULL, -1, 0, NULL, NULL }
};
static uint32_t pairlist_hash(const void *data)
{
return fr_hash_string(((const PAIR_LIST *)data)->name);
}
static int pairlist_cmp(const void *a, const void *b)
{
return strcmp(((const PAIR_LIST *)a)->name,
((const PAIR_LIST *)b)->name);
}
static void my_pairlist_free(void *data)
{
PAIR_LIST *pl = data;
pairlist_free(&pl);
}
static int getusersfile(const char *filename, fr_hash_table_t **pht,
char *compat_mode_str)
{
int rcode;
PAIR_LIST *users = NULL;
PAIR_LIST *entry, *next;
fr_hash_table_t *ht, *tailht;
int order = 0;
if (!filename) {
*pht = NULL;
return 0;
}
rcode = pairlist_read(filename, &users, 1);
if (rcode < 0) {
return -1;
}
if ((debug_flag) ||
(strcmp(compat_mode_str, "cistron") == 0)) {
VALUE_PAIR *vp;
int compat_mode = FALSE;
if (strcmp(compat_mode_str, "cistron") == 0) {
compat_mode = TRUE;
}
entry = users;
while (entry) {
if (compat_mode) {
DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...",
filename, entry->lineno,
entry->name);
}
for (vp = entry->check; vp != NULL; vp = vp->next) {
if (vp->operator != T_OP_EQ) {
continue;
}
if (((vp->attribute & ~0xffff) != 0) ||
(vp->attribute < 0x100)) {
if (!compat_mode) {
DEBUG("[%s]:%d WARNING! Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
filename, entry->lineno,
vp->name, vp->name,
entry->name);
} else {
DEBUG("\tChanging '%s =' to '%s =='",
vp->name, vp->name);
}
vp->operator = T_OP_CMP_EQ;
continue;
}
if (compat_mode) {
if ((vp->attribute >= 0x100) &&
(vp->attribute <= 0xffff) &&
(vp->attribute != PW_HINT) &&
(vp->attribute != PW_HUNTGROUP_NAME)) {
DEBUG("\tChanging '%s =' to '%s +='",
vp->name, vp->name);
vp->operator = T_OP_ADD;
} else {
DEBUG("\tChanging '%s =' to '%s =='",
vp->name, vp->name);
vp->operator = T_OP_CMP_EQ;
}
}
}
for (vp = entry->reply; 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 reply item list for user \"%s\".\n"
"\tThis attribute MUST go on the first line"
" with the other check items",
filename, entry->lineno, vp->name,
entry->name);
}
}
entry = entry->next;
}
}
ht = fr_hash_table_create(pairlist_hash, pairlist_cmp,
my_pairlist_free);
if (!ht) {
pairlist_free(&users);
return -1;
}
tailht = fr_hash_table_create(pairlist_hash, pairlist_cmp,
NULL);
if (!tailht) {
fr_hash_table_free(ht);
pairlist_free(&users);
return -1;
}
for (entry = users; entry != NULL; entry = next) {
PAIR_LIST *tail;
next = entry->next;
entry->next = NULL;
entry->order = order++;
tail = fr_hash_table_finddata(tailht, entry);
if (!tail) {
if (!fr_hash_table_insert(ht, entry) ||
!fr_hash_table_insert(tailht, entry)) {
pairlist_free(&next);
fr_hash_table_free(ht);
fr_hash_table_free(tailht);
return -1;
}
} else {
tail->next = entry;
if (!fr_hash_table_replace(tailht, entry)) {
pairlist_free(&next);
fr_hash_table_free(ht);
fr_hash_table_free(tailht);
return -1;
}
}
}
fr_hash_table_free(tailht);
*pht = ht;
return 0;
}
static int file_detach(void *instance)
{
struct file_instance *inst = instance;
fr_hash_table_free(inst->users);
fr_hash_table_free(inst->acctusers);
fr_hash_table_free(inst->preproxy_users);
fr_hash_table_free(inst->auth_users);
fr_hash_table_free(inst->postproxy_users);
fr_hash_table_free(inst->postauth_users);
free(inst);
return 0;
}
static int file_instantiate(CONF_SECTION *conf, void **instance)
{
struct file_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) {
free(inst);
return -1;
}
rcode = getusersfile(inst->usersfile, &inst->users, inst->compat_mode);
if (rcode != 0) {
radlog(L_ERR|L_CONS, "Errors reading %s", inst->usersfile);
file_detach(inst);
return -1;
}
rcode = getusersfile(inst->acctusersfile, &inst->acctusers, inst->compat_mode);
if (rcode != 0) {
radlog(L_ERR|L_CONS, "Errors reading %s", inst->acctusersfile);
file_detach(inst);
return -1;
}
rcode = getusersfile(inst->preproxy_usersfile, &inst->preproxy_users, inst->compat_mode);
if (rcode != 0) {
radlog(L_ERR|L_CONS, "Errors reading %s", inst->preproxy_usersfile);
file_detach(inst);
return -1;
}
rcode = getusersfile(inst->auth_usersfile, &inst->auth_users, inst->compat_mode);
if (rcode != 0) {
radlog(L_ERR|L_CONS, "Errors reading %s", inst->auth_usersfile);
file_detach(inst);
return -1;
}
rcode = getusersfile(inst->postproxy_usersfile, &inst->postproxy_users, inst->compat_mode);
if (rcode != 0) {
radlog(L_ERR|L_CONS, "Errors reading %s", inst->postproxy_usersfile);
file_detach(inst);
return -1;
}
rcode = getusersfile(inst->postauth_usersfile, &inst->postauth_users, inst->compat_mode);
if (rcode != 0) {
radlog(L_ERR|L_CONS, "Errors reading %s", inst->postauth_usersfile);
file_detach(inst);
return -1;
}
*instance = inst;
return 0;
}
static int file_common(struct file_instance *inst, REQUEST *request,
const char *filename, fr_hash_table_t *ht,
VALUE_PAIR *request_pairs, VALUE_PAIR **reply_pairs)
{
const char *name, *match;
VALUE_PAIR **config_pairs;
VALUE_PAIR *check_tmp;
VALUE_PAIR *reply_tmp;
const PAIR_LIST *user_pl, *default_pl;
int found = 0;
PAIR_LIST my_pl;
char buffer[256];
if (!inst->key) {
VALUE_PAIR *namepair;
namepair = request->username;
name = namepair ? (char *) namepair->vp_strvalue : "NONE";
} else {
int len;
len = radius_xlat(buffer, sizeof(buffer), inst->key,
request, NULL);
if (len) name = buffer;
else name = "NONE";
}
config_pairs = &request->config_items;
if (!ht) return RLM_MODULE_NOOP;
my_pl.name = name;
user_pl = fr_hash_table_finddata(ht, &my_pl);
my_pl.name = "DEFAULT";
default_pl = fr_hash_table_finddata(ht, &my_pl);
while (user_pl || default_pl) {
const PAIR_LIST *pl;
if (!default_pl && user_pl) {
pl = user_pl;
match = name;
user_pl = user_pl->next;
} else if (!user_pl && default_pl) {
pl = default_pl;
match = "DEFAULT";
default_pl = default_pl->next;
} else if (user_pl->order < default_pl->order) {
pl = user_pl;
match = name;
user_pl = user_pl->next;
} else {
pl = default_pl;
match = "DEFAULT";
default_pl = default_pl->next;
}
if (paircompare(request, request_pairs, pl->check, reply_pairs) == 0) {
RDEBUG2("%s: Matched entry %s at line %d",
filename, match, pl->lineno);
found = 1;
check_tmp = paircopy(pl->check);
reply_tmp = paircopy(pl->reply);
pairxlatmove(request, reply_pairs, &reply_tmp);
pairmove(config_pairs, &check_tmp);
pairfree(&reply_tmp);
pairfree(&check_tmp);
if (!fallthrough(pl->reply))
break;
}
}
pairdelete(reply_pairs, PW_FALL_THROUGH);
if (!found)
return RLM_MODULE_NOOP;
return RLM_MODULE_OK;
}
static int file_authorize(void *instance, REQUEST *request)
{
struct file_instance *inst = instance;
return file_common(inst, request, "users", inst->users,
request->packet->vps, &request->reply->vps);
}
static int file_preacct(void *instance, REQUEST *request)
{
struct file_instance *inst = instance;
return file_common(inst, request, "acct_users", inst->acctusers,
request->packet->vps, &request->reply->vps);
}
static int file_preproxy(void *instance, REQUEST *request)
{
struct file_instance *inst = instance;
return file_common(inst, request, "preproxy_users",
inst->preproxy_users,
request->packet->vps, &request->proxy->vps);
}
static int file_postproxy(void *instance, REQUEST *request)
{
struct file_instance *inst = instance;
return file_common(inst, request, "postproxy_users",
inst->postproxy_users,
request->proxy_reply->vps, &request->reply->vps);
}
static int file_authenticate(void *instance, REQUEST *request)
{
struct file_instance *inst = instance;
return file_common(inst, request, "auth_users",
inst->auth_users,
request->packet->vps, &request->reply->vps);
}
static int file_postauth(void *instance, REQUEST *request)
{
struct file_instance *inst = instance;
return file_common(inst, request, "postauth_users",
inst->postauth_users,
request->packet->vps, &request->reply->vps);
}
module_t rlm_files = {
RLM_MODULE_INIT,
"files",
RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,
file_instantiate,
file_detach,
{
file_authenticate,
file_authorize,
file_preacct,
NULL,
NULL,
file_preproxy,
file_postproxy,
file_postauth
},
};