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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 * See if a VALUE_PAIR list contains Fall-Through = Yes
59 static int fallthrough(VALUE_PAIR *vp)
62 tmp = pairfind(vp, PW_FALL_THROUGH);
64 return tmp ? tmp->lvalue : 0;
67 static const CONF_PARSER module_config[] = {
68 { "usersfile", PW_TYPE_FILENAME,
69 offsetof(struct file_instance,usersfile), NULL, "${raddbdir}/users" },
70 { "acctusersfile", PW_TYPE_FILENAME,
71 offsetof(struct file_instance,acctusersfile), NULL, "${raddbdir}/acct_users" },
72 { "preproxy_usersfile", PW_TYPE_FILENAME,
73 offsetof(struct file_instance,preproxy_usersfile), NULL, "${raddbdir}/preproxy_users" },
74 { "compat", PW_TYPE_STRING_PTR,
75 offsetof(struct file_instance,compat_mode), NULL, "cistron" },
76 { NULL, -1, 0, NULL, NULL }
79 static int getusersfile(const char *filename, PAIR_LIST **pair_list, char *compat_mode_str)
82 PAIR_LIST *users = NULL;
84 rcode = pairlist_read(filename, &users, 1);
90 * Walk through the 'users' file list, if we're debugging,
91 * or if we're in compat_mode.
94 (strcmp(compat_mode_str, "cistron") == 0)) {
97 int compat_mode = FALSE;
99 if (strcmp(compat_mode_str, "cistron") == 0) {
106 DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...",
107 filename, entry->lineno,
112 * Look for improper use of '=' in the
113 * check items. They should be using
114 * '==' for on-the-wire RADIUS attributes,
115 * and probably ':=' for server
116 * configuration items.
118 for (vp = entry->check; vp != NULL; vp = vp->next) {
120 * Ignore attributes which are set
123 if (vp->operator != T_OP_EQ) {
128 * If it's a vendor attribute,
129 * or it's a wire protocol,
130 * ensure it has '=='.
132 if (((vp->attribute & ~0xffff) != 0) ||
133 (vp->attribute < 0x100)) {
135 DEBUG("[%s]:%d WARNING! Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
136 filename, entry->lineno,
140 DEBUG("\tChanging '%s =' to '%s =='",
143 vp->operator = T_OP_CMP_EQ;
148 * Cistron Compatibility mode.
150 * Re-write selected attributes
151 * to be '+=', instead of '='.
153 * All others get set to '=='
157 * Non-wire attributes become +=
159 * On the write attributes
162 if ((vp->attribute >= 0x100) &&
163 (vp->attribute <= 0xffff) &&
164 (vp->attribute != PW_HINT) &&
165 (vp->attribute != PW_HUNTGROUP_NAME)) {
166 DEBUG("\tChanging '%s =' to '%s +='",
168 vp->operator = T_OP_ADD;
170 DEBUG("\tChanging '%s =' to '%s =='",
172 vp->operator = T_OP_CMP_EQ;
176 } /* end of loop over check items */
180 * Look for server configuration items
183 * It's a common enough mistake, that it's
186 for (vp = entry->reply; vp != NULL; vp = vp->next) {
188 * If it's NOT a vendor attribute,
189 * and it's NOT a wire protocol
190 * and we ignore Fall-Through,
191 * then bitch about it, giving a
192 * good warning message.
194 if (!(vp->attribute & ~0xffff) &&
195 (vp->attribute > 0xff) &&
196 (vp->attribute > 1000)) {
197 log_debug("[%s]:%d WARNING! Check item \"%s\"\n"
198 "\tfound in reply item list for user \"%s\".\n"
199 "\tThis attribute MUST go on the first line"
200 " with the other check items",
201 filename, entry->lineno, vp->name,
216 * (Re-)read the "users" file into memory.
218 static int file_instantiate(CONF_SECTION *conf, void **instance)
220 struct file_instance *inst;
223 inst = rad_malloc(sizeof *inst);
227 memset(inst, 0, sizeof(*inst));
229 if (cf_section_parse(conf, inst, module_config) < 0) {
234 rcode = getusersfile(inst->usersfile, &inst->users, inst->compat_mode);
236 radlog(L_ERR|L_CONS, "Errors reading %s", inst->usersfile);
237 free(inst->usersfile);
238 free(inst->acctusersfile);
243 rcode = getusersfile(inst->acctusersfile, &inst->acctusers, inst->compat_mode);
245 radlog(L_ERR|L_CONS, "Errors reading %s", inst->acctusersfile);
246 pairlist_free(&inst->users);
247 free(inst->usersfile);
248 free(inst->acctusersfile);
254 * Get the pre-proxy stuff
256 rcode = getusersfile(inst->preproxy_usersfile, &inst->preproxy_users, inst->compat_mode);
258 radlog(L_ERR|L_CONS, "Errors reading %s", inst->preproxy_usersfile);
259 pairlist_free(&inst->users);
260 pairlist_free(&inst->acctusers);
261 free(inst->usersfile);
262 free(inst->acctusersfile);
263 free(inst->preproxy_usersfile);
273 * Find the named user in the database. Create the
274 * set of attribute-value pairs to check and reply with
275 * for this user from the database. The main code only
276 * needs to check the password, the rest is done here.
278 static int file_authorize(void *instance, REQUEST *request)
280 VALUE_PAIR *namepair;
281 VALUE_PAIR *request_pairs;
282 VALUE_PAIR *check_tmp;
283 VALUE_PAIR *reply_tmp;
287 struct file_instance *inst = instance;
288 VALUE_PAIR **check_pairs, **reply_pairs;
290 request_pairs = request->packet->vps;
291 check_pairs = &request->config_items;
292 reply_pairs = &request->reply->vps;
295 * Grab the canonical user name.
297 namepair = request->username;
298 name = namepair ? (char *) namepair->vp_strvalue : "NONE";
301 * Find the entry for the user.
303 for(pl = inst->users; pl; pl = pl->next) {
305 * If the current entry is NOT a default,
306 * AND the name does NOT match the current entry,
307 * then skip to the next entry.
309 if ((strcmp(pl->name, "DEFAULT") != 0) &&
310 (strcmp(name, pl->name) != 0)) {
315 * If the current request matches against the
316 * check pairs, then add the reply pairs from the
317 * entry to the current list of reply pairs.
319 if ((paircompare(request, request_pairs, pl->check, reply_pairs) == 0)) {
320 DEBUG2(" users: Matched entry %s at line %d", pl->name, pl->lineno);
322 check_tmp = paircopy(pl->check);
323 reply_tmp = paircopy(pl->reply);
324 pairxlatmove(request, reply_pairs, &reply_tmp);
325 pairmove(check_pairs, &check_tmp);
326 pairfree(&reply_tmp);
327 pairfree(&check_tmp); /* should be NULL */
332 if (!fallthrough(pl->reply))
338 * See if we succeeded. If we didn't find the user,
339 * then exit from the module.
342 return RLM_MODULE_NOTFOUND;
345 * Remove server internal parameters.
347 pairdelete(reply_pairs, PW_FALL_THROUGH);
349 return RLM_MODULE_OK;
353 * Pre-Accounting - read the acct_users file for check_items and
354 * config_items. Reply items are Not Recommended(TM) in acct_users,
355 * except for Fallthrough, which should work
357 * This function is mostly a copy of file_authorize
359 static int file_preacct(void *instance, REQUEST *request)
361 VALUE_PAIR *namepair;
363 VALUE_PAIR *request_pairs;
364 VALUE_PAIR **config_pairs;
365 VALUE_PAIR **reply_pairs;
366 VALUE_PAIR *check_tmp;
367 VALUE_PAIR *reply_tmp;
370 struct file_instance *inst = instance;
372 namepair = request->username;
373 name = namepair ? (char *) namepair->vp_strvalue : "NONE";
374 request_pairs = request->packet->vps;
375 config_pairs = &request->config_items;
376 reply_pairs = &request->reply->vps;
379 * Find the entry for the user.
381 for (pl = inst->acctusers; pl; pl = pl->next) {
383 if (strcmp(name, pl->name) && strcmp(pl->name, "DEFAULT"))
386 if (paircompare(request, request_pairs, pl->check, reply_pairs) == 0) {
387 DEBUG2(" acct_users: Matched entry %s at line %d",
388 pl->name, pl->lineno);
390 check_tmp = paircopy(pl->check);
391 reply_tmp = paircopy(pl->reply);
392 pairxlatmove(request, reply_pairs, &reply_tmp);
393 pairmove(config_pairs, &check_tmp);
394 pairfree(&reply_tmp);
395 pairfree(&check_tmp); /* should be NULL */
399 if (!fallthrough(pl->reply))
405 * See if we succeeded.
408 return RLM_MODULE_NOOP; /* on to the next module */
410 return RLM_MODULE_OK;
414 * Pre-proxy - read the preproxy_users file for check_items and
417 * This function is mostly a copy of file_authorize
419 static int file_preproxy(void *instance, REQUEST *request)
421 VALUE_PAIR *namepair;
423 VALUE_PAIR *request_pairs;
424 VALUE_PAIR **config_pairs;
425 VALUE_PAIR **reply_pairs;
426 VALUE_PAIR *check_tmp;
427 VALUE_PAIR *reply_tmp;
430 struct file_instance *inst = instance;
432 namepair = request->username;
433 name = namepair ? (char *) namepair->vp_strvalue : "NONE";
434 request_pairs = request->packet->vps;
435 config_pairs = &request->config_items;
436 reply_pairs = &request->proxy->vps;
439 * Find the entry for the user.
441 for (pl = inst->preproxy_users; pl; pl = pl->next) {
446 if (strcmp(name, pl->name) && strcmp(pl->name, "DEFAULT"))
449 if (paircompare(request, request_pairs, pl->check, reply_pairs) == 0) {
452 DEBUG2(" preproxy_users: Matched entry %s at line %d",
453 pl->name, pl->lineno);
455 check_tmp = paircopy(pl->check);
456 reply_tmp = paircopy(pl->reply);
458 for (vp = reply_tmp; vp != NULL; vp = vp->next) {
460 * We've got to xlat the string
461 * before moving it over.
463 if (vp->flags.do_xlat) {
465 char buffer[sizeof(vp->vp_strvalue)];
467 vp->flags.do_xlat = 0;
468 rcode = radius_xlat(buffer, sizeof(buffer),
473 * Parse the string into
476 pairparsevalue(vp, buffer);
478 } /* loop over the things to add to the reply */
480 pairxlatmove(request, reply_pairs, &reply_tmp);
481 pairmove(config_pairs, &check_tmp);
482 pairfree(&reply_tmp);
483 pairfree(&check_tmp); /* should be NULL */
487 if (!fallthrough(pl->reply))
493 * See if we succeeded.
496 return RLM_MODULE_NOOP; /* on to the next module */
498 return RLM_MODULE_OK;
504 static int file_detach(void *instance)
506 struct file_instance *inst = instance;
507 pairlist_free(&inst->users);
508 pairlist_free(&inst->acctusers);
509 pairlist_free(&inst->preproxy_users);
510 free(inst->usersfile);
511 free(inst->acctusersfile);
512 free(inst->preproxy_usersfile);
513 free(inst->compat_mode);
519 /* globally exported name */
520 module_t rlm_files = {
523 0, /* type: reserved */
524 file_instantiate, /* instantiation */
525 file_detach, /* detach */
527 NULL, /* authentication */
528 file_authorize, /* authorization */
529 file_preacct, /* preaccounting */
530 NULL, /* accounting */
531 NULL, /* checksimul */
532 file_preproxy, /* pre-proxy */
533 NULL, /* post-proxy */