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 {
39 PAIR_LIST **hashtable;
41 PAIR_LIST *default_entry;
46 /* Function declarations */
47 static int fastuser_getfile(const char *filename, PAIR_LIST **hashtable);
48 static int fastuser_hash(const char *s, long hashtablesize);
49 static int fastuser_store(PAIR_LIST **hashtable, PAIR_LIST *entry, int idx);
50 static PAIR_LIST *fastuser_find(PAIR_LIST **hashtable, const char *user,
54 * A temporary holding area for config values to be extracted
55 * into, before they are copied into the instance data
57 static struct fastuser_instance config;
59 static CONF_PARSER module_config[] = {
60 { "usersfile", PW_TYPE_STRING_PTR, &config.usersfile, RADIUS_USERS },
61 { "hashsize", PW_TYPE_INTEGER, &config.hashsize, "100000" },
62 { "compat", PW_TYPE_STRING_PTR, &config.compat_mode, "cistron" },
63 { NULL, -1, NULL, NULL }
66 static int fastuser_getfile(const char *filename, PAIR_LIST **hashtable)
69 PAIR_LIST *users = NULL;
70 int compat_mode = FALSE;
71 PAIR_LIST *entry, *next;
75 rcode = pairlist_read(filename, &users, 1);
80 if (strcmp(config.compat_mode, "cistron") == 0) {
87 DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...",
88 filename, entry->lineno, entry->name);
92 * Look for improper use of '=' in the
93 * check items. They should be using
94 * '==' for on-the-wire RADIUS attributes,
95 * and probably ':=' for server
96 * configuration items.
98 for (vp = entry->check; vp != NULL; vp = vp->next) {
100 * Ignore attributes which are set
103 if (vp->operator != T_OP_EQ)
108 * If it's a vendor attribute,
109 * or it's a wire protocol,
110 * ensure it has '=='.
112 if (((vp->attribute & ~0xffff) != 0) ||
113 (vp->attribute < 0x100)) {
115 DEBUG("[%s]:%d WARNING! Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
116 filename, entry->lineno, vp->name, vp->name, entry->name);
118 DEBUG("\tChanging '%s =' to '%s =='",
121 vp->operator = T_OP_CMP_EQ;
126 * Cistron Compatibility mode.
128 * Re-write selected attributes
129 * to be '+=', instead of '='.
131 * All others get set to '=='
135 * Non-wire attributes become +=
137 * On the write attributes
140 if ((vp->attribute >= 0x100) &&
141 (vp->attribute <= 0xffff) &&
142 (vp->attribute != PW_HINT) &&
143 (vp->attribute != PW_HUNTGROUP_NAME)) {
144 DEBUG("\tChanging '%s =' to '%s +='",
146 vp->operator = T_OP_ADD;
148 DEBUG("\tChanging '%s =' to '%s =='",
150 vp->operator = T_OP_CMP_EQ;
154 } /* end of loop over check items */
158 * Look for server configuration items
161 * It's a common enough mistake, that it's
164 for (vp = entry->reply; vp != NULL; vp = vp->next) {
166 * If it's NOT a vendor attribute,
167 * and it's NOT a wire protocol
168 * and we ignore Fall-Through,
169 * then bitch about it, giving a
170 * good warning message.
172 if (!(vp->attribute & ~0xffff) &&
173 (vp->attribute > 0xff) &&
174 (vp->attribute > 1000)) {
175 log_debug("[%s]:%d WARNING! Check item \"%s\"\n"
176 "\tfound in reply item list for user \"%s\".\n"
177 "\tThis attribute MUST go on the first line"
178 " with the other check items",
179 filename, entry->lineno, vp->name,
185 * Ok, we've done all the same BS as
186 * rlm_users, so here we tear apart the
187 * linked list, and store our users in
188 * the hashtable we've built instead
191 /* Save what was next */
194 /* Save the DEFAULT entry specially */
195 if(strcmp(entry->name, "DEFAULT")==0) {
196 config.default_entry = entry;
197 config.default_entry->next = NULL;
201 /* Hash the username */
202 hashindex = fastuser_hash(entry->name, config.hashsize);
204 /* Store user in the hash */
205 fastuser_store(hashtable, entry, hashindex);
207 /* Restore entry to next pair_list */
211 } /* while(entry) loop */
214 * Remove auth-type from the Default
215 * entry because it's not a real
222 /* Hashes the username sent to it and returns index into hashtable */
223 int fastuser_hash(const char *s, long hashtablesize) {
224 unsigned long hash = 0;
227 hash = hash * 7907 + (unsigned char)*s++;
230 return (hash % hashtablesize);
233 /* Stores the username sent into the hashtable */
234 static int fastuser_store(PAIR_LIST **hashtable, PAIR_LIST *new, int idx) {
236 /* store new record at beginning of list */
237 new->next = hashtable[idx];
238 hashtable[idx] = new;
244 * Looks up user in hashtable. If user can't be found, returns 0.
245 * Otherwise returns a pointer to the structure for the user
247 static PAIR_LIST *fastuser_find(PAIR_LIST **hashtable,
248 const char *user, long hashsize)
254 /* first hash the username and get the index into the hashtable */
255 idx = fastuser_hash(user, hashsize);
257 cur = hashtable[idx];
259 while((cur != NULL) && (strcmp(cur->name, user))) {
264 DEBUG2(" HASH: user %s found in hashtable bucket %d", user, idx);
268 return (PAIR_LIST *)0;
274 * (Re-)read the "users" file into memory.
276 static int fastuser_instantiate(CONF_SECTION *conf, void **instance)
278 struct fastuser_instance *inst=0;
282 inst = malloc(sizeof *inst);
284 radlog(L_ERR|L_CONS, "Out of memory\n");
287 memset(inst, 0, sizeof(inst));
289 if (cf_section_parse(conf, module_config) < 0) {
295 * Sue me. The tradeoff for this extra variable
296 * is clean code below
298 memsize = sizeof(PAIR_LIST *) * inst->hashsize;
300 * Allocate space for hash table here
302 if( (inst->hashtable = (PAIR_LIST **)malloc(memsize)) == NULL) {
303 radlog(L_ERR, "fastusers: Can't build hashtable, out of memory!");
306 memset((PAIR_LIST *)inst->hashtable, 0, memsize);
308 rcode = fastuser_getfile(config.usersfile, inst->hashtable);
310 radlog(L_ERR|L_CONS, "Errors reading %s", inst->usersfile);
314 inst->usersfile = config.usersfile;
315 inst->hashsize = config.hashsize;
316 inst->default_entry = config.default_entry;
317 inst->compat_mode = config.compat_mode;
320 config.usersfile = NULL;
321 config.hashtable = NULL;
322 config.default_entry = NULL;
324 config.compat_mode = NULL;
331 * Find the named user in the database. Create the
332 * set of attribute-value pairs to check and reply with
333 * for this user from the database. The main code only
334 * needs to check the password, the rest is done here.
336 static int fastuser_authorize(void *instance, REQUEST *request)
338 VALUE_PAIR *namepair;
339 VALUE_PAIR *request_pairs;
340 VALUE_PAIR *check_tmp;
341 VALUE_PAIR *reply_tmp;
342 VALUE_PAIR **check_pairs;
343 VALUE_PAIR **reply_pairs;
344 VALUE_PAIR *check_save;
348 struct fastuser_instance *inst = instance;
350 request_pairs = request->packet->vps;
351 check_pairs = &request->config_items;
352 reply_pairs = &request->reply->vps;
355 * Grab the canonical user name.
357 namepair = request->username;
358 name = namepair ? (char *) namepair->strvalue : "NONE";
361 * Find the entry for the user.
363 if((user=fastuser_find(inst->hashtable, name, inst->hashsize))==NULL) {
364 return RLM_MODULE_NOTFOUND;
367 if(mainconfig.do_usercollide) {
368 /* Save the orginal config items */
369 check_save = paircopy(request->config_items);
371 while((user) && (!found) && (strcmp(user->name, name)==0)) {
372 DEBUG2(" fastusers: Checking %s at %d", user->name, user->lineno);
374 /* Copy this users check pairs to the request */
375 check_tmp = paircopy(user->check);
376 pairmove(check_pairs, &check_tmp);
379 /* Check the req to see if we matched */
380 if(rad_check_password(request)==0) {
383 /* We didn't match here */
385 /* Restore check items */
386 pairfree(request->config_items);
387 request->config_items = paircopy(check_save);
388 check_pairs = &request->config_items;
393 /* Free our saved config items */
394 pairfree(check_save);
397 return RLM_MODULE_NOTFOUND;
400 DEBUG2(" fastusers: Matched %s at %d", user->name, user->lineno);
402 /* We've already done this above if(mainconfig.do_usercollide) */
403 if(!mainconfig.do_usercollide) {
404 check_tmp = paircopy(user->check);
405 pairmove(check_pairs, &check_tmp);
408 reply_tmp = paircopy(user->reply);
409 pairmove(reply_pairs, &reply_tmp);
413 * We also need to add the pairs from
414 * inst->default_entry if the vp is
415 * not already present.
418 if(inst->default_entry) {
419 check_tmp = paircopy(inst->default_entry->check);
420 reply_tmp = paircopy(inst->default_entry->reply);
421 pairmove(reply_pairs, &reply_tmp);
422 pairmove(check_pairs, &check_tmp);
427 return RLM_MODULE_UPDATED;
431 * Authentication - unused.
433 static int fastuser_authenticate(void *instance, REQUEST *request)
437 return RLM_MODULE_OK;
443 static int fastuser_detach(void *instance)
445 struct fastuser_instance *inst = instance;
450 /* Free hash table */
451 for(hashindex=0; hashindex<inst->hashsize; hashindex++) {
452 if(inst->hashtable[hashindex]) {
453 cur = inst->hashtable[hashindex];
458 free(inst->hashtable);
459 pairlist_free(&inst->users);
460 pairlist_free(&inst->default_entry);
461 free(inst->usersfile);
462 free(inst->compat_mode);
468 * This function is unused
470 static int fastuser_preacct(void *instance, REQUEST *request)
472 return RLM_MODULE_FAIL;
476 * This function is unused
478 static int fastuser_accounting(void *instance, REQUEST *request)
480 return RLM_MODULE_FAIL;
483 /* globally exported name */
484 module_t rlm_fastusers = {
486 0, /* type: reserved */
487 NULL, /* initialization */
488 fastuser_instantiate, /* instantiation */
489 fastuser_authorize, /* authorization */
490 fastuser_authenticate, /* authentication */
491 fastuser_preacct, /* preaccounting */
492 fastuser_accounting, /* accounting */
493 NULL, /* checksimul */
494 fastuser_detach, /* detach */