2 * rlm_fastusers.c authorization: Find a user in the hashed "users" file.
3 * accounting: Do nothing. Auth module only.
10 #include <sys/socket.h>
13 #include <netinet/in.h>
34 struct fastuser_instance {
41 PAIR_LIST **hashtable;
42 PAIR_LIST *default_entry;
48 /* Function declarations */
49 static int fastuser_buildhash(struct fastuser_instance *inst);
50 static int fastuser_getfile(struct fastuser_instance *inst, const char *filename,
51 PAIR_LIST **default_list, PAIR_LIST **hashtable);
52 static int fastuser_hash(const char *s, long hashtablesize);
53 static int fastuser_store(PAIR_LIST **hashtable, PAIR_LIST *entry, int idx);
54 static PAIR_LIST *fastuser_find(PAIR_LIST **hashtable, const char *user,
58 * A temporary holding area for config values to be extracted
59 * into, before they are copied into the instance data
61 static struct fastuser_instance config;
63 static CONF_PARSER module_config[] = {
64 { "usersfile", PW_TYPE_STRING_PTR, &config.usersfile, "${raddbdir}/users_fast" },
65 { "hashsize", PW_TYPE_INTEGER, &config.hashsize, "100000" },
66 { "compat", PW_TYPE_STRING_PTR, &config.compat_mode, "cistron" },
67 { "normal_defaults", PW_TYPE_BOOLEAN, &config.normal_defaults, "yes" },
68 { "hash_reload", PW_TYPE_INTEGER, &config.hash_reload, "600" },
69 { NULL, -1, NULL, NULL }
72 static int fastuser_buildhash(struct fastuser_instance *inst) {
75 PAIR_LIST **newhash=NULL, **oldhash=NULL;
76 PAIR_LIST *newdefaults=NULL, *olddefaults=NULL, *cur=NULL;
79 * Allocate space for hash table here
81 memsize = sizeof(PAIR_LIST *) * inst->hashsize;
82 if( (newhash = (PAIR_LIST **)malloc(memsize)) == NULL) {
83 radlog(L_ERR, "rlm_fastusers: Can't build hashtable, out of memory!");
86 memset((PAIR_LIST *)newhash, 0, memsize);
88 rcode = fastuser_getfile(inst, inst->usersfile, &newdefaults, newhash);
90 radlog(L_ERR|L_CONS, "rlm_fastusers: Errors reading %s", inst->usersfile);
95 * We need to do this now so that users auths
96 * aren't blocked while we free the old table
99 oldhash = inst->hashtable;
100 inst->hashtable = newhash;
101 olddefaults = inst->default_entry;
102 inst->default_entry = newdefaults;
105 * When we get here, we assume the hash built properly.
106 * So we begin to tear down the old one
109 for(hashindex=0; hashindex<inst->hashsize; hashindex++) {
110 if(oldhash[hashindex]) {
111 cur = oldhash[hashindex];
116 pairlist_free(&olddefaults);
121 static int fastuser_getfile(struct fastuser_instance *inst, const char *filename,
122 PAIR_LIST **default_list, PAIR_LIST **hashtable) {
124 PAIR_LIST *users = NULL;
125 int compat_mode = FALSE;
126 PAIR_LIST *entry=NULL, *next=NULL, *cur=NULL, *defaults=NULL;
129 long numdefaults = 0, numusers=0;
131 rcode = pairlist_read(filename, &users, 1);
136 if (strcmp(inst->compat_mode, "cistron") == 0) {
143 DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...",
144 filename, entry->lineno, entry->name);
148 * Look for improper use of '=' in the
149 * check items. They should be using
150 * '==' for on-the-wire RADIUS attributes,
151 * and probably ':=' for server
152 * configuration items.
154 for (vp = entry->check; vp != NULL; vp = vp->next) {
156 * Ignore attributes which are set
159 if (vp->operator != T_OP_EQ)
164 * If it's a vendor attribute,
165 * or it's a wire protocol,
166 * ensure it has '=='.
168 if (((vp->attribute & ~0xffff) != 0) ||
169 (vp->attribute < 0x100)) {
171 DEBUG("[%s]:%d WARNING! Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
172 filename, entry->lineno, vp->name, vp->name, entry->name);
174 DEBUG("\tChanging '%s =' to '%s =='",
177 vp->operator = T_OP_CMP_EQ;
182 * Cistron Compatibility mode.
184 * Re-write selected attributes
185 * to be '+=', instead of '='.
187 * All others get set to '=='
191 * Non-wire attributes become +=
193 * On the write attributes
196 if ((vp->attribute >= 0x100) &&
197 (vp->attribute <= 0xffff) &&
198 (vp->attribute != PW_HINT) &&
199 (vp->attribute != PW_HUNTGROUP_NAME)) {
200 DEBUG("\tChanging '%s =' to '%s +='",
202 vp->operator = T_OP_ADD;
204 DEBUG("\tChanging '%s =' to '%s =='",
206 vp->operator = T_OP_CMP_EQ;
210 } /* end of loop over check items */
214 * Look for server configuration items
217 * It's a common enough mistake, that it's
220 for (vp = entry->reply; vp != NULL; vp = vp->next) {
222 * If it's NOT a vendor attribute,
223 * and it's NOT a wire protocol
224 * and we ignore Fall-Through,
225 * then bitch about it, giving a
226 * good warning message.
228 if (!(vp->attribute & ~0xffff) &&
229 (vp->attribute > 0xff) &&
230 (vp->attribute > 1000)) {
231 log_debug("[%s]:%d WARNING! Check item \"%s\"\n"
232 "\tfound in reply item list for user \"%s\".\n"
233 "\tThis attribute MUST go on the first line"
234 " with the other check items",
235 filename, entry->lineno, vp->name,
241 * Ok, we've done all the same BS as
242 * rlm_users, so here we tear apart the
243 * linked list, and store our users in
244 * the hashtable we've built instead
247 /* Save what was next */
250 /* Save the DEFAULT entry specially */
251 if(strcmp(entry->name, "DEFAULT")==0) {
253 /* put it at the end of the list */
255 for(cur=defaults; cur->next; cur=cur->next);
260 defaults->next = NULL;
266 /* Hash the username */
267 hashindex = fastuser_hash(entry->name, inst->hashsize);
269 /* Store user in the hash */
270 fastuser_store(hashtable, entry, hashindex);
272 /* Restore entry to next pair_list */
276 } /* while(entry) loop */
278 if(!inst->normal_defaults && (numdefaults>1)) {
279 radlog(L_INFO, "Warning: fastusers found multiple DEFAULT entries. Using the first.");
282 *default_list = defaults;
283 radlog(L_INFO, "rlm_fastusers: Loaded %ld users and %ld defaults",
284 numusers, numdefaults);
289 /* Hashes the username sent to it and returns index into hashtable */
290 int fastuser_hash(const char *s, long hashtablesize) {
291 unsigned long hash = 0;
294 hash = hash * 7907 + (unsigned char)*s++;
297 return (hash % hashtablesize);
300 /* Stores the username sent into the hashtable */
301 static int fastuser_store(PAIR_LIST **hashtable, PAIR_LIST *new, int idx) {
303 /* store new record at beginning of list */
304 new->next = hashtable[idx];
305 hashtable[idx] = new;
311 * Looks up user in hashtable. If user can't be found, returns 0.
312 * Otherwise returns a pointer to the structure for the user
314 static PAIR_LIST *fastuser_find(PAIR_LIST **hashtable,
315 const char *user, long hashsize)
321 /* first hash the username and get the index into the hashtable */
322 idx = fastuser_hash(user, hashsize);
324 cur = hashtable[idx];
326 while((cur != NULL) && (strcmp(cur->name, user))) {
331 DEBUG2(" fastusers: user %s found in hashtable bucket %d", user, idx);
335 return (PAIR_LIST *)0;
341 * (Re-)read the "users" file into memory.
343 static int fastuser_instantiate(CONF_SECTION *conf, void **instance)
345 struct fastuser_instance *inst=0;
347 inst = malloc(sizeof *inst);
349 radlog(L_ERR|L_CONS, "Out of memory\n");
352 memset(inst, 0, sizeof(inst));
354 if (cf_section_parse(conf, module_config) < 0) {
359 inst->usersfile = config.usersfile;
360 inst->hashsize = config.hashsize;
361 inst->default_entry = config.default_entry;
362 inst->compat_mode = config.compat_mode;
363 inst->normal_defaults = config.normal_defaults;
364 inst->hash_reload = config.hash_reload;
365 inst->next_reload = time(NULL) + inst->hash_reload;
366 inst->hashtable = NULL;
367 if(fastuser_buildhash(inst) < 0) {
368 radlog(L_ERR, "rlm_fastusers: error building user hash. aborting");
372 config.usersfile = NULL;
373 config.hashtable = NULL;
374 config.default_entry = NULL;
375 config.compat_mode = NULL;
382 * Find the named user in the database. Create the
383 * set of attribute-value pairs to check and reply with
384 * for this user from the database. The main code only
385 * needs to check the password, the rest is done here.
387 static int fastuser_authorize(void *instance, REQUEST *request)
390 VALUE_PAIR *namepair;
391 VALUE_PAIR *request_pairs;
392 VALUE_PAIR *check_tmp;
393 VALUE_PAIR *reply_tmp;
394 VALUE_PAIR **check_pairs;
395 VALUE_PAIR **reply_pairs;
396 VALUE_PAIR *check_save;
400 int checkdefault = 0;
401 struct fastuser_instance *inst = instance;
403 request_pairs = request->packet->vps;
404 check_pairs = &request->config_items;
405 reply_pairs = &request->reply->vps;
408 * Do we need to reload the cache?
409 * Really we should spawn a thread to do this
411 if((inst->hash_reload) && (request->timestamp > inst->next_reload)) {
412 inst->next_reload = request->timestamp + inst->hash_reload;
413 radlog(L_INFO, "rlm_fastusers: Reloading fastusers hash");
414 if(fastuser_buildhash(inst) < 0) {
415 radlog(L_ERR, "rlm_fastusers: error building user hash. aborting");
421 * Grab the canonical user name.
423 namepair = request->username;
424 name = namepair ? (char *) namepair->strvalue : "NONE";
427 * Find the entry for the user.
429 if((user=fastuser_find(inst->hashtable, name, inst->hashsize))==NULL) {
430 if(inst->normal_defaults) {
433 return RLM_MODULE_NOTFOUND;
438 * Usercollide means we have to compare check pairs
441 if(mainconfig.do_usercollide && !checkdefault) {
442 /* Save the orginal config items */
443 check_save = paircopy(request->config_items);
445 while((user) && (!found) && (strcmp(user->name, name)==0)) {
446 if(paircmp(request_pairs, user->check, reply_pairs) != 0) {
450 DEBUG2(" fastusers(uc): Checking %s at %d", user->name, user->lineno);
452 /* Copy this users check pairs to the request */
453 check_tmp = paircopy(user->check);
454 pairmove(check_pairs, &check_tmp);
457 /* Check the req to see if we matched */
458 if(rad_check_password(request)==0) {
461 /* We didn't match here */
463 /* Restore check items */
464 pairfree(request->config_items);
465 request->config_items = paircopy(check_save);
466 check_pairs = &request->config_items;
471 /* Free our saved config items */
472 pairfree(check_save);
476 * No usercollide, just compare check pairs
478 if(!mainconfig.do_usercollide && !checkdefault) {
479 while((user) && (!found) && (strcmp(user->name, name)==0)) {
480 if(paircmp(request_pairs, user->check, reply_pairs) == 0) {
482 DEBUG2(" fastusers: Matched %s at %d", user->name, user->lineno);
490 * When we get here, we've either found the user or not
491 * and we either do normal DEFAULTs or not.
495 * We found the user & normal default
496 * copy relevant pairs and return
498 if(found && inst->normal_defaults) {
499 check_tmp = paircopy(user->check);
500 pairmove(check_pairs, &check_tmp);
502 reply_tmp = paircopy(user->reply);
503 pairmove(reply_pairs, &reply_tmp);
505 return RLM_MODULE_UPDATED;
509 * We didn't find the user, and we aren't supposed to
510 * check defaults. So just report not found.
512 if(!found && !inst->normal_defaults) {
513 return RLM_MODULE_NOTFOUND;
517 * We didn't find the user, but we should
518 * check the defaults.
520 if(!found && inst->normal_defaults) {
521 user = inst->default_entry;
522 while((user) && (!found)) {
523 if(paircmp(request_pairs, user->check, reply_pairs) == 0) {
524 DEBUG2(" fastusers: Matched %s at %d", user->name, user->lineno);
532 check_tmp = paircopy(user->check);
533 pairmove(check_pairs, &check_tmp);
535 reply_tmp = paircopy(user->reply);
536 pairmove(reply_pairs, &reply_tmp);
538 return RLM_MODULE_UPDATED;
541 return RLM_MODULE_NOTFOUND;
546 * We found the user, and we don't use normal defaults.
547 * So copy the check and reply pairs from the default
548 * entry to the request
550 if(found && !inst->normal_defaults) {
552 /* We've already done this above if(mainconfig.do_usercollide) */
553 if(!mainconfig.do_usercollide) {
554 check_tmp = paircopy(user->check);
555 pairmove(check_pairs, &check_tmp);
558 reply_tmp = paircopy(user->reply);
559 pairmove(reply_pairs, &reply_tmp);
563 * We also need to add the pairs from
564 * inst->default_entry if the vp is
565 * not already present.
568 if(inst->default_entry) {
569 check_tmp = paircopy(inst->default_entry->check);
570 reply_tmp = paircopy(inst->default_entry->reply);
571 pairmove(reply_pairs, &reply_tmp);
572 pairmove(check_pairs, &check_tmp);
577 return RLM_MODULE_UPDATED;
580 return RLM_MODULE_NOOP;
584 * Authentication - unused.
586 static int fastuser_authenticate(void *instance, REQUEST *request)
590 return RLM_MODULE_OK;
596 static int fastuser_detach(void *instance)
598 struct fastuser_instance *inst = instance;
602 /* Free hash table */
603 for(hashindex=0; hashindex<inst->hashsize; hashindex++) {
604 if(inst->hashtable[hashindex]) {
605 cur = inst->hashtable[hashindex];
610 free(inst->hashtable);
611 pairlist_free(&inst->default_entry);
612 free(inst->usersfile);
613 free(inst->compat_mode);
619 * This function is unused
621 static int fastuser_preacct(void *instance, REQUEST *request)
623 return RLM_MODULE_FAIL;
627 * This function is unused
629 static int fastuser_accounting(void *instance, REQUEST *request)
631 return RLM_MODULE_FAIL;
634 /* globally exported name */
635 module_t rlm_fastusers = {
637 0, /* type: reserved */
638 NULL, /* initialization */
639 fastuser_instantiate, /* instantiation */
640 fastuser_authorize, /* authorization */
641 fastuser_authenticate, /* authentication */
642 fastuser_preacct, /* preaccounting */
643 fastuser_accounting, /* accounting */
644 NULL, /* checksimul */
645 fastuser_detach, /* detach */