2 * rlm_files.c authorization: Find a user in the "users" file.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2002,2006 The FreeRADIUS server project
21 * Copyright 2000 Jeff Carneal <jeff@apex.net>
24 #include <freeradius-devel/ident.h>
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
34 struct file_instance {
41 fr_hash_table_t *users;
46 fr_hash_table_t *auth_users;
50 fr_hash_table_t *acctusers;
54 char *preproxy_usersfile;
55 fr_hash_table_t *preproxy_users;
58 char *postproxy_usersfile;
59 fr_hash_table_t *postproxy_users;
62 /* post-authenticate */
63 char *postauth_usersfile;
64 fr_hash_table_t *postauth_users;
69 * See if a VALUE_PAIR list contains Fall-Through = Yes
71 static int fallthrough(VALUE_PAIR *vp)
74 tmp = pairfind(vp, PW_FALL_THROUGH, 0);
76 return tmp ? tmp->vp_integer : 0;
79 static const CONF_PARSER module_config[] = {
80 { "usersfile", PW_TYPE_FILENAME,
81 offsetof(struct file_instance,usersfile), NULL, NULL },
82 { "acctusersfile", PW_TYPE_FILENAME,
83 offsetof(struct file_instance,acctusersfile), NULL, NULL },
85 { "preproxy_usersfile", PW_TYPE_FILENAME,
86 offsetof(struct file_instance,preproxy_usersfile), NULL, NULL },
87 { "postproxy_usersfile", PW_TYPE_FILENAME,
88 offsetof(struct file_instance,postproxy_usersfile), NULL, NULL },
90 { "auth_usersfile", PW_TYPE_FILENAME,
91 offsetof(struct file_instance,auth_usersfile), NULL, NULL },
92 { "postauth_usersfile", PW_TYPE_FILENAME,
93 offsetof(struct file_instance,postauth_usersfile), NULL, NULL },
94 { "compat", PW_TYPE_STRING_PTR,
95 offsetof(struct file_instance,compat_mode), NULL, "cistron" },
96 { "key", PW_TYPE_STRING_PTR,
97 offsetof(struct file_instance,key), NULL, NULL },
98 { NULL, -1, 0, NULL, NULL }
102 static uint32_t pairlist_hash(const void *data)
104 return fr_hash_string(((const PAIR_LIST *)data)->name);
107 static int pairlist_cmp(const void *a, const void *b)
109 return strcmp(((const PAIR_LIST *)a)->name,
110 ((const PAIR_LIST *)b)->name);
113 static void my_pairlist_free(void *data)
115 PAIR_LIST *pl = data;
121 static int getusersfile(const char *filename, fr_hash_table_t **pht,
122 char *compat_mode_str)
125 PAIR_LIST *users = NULL;
126 PAIR_LIST *entry, *next;
127 fr_hash_table_t *ht, *tailht;
135 rcode = pairlist_read(filename, &users, 1);
141 * Walk through the 'users' file list, if we're debugging,
142 * or if we're in compat_mode.
145 (strcmp(compat_mode_str, "cistron") == 0)) {
147 int compat_mode = FALSE;
149 if (strcmp(compat_mode_str, "cistron") == 0) {
156 DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...",
157 filename, entry->lineno,
162 * Look for improper use of '=' in the
163 * check items. They should be using
164 * '==' for on-the-wire RADIUS attributes,
165 * and probably ':=' for server
166 * configuration items.
168 for (vp = entry->check; vp != NULL; vp = vp->next) {
170 * Ignore attributes which are set
173 if (vp->operator != T_OP_EQ) {
178 * If it's a vendor attribute,
179 * or it's a wire protocol,
180 * ensure it has '=='.
182 if ((vp->vendor != 0) ||
183 (vp->attribute < 0x100)) {
185 DEBUG("[%s]:%d WARNING! Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
186 filename, entry->lineno,
190 DEBUG("\tChanging '%s =' to '%s =='",
193 vp->operator = T_OP_CMP_EQ;
198 * Cistron Compatibility mode.
200 * Re-write selected attributes
201 * to be '+=', instead of '='.
203 * All others get set to '=='
207 * Non-wire attributes become +=
209 * On the write attributes
212 if ((vp->attribute >= 0x100) &&
213 (vp->attribute <= 0xffff) &&
214 (vp->attribute != PW_HINT) &&
215 (vp->attribute != PW_HUNTGROUP_NAME)) {
216 DEBUG("\tChanging '%s =' to '%s +='",
218 vp->operator = T_OP_ADD;
220 DEBUG("\tChanging '%s =' to '%s =='",
222 vp->operator = T_OP_CMP_EQ;
226 } /* end of loop over check items */
229 * Look for server configuration items
232 * It's a common enough mistake, that it's
235 for (vp = entry->reply; vp != NULL; vp = vp->next) {
237 * If it's NOT a vendor attribute,
238 * and it's NOT a wire protocol
239 * and we ignore Fall-Through,
240 * then bitch about it, giving a
241 * good warning message.
243 if ((vp->vendor == 0) &&
244 (vp->attribute > 0xff) &&
245 (vp->attribute > 1000)) {
246 log_debug("[%s]:%d WARNING! Check item \"%s\"\n"
247 "\tfound in reply item list for user \"%s\".\n"
248 "\tThis attribute MUST go on the first line"
249 " with the other check items",
250 filename, entry->lineno, vp->name,
260 ht = fr_hash_table_create(pairlist_hash, pairlist_cmp,
263 pairlist_free(&users);
267 tailht = fr_hash_table_create(pairlist_hash, pairlist_cmp,
270 fr_hash_table_free(ht);
271 pairlist_free(&users);
276 * Now that we've read it in, put the entries into a hash
279 for (entry = users; entry != NULL; entry = next) {
284 entry->order = order++;
287 * Insert it into the hash table, and remember
288 * the tail of the linked list.
290 tail = fr_hash_table_finddata(tailht, entry);
293 * Insert it into the head & tail.
295 if (!fr_hash_table_insert(ht, entry) ||
296 !fr_hash_table_insert(tailht, entry)) {
297 pairlist_free(&next);
298 fr_hash_table_free(ht);
299 fr_hash_table_free(tailht);
304 if (!fr_hash_table_replace(tailht, entry)) {
305 pairlist_free(&next);
306 fr_hash_table_free(ht);
307 fr_hash_table_free(tailht);
313 fr_hash_table_free(tailht);
322 static int file_detach(void *instance)
324 struct file_instance *inst = instance;
325 fr_hash_table_free(inst->users);
326 fr_hash_table_free(inst->acctusers);
328 fr_hash_table_free(inst->preproxy_users);
329 fr_hash_table_free(inst->postproxy_users);
331 fr_hash_table_free(inst->auth_users);
332 fr_hash_table_free(inst->postauth_users);
340 * (Re-)read the "users" file into memory.
342 static int file_instantiate(CONF_SECTION *conf, void **instance)
344 struct file_instance *inst;
347 inst = rad_malloc(sizeof *inst);
351 memset(inst, 0, sizeof(*inst));
353 if (cf_section_parse(conf, inst, module_config) < 0) {
358 rcode = getusersfile(inst->usersfile, &inst->users, inst->compat_mode);
360 radlog(L_ERR|L_CONS, "Errors reading %s", inst->usersfile);
365 rcode = getusersfile(inst->acctusersfile, &inst->acctusers, inst->compat_mode);
367 radlog(L_ERR|L_CONS, "Errors reading %s", inst->acctusersfile);
374 * Get the pre-proxy stuff
376 rcode = getusersfile(inst->preproxy_usersfile, &inst->preproxy_users, inst->compat_mode);
378 radlog(L_ERR|L_CONS, "Errors reading %s", inst->preproxy_usersfile);
383 rcode = getusersfile(inst->postproxy_usersfile, &inst->postproxy_users, inst->compat_mode);
385 radlog(L_ERR|L_CONS, "Errors reading %s", inst->postproxy_usersfile);
391 rcode = getusersfile(inst->auth_usersfile, &inst->auth_users, inst->compat_mode);
393 radlog(L_ERR|L_CONS, "Errors reading %s", inst->auth_usersfile);
398 rcode = getusersfile(inst->postauth_usersfile, &inst->postauth_users, inst->compat_mode);
400 radlog(L_ERR|L_CONS, "Errors reading %s", inst->postauth_usersfile);
410 * Common code called by everything below.
412 static int file_common(struct file_instance *inst, REQUEST *request,
413 const char *filename, fr_hash_table_t *ht,
414 VALUE_PAIR *request_pairs, VALUE_PAIR **reply_pairs)
416 const char *name, *match;
417 VALUE_PAIR **config_pairs;
418 VALUE_PAIR *check_tmp;
419 VALUE_PAIR *reply_tmp;
420 const PAIR_LIST *user_pl, *default_pl;
426 VALUE_PAIR *namepair;
428 namepair = request->username;
429 name = namepair ? (char *) namepair->vp_strvalue : "NONE";
433 len = radius_xlat(buffer, sizeof(buffer), inst->key,
435 if (len) name = buffer;
439 config_pairs = &request->config_items;
441 if (!ht) return RLM_MODULE_NOOP;
444 user_pl = fr_hash_table_finddata(ht, &my_pl);
445 my_pl.name = "DEFAULT";
446 default_pl = fr_hash_table_finddata(ht, &my_pl);
449 * Find the entry for the user.
451 while (user_pl || default_pl) {
454 if (!default_pl && user_pl) {
457 user_pl = user_pl->next;
459 } else if (!user_pl && default_pl) {
462 default_pl = default_pl->next;
464 } else if (user_pl->order < default_pl->order) {
467 user_pl = user_pl->next;
472 default_pl = default_pl->next;
475 if (paircompare(request, request_pairs, pl->check, reply_pairs) == 0) {
476 RDEBUG2("%s: Matched entry %s at line %d",
477 filename, match, pl->lineno);
479 check_tmp = paircopy(pl->check);
480 reply_tmp = paircopy(pl->reply);
481 pairxlatmove(request, reply_pairs, &reply_tmp);
482 pairmove(config_pairs, &check_tmp);
483 pairfree(&reply_tmp);
484 pairfree(&check_tmp);
489 if (!fallthrough(pl->reply))
495 * Remove server internal parameters.
497 pairdelete(reply_pairs, PW_FALL_THROUGH, 0);
500 * See if we succeeded.
503 return RLM_MODULE_NOOP; /* on to the next module */
505 return RLM_MODULE_OK;
511 * Find the named user in the database. Create the
512 * set of attribute-value pairs to check and reply with
513 * for this user from the database. The main code only
514 * needs to check the password, the rest is done here.
516 static int file_authorize(void *instance, REQUEST *request)
518 struct file_instance *inst = instance;
520 return file_common(inst, request, "users", inst->users,
521 request->packet->vps, &request->reply->vps);
526 * Pre-Accounting - read the acct_users file for check_items and
527 * config_items. Reply items are Not Recommended(TM) in acct_users,
528 * except for Fallthrough, which should work
530 static int file_preacct(void *instance, REQUEST *request)
532 struct file_instance *inst = instance;
534 return file_common(inst, request, "acct_users", inst->acctusers,
535 request->packet->vps, &request->reply->vps);
539 static int file_preproxy(void *instance, REQUEST *request)
541 struct file_instance *inst = instance;
543 return file_common(inst, request, "preproxy_users",
544 inst->preproxy_users,
545 request->packet->vps, &request->proxy->vps);
548 static int file_postproxy(void *instance, REQUEST *request)
550 struct file_instance *inst = instance;
552 return file_common(inst, request, "postproxy_users",
553 inst->postproxy_users,
554 request->proxy_reply->vps, &request->reply->vps);
558 static int file_authenticate(void *instance, REQUEST *request)
560 struct file_instance *inst = instance;
562 return file_common(inst, request, "auth_users",
564 request->packet->vps, &request->reply->vps);
567 static int file_postauth(void *instance, REQUEST *request)
569 struct file_instance *inst = instance;
571 return file_common(inst, request, "postauth_users",
572 inst->postauth_users,
573 request->packet->vps, &request->reply->vps);
577 /* globally exported name */
578 module_t rlm_files = {
581 RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,
582 file_instantiate, /* instantiation */
583 file_detach, /* detach */
585 file_authenticate, /* authentication */
586 file_authorize, /* authorization */
587 file_preacct, /* preaccounting */
588 NULL, /* accounting */
589 NULL, /* checksimul */
591 file_preproxy, /* pre-proxy */
592 file_postproxy, /* post-proxy */
596 file_postauth /* post-auth */