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 The FreeRADIUS server project
21 * Copyright 2000 Jeff Carneal <jeff@apex.net>
24 static const char rcsid[] = "$Id$";
26 #include <freeradius-devel/autoconf.h>
37 #include <freeradius-devel/radiusd.h>
38 #include <freeradius-devel/modules.h>
40 struct file_instance {
52 char *preproxy_usersfile;
53 PAIR_LIST *preproxy_users;
57 PAIR_LIST *auth_users;
60 char *postproxy_usersfile;
61 PAIR_LIST *postproxy_users;
63 /* post-authenticate */
64 char *postauth_usersfile;
65 PAIR_LIST *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);
76 return tmp ? tmp->lvalue : 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 },
84 { "preproxy_usersfile", PW_TYPE_FILENAME,
85 offsetof(struct file_instance,preproxy_usersfile), NULL, NULL },
86 { "auth_usersfile", PW_TYPE_FILENAME,
87 offsetof(struct file_instance,auth_usersfile), NULL, NULL },
88 { "postproxy_usersfile", PW_TYPE_FILENAME,
89 offsetof(struct file_instance,postproxy_usersfile), NULL, NULL },
90 { "postauth_usersfile", PW_TYPE_FILENAME,
91 offsetof(struct file_instance,postauth_usersfile), NULL, NULL },
92 { "compat", PW_TYPE_STRING_PTR,
93 offsetof(struct file_instance,compat_mode), NULL, "cistron" },
94 { NULL, -1, 0, NULL, NULL }
97 static int getusersfile(const char *filename, PAIR_LIST **pair_list, char *compat_mode_str)
100 PAIR_LIST *users = NULL;
107 rcode = pairlist_read(filename, &users, 1);
113 * Walk through the 'users' file list, if we're debugging,
114 * or if we're in compat_mode.
117 (strcmp(compat_mode_str, "cistron") == 0)) {
120 int compat_mode = FALSE;
122 if (strcmp(compat_mode_str, "cistron") == 0) {
129 DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...",
130 filename, entry->lineno,
135 * Look for improper use of '=' in the
136 * check items. They should be using
137 * '==' for on-the-wire RADIUS attributes,
138 * and probably ':=' for server
139 * configuration items.
141 for (vp = entry->check; vp != NULL; vp = vp->next) {
143 * Ignore attributes which are set
146 if (vp->operator != T_OP_EQ) {
151 * If it's a vendor attribute,
152 * or it's a wire protocol,
153 * ensure it has '=='.
155 if (((vp->attribute & ~0xffff) != 0) ||
156 (vp->attribute < 0x100)) {
158 DEBUG("[%s]:%d WARNING! Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
159 filename, entry->lineno,
163 DEBUG("\tChanging '%s =' to '%s =='",
166 vp->operator = T_OP_CMP_EQ;
171 * Cistron Compatibility mode.
173 * Re-write selected attributes
174 * to be '+=', instead of '='.
176 * All others get set to '=='
180 * Non-wire attributes become +=
182 * On the write attributes
185 if ((vp->attribute >= 0x100) &&
186 (vp->attribute <= 0xffff) &&
187 (vp->attribute != PW_HINT) &&
188 (vp->attribute != PW_HUNTGROUP_NAME)) {
189 DEBUG("\tChanging '%s =' to '%s +='",
191 vp->operator = T_OP_ADD;
193 DEBUG("\tChanging '%s =' to '%s =='",
195 vp->operator = T_OP_CMP_EQ;
199 } /* end of loop over check items */
203 * Look for server configuration items
206 * It's a common enough mistake, that it's
209 for (vp = entry->reply; vp != NULL; vp = vp->next) {
211 * If it's NOT a vendor attribute,
212 * and it's NOT a wire protocol
213 * and we ignore Fall-Through,
214 * then bitch about it, giving a
215 * good warning message.
217 if (!(vp->attribute & ~0xffff) &&
218 (vp->attribute > 0xff) &&
219 (vp->attribute > 1000)) {
220 log_debug("[%s]:%d WARNING! Check item \"%s\"\n"
221 "\tfound in reply item list for user \"%s\".\n"
222 "\tThis attribute MUST go on the first line"
223 " with the other check items",
224 filename, entry->lineno, vp->name,
241 static int file_detach(void *instance)
243 struct file_instance *inst = instance;
244 pairlist_free(&inst->users);
245 pairlist_free(&inst->acctusers);
246 pairlist_free(&inst->preproxy_users);
247 pairlist_free(&inst->auth_users);
248 pairlist_free(&inst->postproxy_users);
249 pairlist_free(&inst->postauth_users);
250 free(inst->usersfile);
251 free(inst->acctusersfile);
252 free(inst->preproxy_usersfile);
253 free(inst->auth_usersfile);
254 free(inst->postproxy_usersfile);
255 free(inst->postauth_usersfile);
256 free(inst->compat_mode);
264 * (Re-)read the "users" file into memory.
266 static int file_instantiate(CONF_SECTION *conf, void **instance)
268 struct file_instance *inst;
271 inst = rad_malloc(sizeof *inst);
275 memset(inst, 0, sizeof(*inst));
277 if (cf_section_parse(conf, inst, module_config) < 0) {
282 rcode = getusersfile(inst->usersfile, &inst->users, inst->compat_mode);
284 radlog(L_ERR|L_CONS, "Errors reading %s", inst->usersfile);
289 rcode = getusersfile(inst->acctusersfile, &inst->acctusers, inst->compat_mode);
291 radlog(L_ERR|L_CONS, "Errors reading %s", inst->acctusersfile);
297 * Get the pre-proxy stuff
299 rcode = getusersfile(inst->preproxy_usersfile, &inst->preproxy_users, inst->compat_mode);
301 radlog(L_ERR|L_CONS, "Errors reading %s", inst->preproxy_usersfile);
306 rcode = getusersfile(inst->auth_usersfile, &inst->auth_users, inst->compat_mode);
308 radlog(L_ERR|L_CONS, "Errors reading %s", inst->auth_usersfile);
313 rcode = getusersfile(inst->postproxy_usersfile, &inst->postproxy_users, inst->compat_mode);
315 radlog(L_ERR|L_CONS, "Errors reading %s", inst->postproxy_usersfile);
320 rcode = getusersfile(inst->postauth_usersfile, &inst->postauth_users, inst->compat_mode);
322 radlog(L_ERR|L_CONS, "Errors reading %s", inst->postauth_usersfile);
332 * Common code called by everything below.
334 static int file_common(struct file_instance *inst, REQUEST *request,
335 const char *filename, const PAIR_LIST *list,
336 VALUE_PAIR *request_pairs, VALUE_PAIR **reply_pairs)
338 VALUE_PAIR *namepair;
340 VALUE_PAIR **config_pairs;
341 VALUE_PAIR *check_tmp;
342 VALUE_PAIR *reply_tmp;
346 inst = inst; /* -Wunused fix later? */
348 namepair = request->username;
349 name = namepair ? (char *) namepair->vp_strvalue : "NONE";
350 config_pairs = &request->config_items;
352 if (!list) return RLM_MODULE_NOOP;
355 * Find the entry for the user.
357 for (pl = list; pl; pl = pl->next) {
358 if (strcmp(name, pl->name) && strcmp(pl->name, "DEFAULT"))
361 if (paircompare(request, request_pairs, pl->check, reply_pairs) == 0) {
362 DEBUG2(" %s: Matched entry %s at line %d",
363 filename, pl->name, pl->lineno);
365 check_tmp = paircopy(pl->check);
366 reply_tmp = paircopy(pl->reply);
367 pairxlatmove(request, reply_pairs, &reply_tmp);
368 pairmove(config_pairs, &check_tmp);
369 pairfree(&reply_tmp);
370 pairfree(&check_tmp); /* should be NULL */
375 if (!fallthrough(pl->reply))
381 * Remove server internal parameters.
383 pairdelete(reply_pairs, PW_FALL_THROUGH);
386 * See if we succeeded.
389 return RLM_MODULE_NOOP; /* on to the next module */
391 return RLM_MODULE_OK;
397 * Find the named user in the database. Create the
398 * set of attribute-value pairs to check and reply with
399 * for this user from the database. The main code only
400 * needs to check the password, the rest is done here.
402 static int file_authorize(void *instance, REQUEST *request)
404 struct file_instance *inst = instance;
406 return file_common(inst, request, "users", inst->users,
407 request->packet->vps, &request->reply->vps);
412 * Pre-Accounting - read the acct_users file for check_items and
413 * config_items. Reply items are Not Recommended(TM) in acct_users,
414 * except for Fallthrough, which should work
416 static int file_preacct(void *instance, REQUEST *request)
418 struct file_instance *inst = instance;
420 return file_common(inst, request, "acct_users", inst->acctusers,
421 request->packet->vps, &request->reply->vps);
424 static int file_preproxy(void *instance, REQUEST *request)
426 struct file_instance *inst = instance;
428 return file_common(inst, request, "preproxy_users",
429 inst->preproxy_users,
430 request->packet->vps, &request->proxy->vps);
433 static int file_postproxy(void *instance, REQUEST *request)
435 struct file_instance *inst = instance;
437 return file_common(inst, request, "postproxy_users",
438 inst->postproxy_users,
439 request->proxy_reply->vps, &request->reply->vps);
442 static int file_authenticate(void *instance, REQUEST *request)
444 struct file_instance *inst = instance;
446 return file_common(inst, request, "auth_users",
448 request->packet->vps, &request->reply->vps);
451 static int file_postauth(void *instance, REQUEST *request)
453 struct file_instance *inst = instance;
455 return file_common(inst, request, "postauth_users",
456 inst->postauth_users,
457 request->packet->vps, &request->reply->vps);
461 /* globally exported name */
462 module_t rlm_files = {
465 0, /* type: reserved */
466 file_instantiate, /* instantiation */
467 file_detach, /* detach */
469 file_authenticate, /* authentication */
470 file_authorize, /* authorization */
471 file_preacct, /* preaccounting */
472 NULL, /* accounting */
473 NULL, /* checksimul */
474 file_preproxy, /* pre-proxy */
475 file_postproxy, /* post-proxy */
476 file_postauth /* post-auth */