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$";
27 #include "libradius.h"
41 struct file_instance {
53 char *preproxy_usersfile;
54 PAIR_LIST *preproxy_users;
58 * See if a VALUE_PAIR list contains Fall-Through = Yes
60 static int fallthrough(VALUE_PAIR *vp)
63 tmp = pairfind(vp, PW_FALL_THROUGH);
65 return tmp ? tmp->lvalue : 0;
68 static CONF_PARSER module_config[] = {
69 { "usersfile", PW_TYPE_STRING_PTR,
70 offsetof(struct file_instance,usersfile), NULL, "${raddbdir}/users" },
71 { "acctusersfile", PW_TYPE_STRING_PTR,
72 offsetof(struct file_instance,acctusersfile), NULL, "${raddbdir}/acct_users" },
73 { "preproxy_usersfile", PW_TYPE_STRING_PTR,
74 offsetof(struct file_instance,preproxy_usersfile), NULL, "${raddbdir}/preproxy_users" },
75 { "compat", PW_TYPE_STRING_PTR,
76 offsetof(struct file_instance,compat_mode), NULL, "cistron" },
77 { NULL, -1, 0, NULL, NULL }
80 static int getusersfile(const char *filename, PAIR_LIST **pair_list, char *compat_mode_str)
83 PAIR_LIST *users = NULL;
85 rcode = pairlist_read(filename, &users, 1);
91 * Walk through the 'users' file list, if we're debugging,
92 * or if we're in compat_mode.
95 (strcmp(compat_mode_str, "cistron") == 0)) {
98 int compat_mode = FALSE;
100 if (strcmp(compat_mode_str, "cistron") == 0) {
107 DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...",
108 filename, entry->lineno,
113 * Look for improper use of '=' in the
114 * check items. They should be using
115 * '==' for on-the-wire RADIUS attributes,
116 * and probably ':=' for server
117 * configuration items.
119 for (vp = entry->check; vp != NULL; vp = vp->next) {
121 * Ignore attributes which are set
124 if (vp->operator != T_OP_EQ) {
129 * If it's a vendor attribute,
130 * or it's a wire protocol,
131 * ensure it has '=='.
133 if (((vp->attribute & ~0xffff) != 0) ||
134 (vp->attribute < 0x100)) {
136 DEBUG("[%s]:%d WARNING! Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
137 filename, entry->lineno,
141 DEBUG("\tChanging '%s =' to '%s =='",
144 vp->operator = T_OP_CMP_EQ;
149 * Cistron Compatibility mode.
151 * Re-write selected attributes
152 * to be '+=', instead of '='.
154 * All others get set to '=='
158 * Non-wire attributes become +=
160 * On the write attributes
163 if ((vp->attribute >= 0x100) &&
164 (vp->attribute <= 0xffff) &&
165 (vp->attribute != PW_HINT) &&
166 (vp->attribute != PW_HUNTGROUP_NAME)) {
167 DEBUG("\tChanging '%s =' to '%s +='",
169 vp->operator = T_OP_ADD;
171 DEBUG("\tChanging '%s =' to '%s =='",
173 vp->operator = T_OP_CMP_EQ;
177 } /* end of loop over check items */
181 * Look for server configuration items
184 * It's a common enough mistake, that it's
187 for (vp = entry->reply; vp != NULL; vp = vp->next) {
189 * If it's NOT a vendor attribute,
190 * and it's NOT a wire protocol
191 * and we ignore Fall-Through,
192 * then bitch about it, giving a
193 * good warning message.
195 if (!(vp->attribute & ~0xffff) &&
196 (vp->attribute > 0xff) &&
197 (vp->attribute > 1000)) {
198 log_debug("[%s]:%d WARNING! Check item \"%s\"\n"
199 "\tfound in reply item list for user \"%s\".\n"
200 "\tThis attribute MUST go on the first line"
201 " with the other check items",
202 filename, entry->lineno, vp->name,
217 * (Re-)read the "users" file into memory.
219 static int file_instantiate(CONF_SECTION *conf, void **instance)
221 struct file_instance *inst;
224 inst = rad_malloc(sizeof *inst);
228 memset(inst, 0, sizeof(*inst));
230 if (cf_section_parse(conf, inst, module_config) < 0) {
235 rcode = getusersfile(inst->usersfile, &inst->users, inst->compat_mode);
237 radlog(L_ERR|L_CONS, "Errors reading %s", inst->usersfile);
238 free(inst->usersfile);
239 free(inst->acctusersfile);
244 rcode = getusersfile(inst->acctusersfile, &inst->acctusers, inst->compat_mode);
246 radlog(L_ERR|L_CONS, "Errors reading %s", inst->acctusersfile);
247 pairlist_free(&inst->users);
248 free(inst->usersfile);
249 free(inst->acctusersfile);
255 * Get the pre-proxy stuff
257 rcode = getusersfile(inst->preproxy_usersfile, &inst->preproxy_users, inst->compat_mode);
259 radlog(L_ERR|L_CONS, "Errors reading %s", inst->preproxy_usersfile);
260 pairlist_free(&inst->users);
261 pairlist_free(&inst->acctusers);
262 free(inst->usersfile);
263 free(inst->acctusersfile);
264 free(inst->preproxy_usersfile);
274 * Find the named user in the database. Create the
275 * set of attribute-value pairs to check and reply with
276 * for this user from the database. The main code only
277 * needs to check the password, the rest is done here.
279 static int file_authorize(void *instance, REQUEST *request)
281 VALUE_PAIR *namepair;
282 VALUE_PAIR *request_pairs;
283 VALUE_PAIR *check_tmp;
284 VALUE_PAIR *reply_tmp;
288 struct file_instance *inst = instance;
289 VALUE_PAIR **check_pairs, **reply_pairs;
290 VALUE_PAIR *check_save;
292 request_pairs = request->packet->vps;
293 check_pairs = &request->config_items;
294 reply_pairs = &request->reply->vps;
297 * Grab the canonical user name.
299 namepair = request->username;
300 name = namepair ? (char *) namepair->strvalue : "NONE";
303 * Find the entry for the user.
305 for(pl = inst->users; pl; pl = pl->next) {
307 * If the current entry is NOT a default,
308 * AND the name does NOT match the current entry,
309 * then skip to the next entry.
311 if ((strcmp(pl->name, "DEFAULT") != 0) &&
312 (strcmp(name, pl->name) != 0)) {
317 * If the current request matches against the
318 * check pairs, then add the reply pairs from the
319 * entry to the current list of reply pairs.
321 if ((paircmp(request, request_pairs, pl->check, reply_pairs) == 0)) {
322 if ((mainconfig.do_usercollide) &&
323 (strcmp(pl->name, "DEFAULT"))) {
326 * We have to make sure the password
330 /* Save the orginal config items */
331 check_save = paircopy(request->config_items);
333 /* Copy this users check pairs to the request */
334 check_tmp = paircopy(pl->check);
335 pairmove(check_pairs, &check_tmp);
336 pairfree(&check_tmp);
338 DEBUG2(" users: Checking %s at %d", pl->name, pl->lineno);
339 /* Check the req to see if we matched */
340 if (rad_check_password(request)==0) {
341 DEBUG2(" users: Matched %s at %d", pl->name, pl->lineno);
345 /* Free our saved config items */
346 pairfree(&check_save);
349 * Already copied check items, so
350 * just copy reply here
352 reply_tmp = paircopy(pl->reply);
353 pairxlatmove(request, reply_pairs, &reply_tmp);
354 pairfree(&reply_tmp);
356 /* We didn't match here */
358 /* Restore check items */
359 pairfree(&request->config_items);
360 request->config_items = paircopy(check_save);
361 check_pairs = &request->config_items;
368 DEBUG2(" users: Matched %s at %d", pl->name, pl->lineno);
370 check_tmp = paircopy(pl->check);
371 reply_tmp = paircopy(pl->reply);
372 pairxlatmove(request, reply_pairs, &reply_tmp);
373 pairmove(check_pairs, &check_tmp);
374 pairfree(&reply_tmp);
375 pairfree(&check_tmp); /* should be NULL */
380 if (!fallthrough(pl->reply))
386 * See if we succeeded. If we didn't find the user,
387 * then exit from the module.
390 return RLM_MODULE_NOTFOUND;
393 * Remove server internal parameters.
395 pairdelete(reply_pairs, PW_FALL_THROUGH);
397 return RLM_MODULE_OK;
401 * Pre-Accounting - read the acct_users file for check_items and
402 * config_items. Reply items are Not Recommended(TM) in acct_users,
403 * except for Fallthrough, which should work
405 * This function is mostly a copy of file_authorize
407 static int file_preacct(void *instance, REQUEST *request)
409 VALUE_PAIR *namepair;
411 VALUE_PAIR *request_pairs;
412 VALUE_PAIR **config_pairs;
413 VALUE_PAIR **reply_pairs;
414 VALUE_PAIR *check_tmp;
415 VALUE_PAIR *reply_tmp;
418 struct file_instance *inst = instance;
420 namepair = request->username;
421 name = namepair ? (char *) namepair->strvalue : "NONE";
422 request_pairs = request->packet->vps;
423 config_pairs = &request->config_items;
424 reply_pairs = &request->reply->vps;
427 * Find the entry for the user.
429 for (pl = inst->acctusers; pl; pl = pl->next) {
431 if (strcmp(name, pl->name) && strcmp(pl->name, "DEFAULT"))
434 if (paircmp(request, request_pairs, pl->check, reply_pairs) == 0) {
435 DEBUG2(" acct_users: Matched %s at %d",
436 pl->name, pl->lineno);
438 check_tmp = paircopy(pl->check);
439 reply_tmp = paircopy(pl->reply);
440 pairxlatmove(request, reply_pairs, &reply_tmp);
441 pairmove(config_pairs, &check_tmp);
442 pairfree(&reply_tmp);
443 pairfree(&check_tmp); /* should be NULL */
447 if (!fallthrough(pl->reply))
453 * See if we succeeded.
456 return RLM_MODULE_NOOP; /* on to the next module */
458 return RLM_MODULE_OK;
462 * Pre-proxy - read the preproxy_users file for check_items and
465 * This function is mostly a copy of file_authorize
467 static int file_preproxy(void *instance, REQUEST *request)
469 VALUE_PAIR *namepair;
471 VALUE_PAIR *request_pairs;
472 VALUE_PAIR **config_pairs;
473 VALUE_PAIR **reply_pairs;
474 VALUE_PAIR *check_tmp;
475 VALUE_PAIR *reply_tmp;
478 struct file_instance *inst = instance;
480 namepair = request->username;
481 name = namepair ? (char *) namepair->strvalue : "NONE";
482 request_pairs = request->packet->vps;
483 config_pairs = &request->config_items;
484 reply_pairs = &request->proxy->vps;
487 * Find the entry for the user.
489 for (pl = inst->preproxy_users; pl; pl = pl->next) {
494 if (strcmp(name, pl->name) && strcmp(pl->name, "DEFAULT"))
497 if (paircmp(request, request_pairs, pl->check, reply_pairs) == 0) {
500 DEBUG2(" preproxy_users: Matched %s at %d",
501 pl->name, pl->lineno);
503 check_tmp = paircopy(pl->check);
504 reply_tmp = paircopy(pl->reply);
506 for (vp = reply_tmp; vp != NULL; vp = vp->next) {
508 * We've got to xlat the string
509 * before moving it over.
511 if (vp->flags.do_xlat) {
513 char buffer[sizeof(vp->strvalue)];
515 vp->flags.do_xlat = 0;
516 rcode = radius_xlat(buffer, sizeof(buffer),
521 * Parse the string into
524 pairparsevalue(vp, buffer);
526 } /* loop over the things to add to the reply */
528 pairxlatmove(request, reply_pairs, &reply_tmp);
529 pairmove(config_pairs, &check_tmp);
530 pairfree(&reply_tmp);
531 pairfree(&check_tmp); /* should be NULL */
535 if (!fallthrough(pl->reply))
541 * See if we succeeded.
544 return RLM_MODULE_NOOP; /* on to the next module */
546 return RLM_MODULE_OK;
552 static int file_detach(void *instance)
554 struct file_instance *inst = instance;
555 pairlist_free(&inst->users);
556 pairlist_free(&inst->acctusers);
557 pairlist_free(&inst->preproxy_users);
558 free(inst->usersfile);
559 free(inst->acctusersfile);
560 free(inst->preproxy_usersfile);
561 free(inst->compat_mode);
567 /* globally exported name */
568 module_t rlm_files = {
570 0, /* type: reserved */
571 NULL, /* initialization */
572 file_instantiate, /* instantiation */
574 NULL, /* authentication */
575 file_authorize, /* authorization */
576 file_preacct, /* preaccounting */
577 NULL, /* accounting */
578 NULL, /* checksimul */
579 file_preproxy, /* pre-proxy */
580 NULL, /* post-proxy */
583 file_detach, /* detach */