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"
42 struct file_instance {
55 * See if a VALUE_PAIR list contains Fall-Through = Yes
57 static int fallthrough(VALUE_PAIR *vp)
61 tmp = pairfind(vp, PW_FALL_THROUGH);
63 return tmp ? tmp->lvalue : 0;
66 static CONF_PARSER module_config[] = {
67 { "usersfile", PW_TYPE_STRING_PTR,
68 offsetof(struct file_instance,usersfile), NULL, "${raddbdir}/users" },
69 { "acctusersfile", PW_TYPE_STRING_PTR,
70 offsetof(struct file_instance,acctusersfile), NULL, "${raddbdir}/acct_users" },
71 { "compat", PW_TYPE_STRING_PTR,
72 offsetof(struct file_instance,compat_mode), NULL, "cistron" },
73 { NULL, -1, 0, NULL, NULL }
76 static int getusersfile(const char *filename, PAIR_LIST **pair_list, char *compat_mode_str)
79 PAIR_LIST *users = NULL;
81 rcode = pairlist_read(filename, &users, 1);
87 * Walk through the 'users' file list, if we're debugging,
88 * or if we're in compat_mode.
91 (strcmp(compat_mode_str, "cistron") == 0)) {
94 int compat_mode = FALSE;
96 if (strcmp(compat_mode_str, "cistron") == 0) {
103 DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...",
104 filename, entry->lineno,
109 * Look for improper use of '=' in the
110 * check items. They should be using
111 * '==' for on-the-wire RADIUS attributes,
112 * and probably ':=' for server
113 * configuration items.
115 for (vp = entry->check; vp != NULL; vp = vp->next) {
117 * Ignore attributes which are set
120 if (vp->operator != T_OP_EQ) {
125 * If it's a vendor attribute,
126 * or it's a wire protocol,
127 * ensure it has '=='.
129 if (((vp->attribute & ~0xffff) != 0) ||
130 (vp->attribute < 0x100)) {
132 DEBUG("[%s]:%d WARNING! Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
133 filename, entry->lineno,
137 DEBUG("\tChanging '%s =' to '%s =='",
140 vp->operator = T_OP_CMP_EQ;
145 * Cistron Compatibility mode.
147 * Re-write selected attributes
148 * to be '+=', instead of '='.
150 * All others get set to '=='
154 * Non-wire attributes become +=
156 * On the write attributes
159 if ((vp->attribute >= 0x100) &&
160 (vp->attribute <= 0xffff) &&
161 (vp->attribute != PW_HINT) &&
162 (vp->attribute != PW_HUNTGROUP_NAME)) {
163 DEBUG("\tChanging '%s =' to '%s +='",
165 vp->operator = T_OP_ADD;
167 DEBUG("\tChanging '%s =' to '%s =='",
169 vp->operator = T_OP_CMP_EQ;
173 } /* end of loop over check items */
177 * Look for server configuration items
180 * It's a common enough mistake, that it's
183 for (vp = entry->reply; vp != NULL; vp = vp->next) {
185 * If it's NOT a vendor attribute,
186 * and it's NOT a wire protocol
187 * and we ignore Fall-Through,
188 * then bitch about it, giving a
189 * good warning message.
191 if (!(vp->attribute & ~0xffff) &&
192 (vp->attribute > 0xff) &&
193 (vp->attribute > 1000)) {
194 log_debug("[%s]:%d WARNING! Check item \"%s\"\n"
195 "\tfound in reply item list for user \"%s\".\n"
196 "\tThis attribute MUST go on the first line"
197 " with the other check items",
198 filename, entry->lineno, vp->name,
213 * (Re-)read the "users" file into memory.
215 static int file_instantiate(CONF_SECTION *conf, void **instance)
217 struct file_instance *inst;
220 inst = rad_malloc(sizeof *inst);
222 if (cf_section_parse(conf, inst, module_config) < 0) {
227 rcode = getusersfile(inst->usersfile, &inst->users, inst->compat_mode);
229 radlog(L_ERR|L_CONS, "Errors reading %s", inst->usersfile);
230 free(inst->usersfile);
231 free(inst->acctusersfile);
236 rcode = getusersfile(inst->acctusersfile, &inst->acctusers, inst->compat_mode);
238 radlog(L_ERR|L_CONS, "Errors reading %s", inst->acctusersfile);
239 pairlist_free(&inst->users);
240 free(inst->usersfile);
241 free(inst->acctusersfile);
251 * Find the named user in the database. Create the
252 * set of attribute-value pairs to check and reply with
253 * for this user from the database. The main code only
254 * needs to check the password, the rest is done here.
256 static int file_authorize(void *instance, REQUEST *request)
258 VALUE_PAIR *namepair;
259 VALUE_PAIR *request_pairs;
260 VALUE_PAIR *check_tmp;
261 VALUE_PAIR *reply_tmp;
265 struct file_instance *inst = instance;
266 VALUE_PAIR **check_pairs, **reply_pairs;
267 VALUE_PAIR *check_save;
269 request_pairs = request->packet->vps;
270 check_pairs = &request->config_items;
271 reply_pairs = &request->reply->vps;
274 * Grab the canonical user name.
276 namepair = request->username;
277 name = namepair ? (char *) namepair->strvalue : "NONE";
280 * Find the entry for the user.
282 for(pl = inst->users; pl; pl = pl->next) {
284 * If the current entry is NOT a default,
285 * AND the name does NOT match the current entry,
286 * then skip to the next entry.
288 if ((strcmp(pl->name, "DEFAULT") != 0) &&
289 (strcmp(name, pl->name) != 0)) {
294 * If the current request matches against the
295 * check pairs, then add the reply pairs from the
296 * entry to the current list of reply pairs.
298 if ((paircmp(request_pairs, pl->check, reply_pairs) == 0)) {
300 if((mainconfig.do_usercollide) && (strcmp(pl->name, "DEFAULT"))) {
303 * We have to make sure the password
307 /* Save the orginal config items */
308 check_save = paircopy(request->config_items);
310 /* Copy this users check pairs to the request */
311 check_tmp = paircopy(pl->check);
312 pairmove(check_pairs, &check_tmp);
313 pairfree(&check_tmp);
315 DEBUG2(" users: Checking %s at %d", pl->name, pl->lineno);
316 /* Check the req to see if we matched */
317 if (rad_check_password(request)==0) {
318 DEBUG2(" users: Matched %s at %d", pl->name, pl->lineno);
322 /* Free our saved config items */
323 pairfree(&check_save);
326 * Already copied check items, so
327 * just copy reply here
329 reply_tmp = paircopy(pl->reply);
330 pairmove(reply_pairs, &reply_tmp);
331 pairfree(&reply_tmp);
333 /* We didn't match here */
335 /* Restore check items */
336 pairfree(&request->config_items);
337 request->config_items = paircopy(check_save);
338 check_pairs = &request->config_items;
345 DEBUG2(" users: Matched %s at %d", pl->name, pl->lineno);
347 check_tmp = paircopy(pl->check);
348 reply_tmp = paircopy(pl->reply);
349 pairmove(reply_pairs, &reply_tmp);
350 pairmove(check_pairs, &check_tmp);
351 pairfree(&reply_tmp);
352 pairfree(&check_tmp); /* should be NULL */
357 if (!fallthrough(pl->reply))
363 * See if we succeeded. If we didn't find the user,
364 * then exit from the module.
367 return RLM_MODULE_NOTFOUND;
370 * Remove server internal parameters.
372 pairdelete(reply_pairs, PW_FALL_THROUGH);
374 return RLM_MODULE_OK;
378 * Pre-Accounting - read the acct_users file for check_items and
379 * config_items. Reply items are Not Recommended(TM) in acct_users,
380 * except for Fallthrough, which should work
382 * This function is mostly a copy of file_authorize
384 static int file_preacct(void *instance, REQUEST *request)
386 VALUE_PAIR *namepair;
388 VALUE_PAIR *request_pairs;
389 VALUE_PAIR **config_pairs;
390 VALUE_PAIR **reply_pairs;
391 VALUE_PAIR *check_tmp;
392 VALUE_PAIR *reply_tmp;
395 struct file_instance *inst = instance;
397 namepair = request->username;
398 name = namepair ? (char *) namepair->strvalue : "NONE";
399 request_pairs = request->packet->vps;
400 config_pairs = &request->config_items;
401 reply_pairs = &request->reply->vps;
404 * Find the entry for the user.
406 for (pl = inst->acctusers; pl; pl = pl->next) {
408 if (strcmp(name, pl->name) && strcmp(pl->name, "DEFAULT"))
411 if (paircmp(request_pairs, pl->check, reply_pairs) == 0) {
412 DEBUG2(" acct_users: Matched %s at %d",
413 pl->name, pl->lineno);
415 check_tmp = paircopy(pl->check);
416 reply_tmp = paircopy(pl->reply);
417 pairmove(reply_pairs, &reply_tmp);
418 pairmove(config_pairs, &check_tmp);
419 pairfree(&reply_tmp);
420 pairfree(&check_tmp); /* should be NULL */
424 if (!fallthrough(pl->reply))
430 * See if we succeeded.
433 return RLM_MODULE_NOOP; /* on to the next module */
435 return RLM_MODULE_OK;
441 static int file_detach(void *instance)
443 struct file_instance *inst = instance;
444 pairlist_free(&inst->users);
445 pairlist_free(&inst->acctusers);
446 free(inst->usersfile);
447 free(inst->acctusersfile);
448 free(inst->compat_mode);
454 /* globally exported name */
455 module_t rlm_files = {
457 0, /* type: reserved */
458 NULL, /* initialization */
459 file_instantiate, /* instantiation */
461 NULL, /* authentication */
462 file_authorize, /* authorization */
463 file_preacct, /* preaccounting */
464 NULL, /* accounting */
465 NULL /* checksimul */
467 file_detach, /* detach */