/*
* rlm_fastusers.c authorization: Find a user in the hashed "users" file.
- * accounting: Do nothing. Auth module only.
+ * accounting: Do nothing. Auth module only.
*
+ * 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 2000,2006 The FreeRADIUS server project
+ * Copyright 2000 Jeff Carneal <jeff@apex.net>
*/
-#include "autoconf.h"
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/time.h>
#include <sys/stat.h>
-#include <netinet/in.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
#include <pwd.h>
#include <grp.h>
-#include <time.h>
#include <ctype.h>
#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-
-#if HAVE_MALLOC_H
-# include <malloc.h>
-#endif
-
-#include "radiusd.h"
-#include "modules.h"
+#include <limits.h>
struct fastuser_instance {
char *compat_mode;
- int normal_defaults;
+ int hash_reload;
/* hash table */
- long hashsize;
+ int hashsize;
PAIR_LIST **hashtable;
- PAIR_LIST *users;
- PAIR_LIST *default_entry;
+ PAIR_LIST *defaults;
+ PAIR_LIST *acctusers;
+ int stats;
char *usersfile;
+ char *acctusersfile;
+ time_t next_reload;
+ time_t lastusersload;
+ time_t lastacctusersload;
};
/* Function declarations */
-static int fastuser_getfile(const char *filename, PAIR_LIST **hashtable);
-static int fastuser_hash(const char *s, long hashtablesize);
+static int fallthrough(VALUE_PAIR *vp);
+static int fastuser_buildhash(struct fastuser_instance *inst);
+static int fastuser_getfile(struct fastuser_instance *inst, const char *filename,
+ PAIR_LIST **default_list, PAIR_LIST **pair_list,
+ int isacctfile);
+static int fastuser_hash(const char *s, int hashtablesize);
static int fastuser_store(PAIR_LIST **hashtable, PAIR_LIST *entry, int idx);
-static PAIR_LIST *fastuser_find(PAIR_LIST **hashtable, const char *user,
- long hashsize);
+static PAIR_LIST *fastuser_find(REQUEST *request, PAIR_LIST *user,
+ const char *username);
+static void fastuser_tablestats(PAIR_LIST **hashtable, int size);
+
+static const CONF_PARSER module_config[] = {
+ { "usersfile", PW_TYPE_FILENAME,
+ offsetof(struct fastuser_instance,usersfile), NULL, "${raddbdir}/users_fast" },
+ { "acctusersfile", PW_TYPE_FILENAME,
+ offsetof(struct fastuser_instance,acctusersfile), NULL, "${raddbdir}/acct_users" },
+ { "hashsize", PW_TYPE_INTEGER,
+ offsetof(struct fastuser_instance,hashsize), NULL, "100000" },
+ { "stats", PW_TYPE_BOOLEAN,
+ offsetof(struct fastuser_instance,stats), NULL, "no" },
+ { "compat", PW_TYPE_STRING_PTR,
+ offsetof(struct fastuser_instance,compat_mode), NULL, "cistron" },
+ { "hash_reload", PW_TYPE_INTEGER,
+ offsetof(struct fastuser_instance,hash_reload), NULL, "600" },
+ { NULL, -1, 0, NULL, NULL }
+};
/*
- * A temporary holding area for config values to be extracted
- * into, before they are copied into the instance data
+ * See if a VALUE_PAIR list contains Fall-Through = Yes
*/
-static struct fastuser_instance config;
-
-static CONF_PARSER module_config[] = {
- { "usersfile", PW_TYPE_STRING_PTR, &config.usersfile, "${raddbdir}/users_fast" },
- { "hashsize", PW_TYPE_INTEGER, &config.hashsize, "100000" },
- { "compat", PW_TYPE_STRING_PTR, &config.compat_mode, "cistron" },
- { "normal_defaults", PW_TYPE_BOOLEAN, &config.normal_defaults, "yes" },
- { NULL, -1, NULL, NULL }
-};
+static int fallthrough(VALUE_PAIR *vp)
+{
+ VALUE_PAIR *tmp;
+ tmp = pairfind(vp, PW_FALL_THROUGH);
+ return tmp ? tmp->lvalue : 0;
+}
-static int fastuser_getfile(const char *filename, PAIR_LIST **hashtable)
+/*
+ * returncheck - Check for Auth-Type = Reject and return appropriate
+ * module return code if it is found.
+ */
+static int rad_check_return(VALUE_PAIR *list)
{
+ VALUE_PAIR *authtype;
+
+ /*
+ * We check for Auth-Type = Reject here
+ */
+
+ authtype = pairfind(list, PW_AUTHTYPE);
+ if((authtype) && authtype->lvalue == PW_AUTHTYPE_REJECT) {
+ DEBUG2("rad_check_return: Auth-Type is Reject");
+ return RLM_MODULE_REJECT;
+ }
+
+ return RLM_MODULE_UPDATED;
+}
+
+static int fastuser_buildhash(struct fastuser_instance *inst) {
+ int memsize=0;
+ int rcode, hashindex;
+ PAIR_LIST **newhash=NULL, **oldhash=NULL;
+ PAIR_LIST *newdefaults=NULL, *newacctusers, *cur=NULL;
+ PAIR_LIST *olddefaults=NULL, *oldacctusers=NULL;
+ struct stat statbuf;
+ int reloadusers = 1;
+ int reloadacctusers = 1;
+
+ /*
+ * Allocate space for hash table here
+ */
+ memsize = sizeof(PAIR_LIST *) * inst->hashsize;
+
+ newhash = (PAIR_LIST **) rad_malloc(memsize);
+
+ memset((PAIR_LIST *)newhash, 0, memsize);
+
+ /* Check acct_users last modification time */
+ if ((stat(inst->acctusersfile, &statbuf) != -1)
+ && (statbuf.st_mtime <= inst->lastacctusersload)) {
+ DEBUG2("rlm_fastusers: File %s was unchanged. Not reloading.",
+ inst->acctusersfile);
+ reloadacctusers = 0;
+ rcode = 0;
+ }
+ else
+ /* Read acct_users */
+ rcode = fastuser_getfile(inst, inst->acctusersfile, NULL, &newacctusers, 1);
+
+ if (rcode != 0) {
+ radlog(L_ERR|L_CONS, "rlm_fastusers: Errors reading %s", inst->usersfile);
+ return -1;
+ }
+
+ /* Check users last modification time */
+ if ((stat(inst->usersfile, &statbuf) != -1)
+ && (statbuf.st_mtime <= inst->lastusersload)) {
+ DEBUG2("rlm_fastusers: File %s was unchanged. Not reloading.",
+ inst->usersfile);
+ reloadusers = 0;
+ rcode = 0;
+ /* This was allocated earlier but will remain unused */
+ free(newhash);
+ }
+ else
+ /* Read users */
+ rcode = fastuser_getfile(inst, inst->usersfile, &newdefaults, newhash, 0);
+
+ if (rcode != 0) {
+ radlog(L_ERR|L_CONS, "rlm_fastusers: Errors reading %s", inst->usersfile);
+ return -1;
+ }
+
+ if (reloadusers) {
+ /*
+ * We need to do this now so that users auths
+ * aren't blocked while we free the old table
+ * below
+ */
+ inst->lastusersload = time(NULL);
+ oldhash = inst->hashtable;
+ inst->hashtable = newhash;
+ olddefaults = inst->defaults;
+ inst->defaults = newdefaults;
+
+ /*
+ * When we get here, we assume the hash built properly.
+ * So we begin to tear down the old one
+ */
+ if (oldhash) {
+ for(hashindex=0; hashindex<inst->hashsize; hashindex++) {
+ if(oldhash[hashindex]) {
+ cur = oldhash[hashindex];
+ pairlist_free(&cur);
+ }
+ }
+ free(oldhash);
+ }
+ pairlist_free(&olddefaults);
+ }
+ if (reloadacctusers) {
+ inst->lastacctusersload = time(NULL);
+ oldacctusers = inst->acctusers;
+ inst->acctusers = newacctusers;
+ pairlist_free(&oldacctusers);
+ }
+
+ if(inst->stats)
+ fastuser_tablestats(inst->hashtable, inst->hashsize);
+
+ return 0;
+}
+
+static int fastuser_getfile(struct fastuser_instance *inst, const char *filename,
+ PAIR_LIST **default_list, PAIR_LIST **pair_list,
+ int isacctfile) {
int rcode;
PAIR_LIST *users = NULL;
+ PAIR_LIST *entry=NULL, *next=NULL, *cur=NULL, *defaults=NULL, *lastdefault=NULL;
int compat_mode = FALSE;
- PAIR_LIST *entry, *next, *cur;
- VALUE_PAIR *vp;
+ VALUE_PAIR *vp=NULL;
int hashindex = 0;
- int numdefaults = 0;
+ int numdefaults = 0, numusers=0;
+ radlog(L_INFO, " fastusers: Reading %s", filename);
rcode = pairlist_read(filename, &users, 1);
if (rcode < 0) {
return -1;
}
- if (strcmp(config.compat_mode, "cistron") == 0) {
+ if (strcmp(inst->compat_mode, "cistron") == 0) {
compat_mode = TRUE;
}
-
+
entry = users;
while (entry) {
if (compat_mode) {
* configuration items.
*/
for (vp = entry->check; vp != NULL; vp = vp->next) {
- /*
+ /*
* Ignore attributes which are set
* properly.
*/
- if (vp->operator != T_OP_EQ)
+ if (vp->operator != T_OP_EQ)
continue;
-
+
/*
* If it's a vendor attribute,
- * or it's a wire protocol,
+ * or it's a wire protocol,
* ensure it has '=='.
*/
if (((vp->attribute & ~0xffff) != 0) ||
vp->operator = T_OP_CMP_EQ;
continue;
}
-
+
/*
* Cistron Compatibility mode.
*
vp->operator = T_OP_CMP_EQ;
}
}
-
+
} /* end of loop over check items */
-
-
+
+
/*
* Look for server configuration items
* in the reply list.
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",
+ " with the other check items",
filename, entry->lineno, vp->name,
entry->name);
}
/* Save what was next */
next = entry->next;
- /* Save the DEFAULT entry specially */
- if(strcmp(entry->name, "DEFAULT")==0) {
+ if(!isacctfile) {
+ /* Save the DEFAULT entry specially */
+ if(strcmp(entry->name, "DEFAULT")==0) {
+
+ /* Save this as the last default we've seen */
+ lastdefault = entry;
numdefaults++;
+
/* put it at the end of the list */
- if(config.default_entry) {
- for(cur=config.default_entry; cur->next; cur=cur->next);
+ if(defaults) {
+ for(cur=defaults; cur->next; cur=cur->next);
cur->next = entry;
entry->next = NULL;
} else {
- config.default_entry = entry;
- config.default_entry->next = NULL;
+ defaults = entry;
+ defaults->next = NULL;
}
- } else {
+ } else {
+ numusers++;
- /* Hash the username */
- hashindex = fastuser_hash(entry->name, config.hashsize);
+ /* Hash the username */
+ hashindex = fastuser_hash(entry->name, inst->hashsize);
- /* Store user in the hash */
- fastuser_store(hashtable, entry, hashindex);
+ /* Store the last default before this entry */
+ entry->lastdefault = lastdefault;
- /* Restore entry to next pair_list */
+ /* Store user in the hash */
+ fastuser_store(pair_list, entry, hashindex);
+ }
}
+ /* Restore entry to next pair_list */
entry = next;
} /* while(entry) loop */
- if(!config.normal_defaults && (numdefaults>1)) {
- radlog(L_INFO, "Warning: fastusers found multiple DEFAULT entries. Using the first.");
+ if(!isacctfile && (default_list)) {
+ *default_list = defaults;
+ radlog(L_INFO, "rlm_fastusers: Loaded %d users and %d defaults",
+ numusers, numdefaults);
+ } else {
+ *pair_list = users;
}
- /*
- * We *should* do this to help out clueless admins
- * but it's documented, so it will confuse those who
- * do read the docs if we do it here as well
- if(!config.normal_defaults) {
- pairdelete(&config.default_entry->check, PW_AUTHTYPE);
- }
- */
-
return 0;
}
/* Hashes the username sent to it and returns index into hashtable */
-int fastuser_hash(const char *s, long hashtablesize) {
- unsigned long hash = 0;
+int fastuser_hash(const char *s, int hashtablesize) {
+ unsigned int hash = 0;
- while (*s != '\0') {
- hash = hash * 7907 + (unsigned char)*s++;
- }
+ while (*s != '\0') {
+ hash = hash * 7907 + (unsigned char)*s++;
+ }
- return (hash % hashtablesize);
+ return (hash % hashtablesize);
}
/* Stores the username sent into the hashtable */
static int fastuser_store(PAIR_LIST **hashtable, PAIR_LIST *new, int idx) {
+ PAIR_LIST *cur;
- /* store new record at beginning of list */
- new->next = hashtable[idx];
- hashtable[idx] = new;
-
- return 1;
+ cur = hashtable[idx];
+ /* store new record at end of list */
+ if(cur) {
+ while (cur->next != NULL)
+ cur=cur->next;
+ cur->next = new;
+ new->next = NULL;
+ } else {
+ new->next = hashtable[idx];
+ hashtable[idx] = new;
+ }
+ return 1;
}
/*
* Looks up user in hashtable. If user can't be found, returns 0.
* Otherwise returns a pointer to the structure for the user
*/
-static PAIR_LIST *fastuser_find(PAIR_LIST **hashtable,
- const char *user, long hashsize)
+static PAIR_LIST *fastuser_find(REQUEST *request, PAIR_LIST *user,
+ const char *username)
{
+ PAIR_LIST *cur=user;
+ int userfound = 0;
- PAIR_LIST *cur;
- int idx;
+ /*
+ * Now we have to make sure it's the right user by
+ * comparing the check pairs
+ */
+ while((cur) && (!userfound)) {
+ if((strcmp(cur->name, username)==0) &&
+ paircompare(request, request->packet->vps, cur->check, &request->reply->vps) == 0) {
+ userfound = 1;
+ DEBUG2(" fastusers: Matched %s at %d", cur->name, cur->lineno);
+ } else {
+ cur = cur->next;
+ }
+ }
- /* first hash the username and get the index into the hashtable */
- idx = fastuser_hash(user, hashsize);
+ if(cur) {
+ return cur;
+ }
- cur = hashtable[idx];
+ return (PAIR_LIST *)0;
+}
- while((cur != NULL) && (strcmp(cur->name, user))) {
- cur = cur->next;
- }
+/*
+ * Generate and log statistics about our hash table
+ */
+static void fastuser_tablestats(PAIR_LIST **hashtable, int size) {
+ int i, count;
+ int countarray[256];
+ int toomany=0;
+ PAIR_LIST *cur;
- if(cur) {
- DEBUG2(" fastusers: user %s found in hashtable bucket %d", user, idx);
- return cur;
- }
+ memset(countarray, 0, sizeof(countarray));
- return (PAIR_LIST *)0;
+ for(i=0; i<size; i++) {
+ count = 0;
+ for(cur=hashtable[i]; cur; cur=cur->next) {
+ count++;
+ }
+ if(count<256) {
+ countarray[count]++;
+ } else {
+ toomany++;
+ }
+ }
-}
+ for(i=0; i<256; i++)
+ if(countarray[i]) {
+ radlog(L_INFO, "rlm_fastusers: Hash buckets with %d users: %d",
+ i, countarray[i]);
+ }
+ if(toomany) {
+ radlog(L_INFO, "rlm_fastusers: Hash buckets with more than 256: %d",
+ toomany);
+ }
+}
/*
* (Re-)read the "users" file into memory.
static int fastuser_instantiate(CONF_SECTION *conf, void **instance)
{
struct fastuser_instance *inst=0;
- int rcode;
- long memsize=0;
- inst = malloc(sizeof *inst);
- if (!inst) {
- radlog(L_ERR|L_CONS, "Out of memory\n");
+ inst = rad_malloc(sizeof *inst);
+ if (!inst)
return -1;
- }
- memset(inst, 0, sizeof(inst));
+ memset(inst, 0, sizeof(*inst));
- if (cf_section_parse(conf, module_config) < 0) {
+ if (cf_section_parse(conf, inst, module_config) < 0) {
free(inst);
return -1;
}
- /*
- * Sue me. The tradeoff for this extra variable
- * is clean code below
- */
- memsize = sizeof(PAIR_LIST *) * config.hashsize;
- /*
- * Allocate space for hash table here
- */
- if( (inst->hashtable = (PAIR_LIST **)malloc(memsize)) == NULL) {
- radlog(L_ERR, "fastusers: Can't build hashtable, out of memory!");
- return -1;
- }
- memset((PAIR_LIST *)inst->hashtable, 0, memsize);
-
- rcode = fastuser_getfile(config.usersfile, inst->hashtable);
- if (rcode != 0) {
- radlog(L_ERR|L_CONS, "Errors reading %s", config.usersfile);
+ inst->next_reload = time(NULL) + inst->hash_reload;
+ inst->hashtable = NULL;
+ inst->lastusersload = 0;
+ inst->lastacctusersload = 0;
+ if(fastuser_buildhash(inst) < 0) {
+ radlog(L_ERR, "rlm_fastusers: error building user hash. aborting");
return -1;
}
- inst->usersfile = config.usersfile;
- inst->hashsize = config.hashsize;
- inst->default_entry = config.default_entry;
- inst->compat_mode = config.compat_mode;
- inst->normal_defaults = config.normal_defaults;
- inst->users = NULL;
-
- config.usersfile = NULL;
- config.hashtable = NULL;
- config.default_entry = NULL;
- config.users = NULL;
- config.compat_mode = NULL;
+ /*
+ * Need code here to read acct_users file
+ */
*instance = inst;
return 0;
{
VALUE_PAIR *namepair;
- VALUE_PAIR *request_pairs;
VALUE_PAIR *check_tmp;
VALUE_PAIR *reply_tmp;
- VALUE_PAIR **check_pairs;
- VALUE_PAIR **reply_pairs;
- VALUE_PAIR *check_save;
PAIR_LIST *user;
+ PAIR_LIST *curdefault;
const char *name;
- int found=0;
- int checkdefault = 0;
+ int userfound=0;
+ int defaultfound=0;
+ int hashidx=0;
struct fastuser_instance *inst = instance;
- request_pairs = request->packet->vps;
- check_pairs = &request->config_items;
- reply_pairs = &request->reply->vps;
+ /*
+ * Do we need to reload the cache?
+ * Really we should spawn a thread to do this
+ */
+ if((inst->hash_reload) && (request->timestamp > inst->next_reload)) {
+ inst->next_reload = request->timestamp + inst->hash_reload;
+ radlog(L_INFO, "rlm_fastusers: Reloading fastusers hash");
+ if(fastuser_buildhash(inst) < 0) {
+ radlog(L_ERR, "rlm_fastusers: error building user hash. aborting");
+ return RLM_MODULE_FAIL;
+ }
+ }
- /*
+ /*
* Grab the canonical user name.
*/
namepair = request->username;
- name = namepair ? (char *) namepair->strvalue : "NONE";
+ name = namepair ? (char *) namepair->vp_strvalue : "NONE";
/*
* Find the entry for the user.
*/
- if((user=fastuser_find(inst->hashtable, name, inst->hashsize))==NULL) {
- if(inst->normal_defaults) {
- checkdefault = 1;
- } else {
- return RLM_MODULE_NOTFOUND;
- }
+ hashidx = fastuser_hash(name, inst->hashsize);
+ user = inst->hashtable[hashidx];
+ if((user=fastuser_find(request, user, name))!=NULL) {
+ userfound = 1;
}
/*
- * Usercollide means we have to compare check pairs
- * _and_ the password
+ * If there's no lastdefault and we
+ * don't fallthrough, just copy the
+ * pairs for this user and return
*/
- if(mainconfig.do_usercollide && !checkdefault) {
- /* Save the orginal config items */
- check_save = paircopy(request->config_items);
-
- while((user) && (!found) && (strcmp(user->name, name)==0)) {
- if(paircmp(request_pairs, user->check, reply_pairs) != 0) {
- user = user->next;
- continue;
- }
- DEBUG2(" fastusers(uc): Checking %s at %d", user->name, user->lineno);
+ if((user) && (userfound) && (user->lastdefault == NULL)) {
+ DEBUG2("rlm_fastusers: user found before DEFAULT");
- /* Copy this users check pairs to the request */
- check_tmp = paircopy(user->check);
- pairmove(check_pairs, &check_tmp);
- pairfree(check_tmp);
+ check_tmp = paircopy(user->check);
+ pairmove(&request->config_items, &check_tmp);
+ pairfree(&check_tmp);
- /* Check the req to see if we matched */
- if(rad_check_password(request)==0) {
- found = 1;
+ reply_tmp = paircopy(user->reply);
+ pairmove(&request->reply->vps, &reply_tmp);
+ pairfree(&reply_tmp);
- /* We didn't match here */
- } else {
- /* Restore check items */
- pairfree(request->config_items);
- request->config_items = paircopy(check_save);
- check_pairs = &request->config_items;
- user = user->next;
- }
+ if(!fallthrough(user->reply)) {
+ pairdelete(&request->reply->vps, PW_FALL_THROUGH);
+ return(rad_check_return(user->check));
+ } else {
+ user=user->next;
+ user=fastuser_find(request, user, name);
}
-
- /* Free our saved config items */
- pairfree(check_save);
}
/*
- * No usercollide, just compare check pairs
+ * When we get here, we've either found
+ * the user or not, but to preserve order
+ * we start at the top of the default
+ * list and work our way thru
+ * When we get to the user's 'lastdefault'
+ * we check to see if we should stop
+ * and return
*/
- if(!mainconfig.do_usercollide && !checkdefault) {
- while((user) && (!found) && (strcmp(user->name, name)==0)) {
- if(paircmp(request_pairs, user->check, reply_pairs) == 0) {
- found = 1;
- DEBUG2(" fastusers: Matched %s at %d", user->name, user->lineno);
- } else {
- user = user->next;
- }
- }
- }
+ DEBUG2("rlm_fastusers: checking defaults");
- /*
- * When we get here, we've either found the user or not
- * and we either do normal DEFAULTs or not.
- */
-
- /*
- * We found the user & normal default
- * copy relevant pairs and return
- */
- if(found && inst->normal_defaults) {
- check_tmp = paircopy(user->check);
- pairmove(check_pairs, &check_tmp);
- pairfree(check_tmp);
- reply_tmp = paircopy(user->reply);
- pairmove(reply_pairs, &reply_tmp);
- pairfree(reply_tmp);
- return RLM_MODULE_UPDATED;
- }
+ curdefault = inst->defaults;
+ while(curdefault) {
+ if(paircompare(request, request->packet->vps, curdefault->check,
+ &request->reply->vps) == 0) {
+ DEBUG2(" fastusers: Matched %s at %d",
+ curdefault->name, curdefault->lineno);
+ defaultfound = 1;
- /*
- * We didn't find the user, and we aren't supposed to
- * check defaults. So just report not found.
- */
- if(!found && !inst->normal_defaults) {
- return RLM_MODULE_NOTFOUND;
- }
+ check_tmp = paircopy(curdefault->check);
+ pairmove(&request->config_items, &check_tmp);
+ pairfree(&check_tmp);
- /*
- * We didn't find the user, but we should
- * check the defaults.
- */
- if(!found && inst->normal_defaults) {
- user = inst->default_entry;
- while((user) && (!found)) {
- if(paircmp(request_pairs, user->check, reply_pairs) == 0) {
- DEBUG2(" fastusers: Matched %s at %d", user->name, user->lineno);
- found = 1;
- } else {
- user = user->next;
- }
- }
+ reply_tmp = paircopy(curdefault->reply);
+ pairmove(&request->reply->vps, &reply_tmp);
+ pairfree(&reply_tmp);
- if(found) {
- check_tmp = paircopy(user->check);
- pairmove(check_pairs, &check_tmp);
- pairfree(check_tmp);
- reply_tmp = paircopy(user->reply);
- pairmove(reply_pairs, &reply_tmp);
- pairfree(reply_tmp);
- return RLM_MODULE_UPDATED;
+ /*
+ * There's no fallthru on this default which
+ * is *before* we find the user in the file,
+ * so we know it's safe to quit here
+ */
+ if (!fallthrough(curdefault->reply))
+ break;
- } else {
- return RLM_MODULE_NOTFOUND;
}
- }
- /*
- * We found the user, and we don't use normal defaults.
- * So copy the check and reply pairs from the default
- * entry to the request
- */
- if(found && !inst->normal_defaults) {
+ /*
+ * If we found the user, we want to stop
+ * processing once we get to 'lastdefault'
+ * This way we can process this user's entry
+ * in the order it was found in the file
+ */
+ while((userfound && (user) && (curdefault == user->lastdefault))) {
+ DEBUG2(" fastusers: found lastdefault at line %d",
+ curdefault->lineno);
- /* We've already done this above if(mainconfig.do_usercollide) */
- if(!mainconfig.do_usercollide) {
check_tmp = paircopy(user->check);
- pairmove(check_pairs, &check_tmp);
- pairfree(check_tmp);
- }
- reply_tmp = paircopy(user->reply);
- pairmove(reply_pairs, &reply_tmp);
- pairfree(reply_tmp);
+ pairmove(&request->config_items, &check_tmp);
+ pairfree(&check_tmp);
- /*
- * We also need to add the pairs from
- * inst->default_entry if the vp is
- * not already present.
- */
-
- if(inst->default_entry) {
- check_tmp = paircopy(inst->default_entry->check);
- reply_tmp = paircopy(inst->default_entry->reply);
- pairmove(reply_pairs, &reply_tmp);
- pairmove(check_pairs, &check_tmp);
- pairfree(reply_tmp);
- pairfree(check_tmp);
+ reply_tmp = paircopy(user->reply);
+ pairmove(&request->reply->vps, &reply_tmp);
+ pairfree(&reply_tmp);
+
+ if(!fallthrough(user->reply)) {
+ pairdelete(&request->reply->vps, PW_FALL_THROUGH);
+ return(rad_check_return(user->check));
+ }
+
+ /*
+ * Find next occurence of THIS user in
+ * the users file
+ */
+ user=user->next;
+ user=fastuser_find(request, user, name);
}
- return RLM_MODULE_UPDATED;
+ curdefault = curdefault->next;
}
- return RLM_MODULE_NOOP;
+ if(userfound || defaultfound) {
+ pairdelete(&request->reply->vps, PW_FALL_THROUGH);
+ return(rad_check_return(request->config_items));
+ } else {
+ DEBUG2("rlm_fastusers: user not found");
+ return RLM_MODULE_NOTFOUND;
+ }
}
/*
}
/*
+ * 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
+ */
+static int fastuser_preacct(void *instance, REQUEST *request)
+{
+ VALUE_PAIR *namepair;
+ const char *name;
+ VALUE_PAIR *request_pairs;
+ VALUE_PAIR **config_pairs;
+ VALUE_PAIR *reply_pairs = NULL;
+ VALUE_PAIR *check_tmp;
+ VALUE_PAIR *reply_tmp;
+ PAIR_LIST *pl = NULL;
+ int found = 0;
+ struct fastuser_instance *inst = instance;
+
+ namepair = request->username;
+ name = namepair ? (char *) namepair->vp_strvalue : "NONE";
+ request_pairs = request->packet->vps;
+ config_pairs = &request->config_items;
+
+ /*
+ * Find the entry for the user.
+ */
+ for (pl = inst->acctusers; pl; pl = pl->next) {
+
+ if (strcmp(name, pl->name) && strcmp(pl->name, "DEFAULT"))
+ continue;
+
+ if (paircompare(request, request_pairs, pl->check, &reply_pairs) == 0) {
+ DEBUG2(" acct_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(config_pairs, &check_tmp);
+ pairfree(&reply_tmp);
+ pairfree(&check_tmp); /* should be NULL */
+ /*
+ * Fallthrough?
+ */
+ if (!fallthrough(pl->reply))
+ break;
+ }
+ }
+
+ /*
+ * See if we succeeded.
+ */
+ if (!found)
+ return RLM_MODULE_NOOP; /* on to the next module */
+
+ /*
+ * FIXME: log a warning if there are any reply items other than
+ * Fallthrough
+ */
+ pairfree(&reply_pairs); /* Don't need these */
+
+ return RLM_MODULE_OK;
+}
+
+/*
* Clean up.
*/
static int fastuser_detach(void *instance)
int hashindex;
PAIR_LIST *cur;
-
/* Free hash table */
for(hashindex=0; hashindex<inst->hashsize; hashindex++) {
if(inst->hashtable[hashindex]) {
cur = inst->hashtable[hashindex];
pairlist_free(&cur);
}
- }
+ }
free(inst->hashtable);
- pairlist_free(&inst->users);
- pairlist_free(&inst->default_entry);
- free(inst->usersfile);
- free(inst->compat_mode);
- free(inst);
- return 0;
-}
-
-/*
- * This function is unused
- */
-static int fastuser_preacct(void *instance, REQUEST *request)
-{
- return RLM_MODULE_FAIL;
+ pairlist_free(&inst->defaults);
+ pairlist_free(&inst->acctusers);
+ return 0;
}
/*
* This function is unused
*/
-static int fastuser_accounting(void *instance, REQUEST *request)
+static int fastuser_accounting(void *instance UNUSED, REQUEST *request UNUSED)
{
+ /*
+ * FIXME: should re rather return RLM_MODULE_NOOP here?
+ */
return RLM_MODULE_FAIL;
}
/* globally exported name */
module_t rlm_fastusers = {
+ RLM_MODULE_INIT,
"fastusers",
0, /* type: reserved */
- NULL, /* initialization */
fastuser_instantiate, /* instantiation */
- fastuser_authorize, /* authorization */
- fastuser_authenticate, /* authentication */
- fastuser_preacct, /* preaccounting */
- fastuser_accounting, /* accounting */
- NULL, /* checksimul */
- fastuser_detach, /* detach */
- NULL /* destroy */
+ fastuser_detach, /* detach */
+ {
+ fastuser_authenticate, /* authentication */
+ fastuser_authorize, /* authorization */
+ fastuser_preacct, /* preaccounting */
+ fastuser_accounting, /* accounting */
+ NULL, /* checksimul */
+ NULL, /* pre-proxy */
+ NULL, /* post-proxy */
+ NULL /* post-auth */
+ },
};