/*
* rlm_files.c authorization: Find a user in the "users" file.
- * accounting: Write the "detail" files.
*
* Version: $Id$
*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2002,2006 The FreeRADIUS server project
+ * Copyright 2000 Jeff Carneal <jeff@apex.net>
*/
-static const char rcsid[] = "$Id$";
-
-#include "autoconf.h"
-
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <netinet/in.h>
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
-#include "radiusd.h"
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
-#include <stdlib.h>
-#include <string.h>
-#include <netdb.h>
#include <ctype.h>
#include <fcntl.h>
-#include <limits.h>
-
-#if HAVE_MALLOC_H
-# include <malloc.h>
-#endif
-
-#include "modules.h"
-
-#ifdef WITH_DBM
-# include <dbm.h>
-#endif
-#ifdef WITH_NDBM
-# include <ndbm.h>
-#endif
+#include <limits.h>
struct file_instance {
char *compat_mode;
- /* autz */
- char *usersfile;
- PAIR_LIST *users;
+ char *key;
- /* preacct */
- char *acctusersfile;
- PAIR_LIST *acctusers;
-};
+ /* autz */
+ char *usersfile;
+ fr_hash_table_t *users;
-#if defined(WITH_DBM) || defined(WITH_NDBM)
-/*
- * See if a potential DBM file is present.
- */
-static int checkdbm(char *users, char *ext)
-{
- char buffer[256];
- struct stat st;
- strcpy(buffer, users);
- strcat(buffer, ext);
+ /* authenticate */
+ char *auth_usersfile;
+ fr_hash_table_t *auth_users;
- return stat(buffer, &st);
-}
+ /* preacct */
+ char *acctusersfile;
+ fr_hash_table_t *acctusers;
-/*
- * Find the named user in the DBM user database.
- * Returns: -1 not found
- * 0 found but doesn't match.
- * 1 found and matches.
- */
-static int dbm_find(DBM *dbmfile, char *name, VALUE_PAIR *request_pairs,
- VALUE_PAIR **check_pairs, VALUE_PAIR **reply_pairs)
-{
- datum named;
- datum contentd;
- char *ptr;
- VALUE_PAIR *check_tmp;
- VALUE_PAIR *reply_tmp;
- int ret = 0;
+#ifdef WITH_PROXY
+ /* pre-proxy */
+ char *preproxy_usersfile;
+ fr_hash_table_t *preproxy_users;
- named.dptr = name;
- named.dsize = strlen(name);
-#ifdef WITH_DBM
- contentd = fetch(named);
+ /* post-proxy */
+ char *postproxy_usersfile;
+ fr_hash_table_t *postproxy_users;
#endif
-#ifdef WITH_NDBM
- contentd = dbm_fetch(dbmfile, named);
-#endif
- if(contentd.dptr == NULL)
- return -1;
-
- check_tmp = NULL;
- reply_tmp = NULL;
-
- /*
- * Parse the check values
- */
- ptr = contentd.dptr;
- contentd.dptr[contentd.dsize] = '\0';
- if (*ptr != '\n' && userparse(ptr, &check_tmp) != 0) {
- radlog(L_ERR|L_CONS, "Parse error (check) for user %s", name);
- pairfree(check_tmp);
- return -1;
- }
- while(*ptr != '\n' && *ptr != '\0') {
- ptr++;
- }
- if(*ptr != '\n') {
- radlog(L_ERR|L_CONS, "Parse error (no reply pairs) for user %s",
- name);
- pairfree(check_tmp);
- return -1;
- }
- ptr++;
-
- /*
- * Parse the reply values
- */
- if (userparse(ptr, &reply_tmp) != 0) {
- radlog(L_ERR|L_CONS, "Parse error (reply) for user %s", name);
- pairfree(check_tmp);
- pairfree(reply_tmp);
- return -1;
- }
-
- /*
- * See if the check_pairs match.
- */
- if (paircmp(request_pairs, check_tmp, reply_pairs) == 0) {
- ret = 1;
- pairmove(reply_pairs, &reply_tmp);
- pairmove2(reply_pairs, &reply_tmp, PW_FALL_THROUGH);
- pairmove(check_pairs, &check_tmp);
- }
- pairfree(reply_tmp);
- pairfree(check_tmp);
+ /* post-authenticate */
+ char *postauth_usersfile;
+ fr_hash_table_t *postauth_users;
+};
- return ret;
-}
-#endif /* DBM */
/*
* See if a VALUE_PAIR list contains Fall-Through = Yes
static int fallthrough(VALUE_PAIR *vp)
{
VALUE_PAIR *tmp;
+ tmp = pairfind(vp, PW_FALL_THROUGH, 0);
- tmp = pairfind(vp, PW_FALL_THROUGH);
-
- return tmp ? tmp->lvalue : 0;
+ 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 },
+#ifdef WITH_PROXY
+ { "preproxy_usersfile", PW_TYPE_FILENAME,
+ offsetof(struct file_instance,preproxy_usersfile), NULL, NULL },
+ { "postproxy_usersfile", PW_TYPE_FILENAME,
+ offsetof(struct file_instance,postproxy_usersfile), NULL, NULL },
+#endif
+ { "auth_usersfile", PW_TYPE_FILENAME,
+ offsetof(struct file_instance,auth_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 int file_init(void)
+static uint32_t pairlist_hash(const void *data)
{
- return 0;
+ return fr_hash_string(((const PAIR_LIST *)data)->name);
}
-/*
- * A temporary holding area for config values to be extracted
- * into, before they are copied into the instance data
- */
-static struct file_instance config;
+static int pairlist_cmp(const void *a, const void *b)
+{
+ return strcmp(((const PAIR_LIST *)a)->name,
+ ((const PAIR_LIST *)b)->name);
+}
-static CONF_PARSER module_config[] = {
- { "usersfile", PW_TYPE_STRING_PTR, &config.usersfile, RADIUS_USERS },
- { "acctusersfile", PW_TYPE_STRING_PTR, &config.acctusersfile, RADIUS_ACCT_USERS },
- { "compat", PW_TYPE_STRING_PTR, &config.compat_mode, "cistron" },
- { NULL, -1, NULL, NULL }
-};
+static void my_pairlist_free(void *data)
+{
+ PAIR_LIST *pl = data;
-static int getusersfile(const char *filename, PAIR_LIST **pair_list)
+ pairlist_free(&pl);
+}
+
+
+static int getusersfile(const char *filename, fr_hash_table_t **pht,
+ char *compat_mode_str)
{
int rcode;
- PAIR_LIST *users = NULL;
-#if defined(WITH_DBM) || defined(WITH_NDBM)
- if (!use_dbm &&
- (checkdbm(filename, ".dir") == 0 ||
- checkdbm(filename, ".db") == 0)) {
- radlog(L_INFO|L_CONS, "DBM files found but no -b flag " "given - NOT using DBM");
- }
-#endif
+ PAIR_LIST *users = NULL;
+ PAIR_LIST *entry, *next;
+ fr_hash_table_t *ht, *tailht;
+ int order = 0;
+
+ if (!filename) {
+ *pht = NULL;
+ return 0;
+ }
- if (!use_dbm) {
- rcode = pairlist_read(filename, &users, 1);
- if (rcode < 0) {
- return -1;
- }
+ rcode = pairlist_read(filename, &users, 1);
+ if (rcode < 0) {
+ return -1;
}
- /*
- * Walk through the 'users' file list, if we're debugging,
+ /*
+ * Walk through the 'users' file list, if we're debugging,
* or if we're in compat_mode.
- */
- if ((debug_flag) ||
- (strcmp(config.compat_mode, "cistron") == 0)) {
- PAIR_LIST *entry;
- VALUE_PAIR *vp;
+ */
+ if ((debug_flag) ||
+ (strcmp(compat_mode_str, "cistron") == 0)) {
+ VALUE_PAIR *vp;
int compat_mode = FALSE;
- if (strcmp(config.compat_mode, "cistron") == 0) {
+ if (strcmp(compat_mode_str, "cistron") == 0) {
compat_mode = TRUE;
}
-
- entry = users;
- while (entry) {
+
+ entry = users;
+ while (entry) {
if (compat_mode) {
DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...",
- filename, entry->lineno,
- entry->name);
+ filename, entry->lineno,
+ entry->name);
}
- /*
- * Look for improper use of '=' in the
- * check items. They should be using
- * '==' for on-the-wire RADIUS attributes,
- * and probably ':=' for server
- * configuration items.
- */
- for (vp = entry->check; vp != NULL; vp = vp->next) {
- /*
- * Ignore attributes which are set
- * properly.
- */
- if (vp->operator != T_OP_EQ) {
- continue;
- }
-
- /*
- * If it's a vendor attribute,
- * or it's a wire protocol,
- * ensure it has '=='.
- */
- if (((vp->attribute & ~0xffff) != 0) ||
- (vp->attribute < 0x100)) {
+ /*
+ * Look for improper use of '=' in the
+ * check items. They should be using
+ * '==' for on-the-wire RADIUS attributes,
+ * and probably ':=' for server
+ * configuration items.
+ */
+ for (vp = entry->check; vp != NULL; vp = vp->next) {
+ /*
+ * Ignore attributes which are set
+ * properly.
+ */
+ if (vp->operator != T_OP_EQ) {
+ continue;
+ }
+
+ /*
+ * If it's a vendor attribute,
+ * or it's a wire protocol,
+ * ensure it has '=='.
+ */
+ if ((vp->vendor != 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);
+ filename, entry->lineno,
+ vp->name, vp->name,
+ entry->name);
} else {
DEBUG("\tChanging '%s =' to '%s =='",
- vp->name, vp->name);
+ vp->name, vp->name);
}
vp->operator = T_OP_CMP_EQ;
continue;
- }
-
+ }
+
/*
* Cistron Compatibility mode.
*
* become ==
*/
if ((vp->attribute >= 0x100) &&
- (vp->attribute <= 0xffff) &&
- (vp->attribute != PW_HINT) &&
- (vp->attribute != PW_HUNTGROUP_NAME)) {
+ (vp->attribute <= 0xffff) &&
+ (vp->attribute != PW_HINT) &&
+ (vp->attribute != PW_HUNTGROUP_NAME)) {
DEBUG("\tChanging '%s =' to '%s +='",
- vp->name, vp->name);
+ vp->name, vp->name);
vp->operator = T_OP_ADD;
} else {
DEBUG("\tChanging '%s =' to '%s =='",
- vp->name, vp->name);
+ vp->name, vp->name);
vp->operator = T_OP_CMP_EQ;
}
}
-
- } /* end of loop over check items */
-
-
- /*
- * Look for server configuration items
- * in the reply list.
- *
- * It's a common enough mistake, that it's
- * worth doing.
- */
- for (vp = entry->reply; vp != NULL; vp = vp->next) {
- /*
- * If it's NOT a vendor attribute,
- * and it's NOT a wire protocol
- * and we ignore Fall-Through,
- * then bitch about it, giving a
- * good warning message.
- */
- 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;
- }
-
- }
-
- *pair_list = users;
- return 0;
-}
-
-/*
- * (Re-)read the "users" file into memory.
- */
-static int file_instantiate(CONF_SECTION *conf, void **instance)
-{
- struct file_instance *inst;
- int rcode;
- inst = malloc(sizeof *inst);
- if (!inst) {
- radlog(L_ERR|L_CONS, "Out of memory\n");
- return -1;
- }
-
- if (cf_section_parse(conf, module_config) < 0) {
- free(inst);
- return -1;
- }
-
- inst->usersfile = config.usersfile;
- inst->acctusersfile = config.acctusersfile;
- config.usersfile = NULL;
- config.acctusersfile = NULL;
-
- rcode = getusersfile(inst->usersfile, &inst->users);
- if (rcode != 0) {
- radlog(L_ERR|L_CONS, "Errors reading %s", inst->usersfile);
- free(inst->usersfile);
- free(inst->acctusersfile);
- free(inst);
- return -1;
- }
-
- rcode = getusersfile(inst->acctusersfile, &inst->acctusers);
- if (rcode != 0) {
- radlog(L_ERR|L_CONS, "Errors reading %s", inst->acctusersfile);
- pairlist_free(&inst->users);
- free(inst->usersfile);
- free(inst->acctusersfile);
- free(inst);
- return -1;
- }
-
- *instance = inst;
- return 0;
-}
+ } /* end of loop over check items */
-/*
- * Find the named user in the database. Create the
- * set of attribute-value pairs to check and reply with
- * for this user from the database. The main code only
- * needs to check the password, the rest is done here.
- */
-static int file_authorize(void *instance, REQUEST *request)
-{
- VALUE_PAIR *namepair;
- VALUE_PAIR *request_pairs;
- VALUE_PAIR *check_tmp;
- VALUE_PAIR *reply_tmp;
- PAIR_LIST *pl;
- int found = 0;
-#if defined(WITH_DBM) || defined(WITH_NDBM)
- int i, r;
- char buffer[256];
-#endif
- const char *name;
- struct file_instance *inst = instance;
-#ifdef WITH_USERCOLLIDE
- VALUE_PAIR *auth_type_pair;
- VALUE_PAIR *password_pair;
- VALUE_PAIR *auth_item;
- int auth_type = -1;
- int result = 1;
-#endif
- VALUE_PAIR **check_pairs, **reply_pairs;
+ /*
+ * Look for server configuration items
+ * in the reply list.
+ *
+ * It's a common enough mistake, that it's
+ * worth doing.
+ */
+ for (vp = entry->reply; vp != NULL; vp = vp->next) {
+ /*
+ * If it's NOT a vendor attribute,
+ * and it's NOT a wire protocol
+ * and we ignore Fall-Through,
+ * then bitch about it, giving a
+ * good warning message.
+ */
+ if ((vp->vendor == 0) &&
+ (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;
+ }
- request_pairs = request->packet->vps;
- check_pairs = &request->config_items;
- reply_pairs = &request->reply->vps;
+ }
- /*
- * Grab the canonical user name.
- */
- namepair = request->username;
- name = namepair ? (char *) namepair->strvalue : "NONE";
+ ht = fr_hash_table_create(pairlist_hash, pairlist_cmp,
+ my_pairlist_free);
+ if (!ht) {
+ pairlist_free(&users);
+ return -1;
+ }
- /*
- * Find the entry for the user.
- */
-#if defined(WITH_DBM) || defined(WITH_NDBM)
- /*
- * FIXME: move to rlm_dbm.c
- */
- if (use_dbm) {
- /*
- * FIXME: No Prefix / Suffix support for DBM.
- */
-#ifdef WITH_DBM
- if (dbminit(inst->usersfile) != 0)
-#endif
-#ifdef WITH_NDBM
- if ((dbmfile = dbm_open(inst->usersfile, O_RDONLY, 0)) == NULL)
-#endif
- {
- radlog(L_ERR|L_CONS, "cannot open dbm file %s",
- buffer);
- return RLM_MODULE_FAIL;
- }
+ tailht = fr_hash_table_create(pairlist_hash, pairlist_cmp,
+ NULL);
+ if (!tailht) {
+ fr_hash_table_free(ht);
+ pairlist_free(&users);
+ return -1;
+ }
- r = dbm_find(dbmfile, name, request_pairs, check_pairs,
- reply_pairs);
- if (r > 0) found = 1;
- if (r <= 0 || fallthrough(*reply_pairs)) {
-
- pairdelete(reply_pairs, PW_FALL_THROUGH);
-
- sprintf(buffer, "DEFAULT");
- i = 0;
- while ((r = dbm_find(dbmfile, buffer, request_pairs,
- check_pairs, reply_pairs)) >= 0 || i < 2) {
- if (r > 0) {
- found = 1;
- if (!fallthrough(*reply_pairs))
- break;
- pairdelete(reply_pairs,PW_FALL_THROUGH);
- }
- sprintf(buffer, "DEFAULT%d", i++);
- }
- }
-#ifdef WITH_DBM
- dbmclose();
-#endif
-#ifdef WITH_NDBM
- dbm_close(dbmfile);
-#endif
- } else
/*
- * Note the fallthrough through the #endif.
+ * Now that we've read it in, put the entries into a hash
+ * for faster access.
*/
-#endif
+ for (entry = users; entry != NULL; entry = next) {
+ PAIR_LIST *tail;
- for(pl = inst->users; pl; pl = pl->next) {
-#ifdef WITH_USERCOLLIDE
- result = 1;
-#endif
- /*
- * If the current entry is NOT a default,
- * AND the name does NOT match the current entry,
- * then skip to the next entry.
- */
- if ((strcmp(pl->name, "DEFAULT") != 0) &&
- (strcmp(name, pl->name) != 0)) {
- continue;
- }
+ next = entry->next;
+ entry->next = NULL;
+ entry->order = order++;
/*
- * If the current request matches against the
- * check pairs, then add the reply pairs from the
- * entry to the current list of reply pairs.
+ * Insert it into the hash table, and remember
+ * the tail of the linked list.
*/
- if ((paircmp(request_pairs, pl->check, reply_pairs) == 0)) {
-#ifdef WITH_USERCOLLIDE
- /*
- * We don't compare pass on default users
- * or they never match. Oops.
+ tail = fr_hash_table_finddata(tailht, entry);
+ if (!tail) {
+ /*
+ * Insert it into the head & tail.
*/
- if(strcmp(pl->name, "DEFAULT")) {
- /*
- * We check the pass as a config
- * item with user collisions Most
- * of this is stolen out of
- * rad_check_password()
- */
- if ((auth_type_pair = pairfind(pl->check, PW_AUTHTYPE)) != NULL) {
- auth_type = auth_type_pair->lvalue;
- DEBUG2(" file_auth (Usercollide): auth_type %d", auth_type);
- }
-
- /* Find pass in the REQ */
- auth_item = request->password;
- if (auth_item == NULL) {
- DEBUG2(" file_auth (Usercollide): No password in the request");
- return RLM_MODULE_OK;
- }
-
- /* Find the password from the users file. */
- if ((password_pair = pairfind(pl->check, PW_CRYPT_PASSWORD)) != NULL)
- auth_type = PW_AUTHTYPE_CRYPT;
- else
- password_pair = pairfind(pl->check, PW_PASSWORD);
-
- switch(auth_type) {
- case PW_AUTHTYPE_CRYPT:
- DEBUG2(" file_auth (Usercollide): Checking Crypt");
- if (password_pair == NULL) {
- result = auth_item->strvalue ? 0 : 1;
- break;
- }
- if (strcmp(password_pair->strvalue,
- crypt(auth_item->strvalue,
- password_pair->strvalue)) != 0)
- result = 0;
- break;
- case PW_AUTHTYPE_LOCAL:
- DEBUG2(" file_auth (Usercollide): Checking Local");
- if (auth_item->attribute != PW_CHAP_PASSWORD) {
- if (password_pair == NULL ||
- strcmp(password_pair->strvalue,
- auth_item->strvalue)!=0)
- result = 0;
- break;
- }
- case PW_AUTHTYPE_ACCEPT:
- break;
- default:
- continue;
- } /* switch(auth_type) */
- } /* if(!default) */
-
- if(result) {
-#endif
- DEBUG2(" users: Matched %s at %d", pl->name, pl->lineno);
- found = 1;
- check_tmp = paircopy(pl->check);
- reply_tmp = paircopy(pl->reply);
- pairmove(reply_pairs, &reply_tmp);
- pairmove(check_pairs, &check_tmp);
- pairfree(reply_tmp);
- pairfree(check_tmp); /* should be NULL */
- /*
- * Fallthrough?
- */
- if (!fallthrough(pl->reply))
- break;
-#ifdef WITH_USERCOLLIDE
+ 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;
}
-#endif
}
}
-
- /*
- * See if we succeeded. If we didn't find the user,
- * then exit from the module.
- */
- if (!found)
- return RLM_MODULE_NOTFOUND;
- /*
- * Remove server internal parameters.
- */
- pairdelete(reply_pairs, PW_FALL_THROUGH);
+ fr_hash_table_free(tailht);
+ *pht = ht;
- return RLM_MODULE_OK;
+ return 0;
}
/*
- * Authentication - unused.
+ * Clean up.
*/
-static int file_authenticate(void *instance, REQUEST *request)
+static int file_detach(void *instance)
{
- instance = instance;
- request = request;
- return RLM_MODULE_OK;
+ struct file_instance *inst = instance;
+ fr_hash_table_free(inst->users);
+ fr_hash_table_free(inst->acctusers);
+#ifdef WITH_PROXY
+ fr_hash_table_free(inst->preproxy_users);
+ fr_hash_table_free(inst->postproxy_users);
+#endif
+ fr_hash_table_free(inst->auth_users);
+ fr_hash_table_free(inst->postauth_users);
+ free(inst);
+ return 0;
}
+
+
/*
- * Pre-Accounting - read the acct_users file for check_items and
- * config_items. Reply items are Not Recommended(TM) in acct_users,
- * except for Fallthrough, which should work
- *
- * This function is mostly a copy of file_authorize
+ * (Re-)read the "users" file into memory.
*/
-static int file_preacct(void *instance, REQUEST *request)
+static int file_instantiate(CONF_SECTION *conf, void **instance)
{
- VALUE_PAIR *namepair;
- const char *name;
- VALUE_PAIR *request_pairs;
+ 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;
+ }
+
+#ifdef WITH_PROXY
+ /*
+ * Get the pre-proxy stuff
+ */
+ 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->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;
+ }
+#endif
+
+ 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->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;
+}
+
+/*
+ * Common code called by everything below.
+ */
+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 *reply_pairs = NULL;
VALUE_PAIR *check_tmp;
VALUE_PAIR *reply_tmp;
- PAIR_LIST *pl;
+ const PAIR_LIST *user_pl, *default_pl;
int found = 0;
-#if defined(WITH_DBM) || defined(WITH_NDBM)
- int i, r;
+ PAIR_LIST my_pl;
char buffer[256];
-#endif
- struct file_instance *inst = instance;
- namepair = request->username;
- name = namepair ? (char *) namepair->strvalue : "NONE";
- request_pairs = request->packet->vps;
+ 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);
+
/*
* Find the entry for the user.
*/
-#if defined(WITH_DBM) || defined(WITH_NDBM)
- /*
- * FIXME: move to rlm_dbm.c
- */
- if (use_dbm) {
- /*
- * FIXME: No Prefix / Suffix support for DBM.
- */
-#ifdef WITH_DBM
- if (dbminit(inst->acctusersfile) != 0)
-#endif
-#ifdef WITH_NDBM
- if ((dbmfile = dbm_open(inst->acctusersfile, O_RDONLY, 0)) == NULL)
-#endif
- {
- radlog(L_ERR|L_CONS, "cannot open dbm file %s",
- buffer);
- return RLM_MODULE_FAIL;
- }
-
- r = dbm_find(dbmfile, name, request_pairs, config_pairs,
- &reply_pairs);
- if (r > 0) found = 1;
- if (r <= 0 || fallthrough(*reply_pairs)) {
-
- pairdelete(reply_pairs, PW_FALL_THROUGH);
-
- sprintf(buffer, "DEFAULT");
- i = 0;
- while ((r = dbm_find(dbmfile, buffer, request_pairs,
- config_pairs, &reply_pairs)) >= 0 || i < 2) {
- if (r > 0) {
- found = 1;
- if (!fallthrough(*reply_pairs))
- break;
- pairdelete(reply_pairs,PW_FALL_THROUGH);
- }
- sprintf(buffer, "DEFAULT%d", i++);
- }
+ 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;
}
-#ifdef WITH_DBM
- dbmclose();
-#endif
-#ifdef WITH_NDBM
- dbm_close(dbmfile);
-#endif
- } else
- /*
- * Note the fallthrough through the #endif.
- */
-#endif
- for(pl = inst->acctusers; pl; pl = pl->next) {
-
- if (strcmp(name, pl->name) && strcmp(pl->name, "DEFAULT"))
- continue;
-
- if (paircmp(request_pairs, pl->check, &reply_pairs) == 0) {
- DEBUG2(" acct_users: Matched %s at %d",
- pl->name, pl->lineno);
+ 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);
- pairmove(&reply_pairs, &reply_tmp);
+ pairxlatmove(request, reply_pairs, &reply_tmp);
pairmove(config_pairs, &check_tmp);
- pairfree(reply_tmp);
- pairfree(check_tmp); /* should be NULL */
+ pairfree(&reply_tmp);
+ pairfree(&check_tmp);
+
/*
* Fallthrough?
*/
}
/*
- * See if we succeeded.
+ * Remove server internal parameters.
*/
- if (!found)
- return RLM_MODULE_NOOP; /* on to the next module */
+ pairdelete(reply_pairs, PW_FALL_THROUGH, 0);
/*
- * FIXME: log a warning if there are any reply items other than
- * Fallthrough
+ * See if we succeeded.
*/
- pairfree(reply_pairs); /* Don't need these */
+ if (!found)
+ return RLM_MODULE_NOOP; /* on to the next module */
return RLM_MODULE_OK;
+
}
+
/*
- * Clean up.
+ * Find the named user in the database. Create the
+ * set of attribute-value pairs to check and reply with
+ * for this user from the database. The main code only
+ * needs to check the password, the rest is done here.
*/
-static int file_detach(void *instance)
+static int file_authorize(void *instance, REQUEST *request)
{
- struct file_instance *inst = instance;
- pairlist_free(&inst->users);
- pairlist_free(&inst->acctusers);
- free(inst->usersfile);
- free(inst->acctusersfile);
- free(inst);
- return 0;
+ struct file_instance *inst = instance;
+
+ return file_common(inst, request, "users", inst->users,
+ request->packet->vps, &request->reply->vps);
+}
+
+
+/*
+ * Pre-Accounting - read the acct_users file for check_items and
+ * config_items. Reply items are Not Recommended(TM) in acct_users,
+ * except for Fallthrough, which should work
+ */
+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);
+}
+
+#ifdef WITH_PROXY
+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);
+}
+#endif
+
+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);
}
/* globally exported name */
module_t rlm_files = {
+ RLM_MODULE_INIT,
"files",
- 0, /* type: reserved */
- file_init, /* initialization */
+ RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,
file_instantiate, /* instantiation */
- file_authorize, /* authorization */
- file_authenticate, /* authentication */
- file_preacct, /* preaccounting */
- NULL, /* accounting */
file_detach, /* detach */
- NULL /* destroy */
+ {
+ file_authenticate, /* authentication */
+ file_authorize, /* authorization */
+ file_preacct, /* preaccounting */
+ NULL, /* accounting */
+ NULL, /* checksimul */
+#ifdef WITH_PROXY
+ file_preproxy, /* pre-proxy */
+ file_postproxy, /* post-proxy */
+#else
+ NULL, NULL,
+#endif
+ file_postauth /* post-auth */
+ },
};