2 * rlm_files.c authorization: Find a user in the "users" file.
3 * accounting: Write the "detail" files.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * Copyright 2000 The FreeRADIUS server project
22 * Copyright 2000 Jeff Carneal <jeff@apex.net>
25 static const char rcsid[] = "$Id$";
28 #include "libradius.h"
46 struct file_instance {
59 * See if a VALUE_PAIR list contains Fall-Through = Yes
61 static int fallthrough(VALUE_PAIR *vp)
65 tmp = pairfind(vp, PW_FALL_THROUGH);
67 return tmp ? tmp->lvalue : 0;
71 * A temporary holding area for config values to be extracted
72 * into, before they are copied into the instance data
74 static struct file_instance config;
76 static CONF_PARSER module_config[] = {
77 { "usersfile", PW_TYPE_STRING_PTR, &config.usersfile, "${raddbdir}/users" },
78 { "acctusersfile", PW_TYPE_STRING_PTR, &config.acctusersfile, "${raddbdir}/acct_users" },
79 { "compat", PW_TYPE_STRING_PTR, &config.compat_mode, "cistron" },
80 { NULL, -1, NULL, NULL }
83 static int getusersfile(const char *filename, PAIR_LIST **pair_list)
86 PAIR_LIST *users = NULL;
88 rcode = pairlist_read(filename, &users, 1);
94 * Walk through the 'users' file list, if we're debugging,
95 * or if we're in compat_mode.
98 (strcmp(config.compat_mode, "cistron") == 0)) {
101 int compat_mode = FALSE;
103 if (strcmp(config.compat_mode, "cistron") == 0) {
110 DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...",
111 filename, entry->lineno,
116 * Look for improper use of '=' in the
117 * check items. They should be using
118 * '==' for on-the-wire RADIUS attributes,
119 * and probably ':=' for server
120 * configuration items.
122 for (vp = entry->check; vp != NULL; vp = vp->next) {
124 * Ignore attributes which are set
127 if (vp->operator != T_OP_EQ) {
132 * If it's a vendor attribute,
133 * or it's a wire protocol,
134 * ensure it has '=='.
136 if (((vp->attribute & ~0xffff) != 0) ||
137 (vp->attribute < 0x100)) {
139 DEBUG("[%s]:%d WARNING! Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
140 filename, entry->lineno,
144 DEBUG("\tChanging '%s =' to '%s =='",
147 vp->operator = T_OP_CMP_EQ;
152 * Cistron Compatibility mode.
154 * Re-write selected attributes
155 * to be '+=', instead of '='.
157 * All others get set to '=='
161 * Non-wire attributes become +=
163 * On the write attributes
166 if ((vp->attribute >= 0x100) &&
167 (vp->attribute <= 0xffff) &&
168 (vp->attribute != PW_HINT) &&
169 (vp->attribute != PW_HUNTGROUP_NAME)) {
170 DEBUG("\tChanging '%s =' to '%s +='",
172 vp->operator = T_OP_ADD;
174 DEBUG("\tChanging '%s =' to '%s =='",
176 vp->operator = T_OP_CMP_EQ;
180 } /* end of loop over check items */
184 * Look for server configuration items
187 * It's a common enough mistake, that it's
190 for (vp = entry->reply; vp != NULL; vp = vp->next) {
192 * If it's NOT a vendor attribute,
193 * and it's NOT a wire protocol
194 * and we ignore Fall-Through,
195 * then bitch about it, giving a
196 * good warning message.
198 if (!(vp->attribute & ~0xffff) &&
199 (vp->attribute > 0xff) &&
200 (vp->attribute > 1000)) {
201 log_debug("[%s]:%d WARNING! Check item \"%s\"\n"
202 "\tfound in reply item list for user \"%s\".\n"
203 "\tThis attribute MUST go on the first line"
204 " with the other check items",
205 filename, entry->lineno, vp->name,
220 * (Re-)read the "users" file into memory.
222 static int file_instantiate(CONF_SECTION *conf, void **instance)
224 struct file_instance *inst;
227 inst = malloc(sizeof *inst);
229 radlog(L_ERR|L_CONS, "Out of memory\n");
233 if (cf_section_parse(conf, module_config) < 0) {
238 inst->usersfile = config.usersfile;
239 inst->acctusersfile = config.acctusersfile;
240 inst->compat_mode = config.compat_mode;
241 config.usersfile = NULL;
242 config.acctusersfile = NULL;
244 rcode = getusersfile(inst->usersfile, &inst->users);
246 radlog(L_ERR|L_CONS, "Errors reading %s", inst->usersfile);
247 free(inst->usersfile);
248 free(inst->acctusersfile);
253 rcode = getusersfile(inst->acctusersfile, &inst->acctusers);
255 radlog(L_ERR|L_CONS, "Errors reading %s", inst->acctusersfile);
256 pairlist_free(&inst->users);
257 free(inst->usersfile);
258 free(inst->acctusersfile);
263 config.compat_mode = NULL;
270 * Find the named user in the database. Create the
271 * set of attribute-value pairs to check and reply with
272 * for this user from the database. The main code only
273 * needs to check the password, the rest is done here.
275 static int file_authorize(void *instance, REQUEST *request)
277 VALUE_PAIR *namepair;
278 VALUE_PAIR *request_pairs;
279 VALUE_PAIR *check_tmp;
280 VALUE_PAIR *reply_tmp;
284 struct file_instance *inst = instance;
285 VALUE_PAIR **check_pairs, **reply_pairs;
286 VALUE_PAIR *check_save;
288 request_pairs = request->packet->vps;
289 check_pairs = &request->config_items;
290 reply_pairs = &request->reply->vps;
293 * Grab the canonical user name.
295 namepair = request->username;
296 name = namepair ? (char *) namepair->strvalue : "NONE";
299 * Find the entry for the user.
301 for(pl = inst->users; pl; pl = pl->next) {
303 * If the current entry is NOT a default,
304 * AND the name does NOT match the current entry,
305 * then skip to the next entry.
307 if ((strcmp(pl->name, "DEFAULT") != 0) &&
308 (strcmp(name, pl->name) != 0)) {
313 * If the current request matches against the
314 * check pairs, then add the reply pairs from the
315 * entry to the current list of reply pairs.
317 if ((paircmp(request_pairs, pl->check, reply_pairs) == 0)) {
319 if((mainconfig.do_usercollide) && (strcmp(pl->name, "DEFAULT"))) {
322 * We have to make sure the password
326 /* Save the orginal config items */
327 check_save = paircopy(request->config_items);
329 /* Copy this users check pairs to the request */
330 check_tmp = paircopy(pl->check);
331 pairmove(check_pairs, &check_tmp);
332 pairfree(&check_tmp);
334 DEBUG2(" users: Checking %s at %d", pl->name, pl->lineno);
335 /* Check the req to see if we matched */
336 if (rad_check_password(request)==0) {
337 DEBUG2(" users: Matched %s at %d", pl->name, pl->lineno);
341 /* Free our saved config items */
342 pairfree(&check_save);
345 * Already copied check items, so
346 * just copy reply here
348 reply_tmp = paircopy(pl->reply);
349 pairmove(reply_pairs, &reply_tmp);
350 pairfree(&reply_tmp);
352 /* We didn't match here */
354 /* Restore check items */
355 pairfree(&request->config_items);
356 request->config_items = paircopy(check_save);
357 check_pairs = &request->config_items;
364 DEBUG2(" users: Matched %s at %d", pl->name, pl->lineno);
366 check_tmp = paircopy(pl->check);
367 reply_tmp = paircopy(pl->reply);
368 pairmove(reply_pairs, &reply_tmp);
369 pairmove(check_pairs, &check_tmp);
370 pairfree(&reply_tmp);
371 pairfree(&check_tmp); /* should be NULL */
376 if (!fallthrough(pl->reply))
382 * See if we succeeded. If we didn't find the user,
383 * then exit from the module.
386 return RLM_MODULE_NOTFOUND;
389 * Remove server internal parameters.
391 pairdelete(reply_pairs, PW_FALL_THROUGH);
393 return RLM_MODULE_OK;
397 * Pre-Accounting - read the acct_users file for check_items and
398 * config_items. Reply items are Not Recommended(TM) in acct_users,
399 * except for Fallthrough, which should work
401 * This function is mostly a copy of file_authorize
403 static int file_preacct(void *instance, REQUEST *request)
405 VALUE_PAIR *namepair;
407 VALUE_PAIR *request_pairs;
408 VALUE_PAIR **config_pairs;
409 VALUE_PAIR *reply_pairs = NULL;
410 VALUE_PAIR *check_tmp;
411 VALUE_PAIR *reply_tmp;
414 struct file_instance *inst = instance;
416 namepair = request->username;
417 name = namepair ? (char *) namepair->strvalue : "NONE";
418 request_pairs = request->packet->vps;
419 config_pairs = &request->config_items;
422 * Find the entry for the user.
424 for (pl = inst->acctusers; pl; pl = pl->next) {
426 if (strcmp(name, pl->name) && strcmp(pl->name, "DEFAULT"))
429 if (paircmp(request_pairs, pl->check, &reply_pairs) == 0) {
430 DEBUG2(" acct_users: Matched %s at %d",
431 pl->name, pl->lineno);
433 check_tmp = paircopy(pl->check);
434 reply_tmp = paircopy(pl->reply);
435 pairmove(&reply_pairs, &reply_tmp);
436 pairmove(config_pairs, &check_tmp);
437 pairfree(&reply_tmp);
438 pairfree(&check_tmp); /* should be NULL */
442 if (!fallthrough(pl->reply))
448 * See if we succeeded.
451 return RLM_MODULE_NOOP; /* on to the next module */
454 * FIXME: log a warning if there are any reply items other than
457 pairfree(&reply_pairs); /* Don't need these */
459 return RLM_MODULE_OK;
465 static int file_detach(void *instance)
467 struct file_instance *inst = instance;
468 pairlist_free(&inst->users);
469 pairlist_free(&inst->acctusers);
470 free(inst->usersfile);
471 free(inst->acctusersfile);
472 free(inst->compat_mode);
478 /* globally exported name */
479 module_t rlm_files = {
481 0, /* type: reserved */
482 NULL, /* initialization */
483 file_instantiate, /* instantiation */
484 file_authorize, /* authorization */
485 NULL, /* authentication */
486 file_preacct, /* preaccounting */
487 NULL, /* accounting */
488 NULL, /* checksimul */
489 file_detach, /* detach */