2 * rlm_files.c authorization: Find a user in the "users" file.
3 * accounting: Write the "detail" files.
9 static const char rcsid[] = "$Id$";
37 struct file_instance {
49 #if defined(WITH_DBM) || defined(WITH_NDBM)
51 * See if a potential DBM file is present.
53 static int checkdbm(char *users, char *ext)
58 strcpy(buffer, users);
61 return stat(buffer, &st);
65 * Find the named user in the DBM user database.
66 * Returns: -1 not found
67 * 0 found but doesn't match.
68 * 1 found and matches.
70 static int dbm_find(DBM *dbmfile, char *name, VALUE_PAIR *request_pairs,
71 VALUE_PAIR **check_pairs, VALUE_PAIR **reply_pairs)
76 VALUE_PAIR *check_tmp;
77 VALUE_PAIR *reply_tmp;
81 named.dsize = strlen(name);
83 contentd = fetch(named);
86 contentd = dbm_fetch(dbmfile, named);
88 if(contentd.dptr == NULL)
95 * Parse the check values
98 contentd.dptr[contentd.dsize] = '\0';
100 if (*ptr != '\n' && userparse(ptr, &check_tmp) != 0) {
101 radlog(L_ERR|L_CONS, "Parse error (check) for user %s", name);
105 while(*ptr != '\n' && *ptr != '\0') {
109 radlog(L_ERR|L_CONS, "Parse error (no reply pairs) for user %s",
117 * Parse the reply values
119 if (userparse(ptr, &reply_tmp) != 0) {
120 radlog(L_ERR|L_CONS, "Parse error (reply) for user %s", name);
127 * See if the check_pairs match.
129 if (paircmp(request_pairs, check_tmp, reply_pairs) == 0) {
131 pairmove(reply_pairs, &reply_tmp);
132 pairmove2(reply_pairs, &reply_tmp, PW_FALL_THROUGH);
133 pairmove(check_pairs, &check_tmp);
143 * See if a VALUE_PAIR list contains Fall-Through = Yes
145 static int fallthrough(VALUE_PAIR *vp)
149 tmp = pairfind(vp, PW_FALL_THROUGH);
151 return tmp ? tmp->lvalue : 0;
156 static int file_init(void)
162 * A temporary holding area for config values to be extracted
163 * into, before they are copied into the instance data
165 static struct file_instance config;
167 static CONF_PARSER module_config[] = {
168 { "usersfile", PW_TYPE_STRING_PTR, &config.usersfile, RADIUS_USERS },
169 { "acctusersfile", PW_TYPE_STRING_PTR, &config.acctusersfile, RADIUS_ACCT_USERS },
170 { "compat", PW_TYPE_STRING_PTR, &config.compat_mode, "cistron" },
171 { NULL, -1, NULL, NULL }
174 static int getusersfile(const char *filename, PAIR_LIST **pair_list)
177 PAIR_LIST *users = NULL;
178 #if defined(WITH_DBM) || defined(WITH_NDBM)
180 (checkdbm(filename, ".dir") == 0 ||
181 checkdbm(filename, ".db") == 0)) {
182 radlog(L_INFO|L_CONS, "DBM files found but no -b flag " "given - NOT using DBM");
187 rcode = pairlist_read(filename, &users, 1);
194 * Walk through the 'users' file list, if we're debugging,
195 * or if we're in compat_mode.
198 (strcmp(config.compat_mode, "cistron") == 0)) {
201 int compat_mode = FALSE;
203 if (strcmp(config.compat_mode, "cistron") == 0) {
210 DEBUG("[%s]:%d Cistron compatibility checks for entry %s ...",
211 filename, entry->lineno,
216 * Look for improper use of '=' in the
217 * check items. They should be using
218 * '==' for on-the-wire RADIUS attributes,
219 * and probably ':=' for server
220 * configuration items.
222 for (vp = entry->check; vp != NULL; vp = vp->next) {
224 * Ignore attributes which are set
227 if (vp->operator != T_OP_EQ) {
232 * If it's a vendor attribute,
233 * or it's a wire protocol,
234 * ensure it has '=='.
236 if (((vp->attribute & ~0xffff) != 0) ||
237 (vp->attribute < 0x100)) {
239 DEBUG("[%s]:%d WARNING! Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
240 filename, entry->lineno,
244 DEBUG("\tChanging '%s =' to '%s =='",
247 vp->operator = T_OP_CMP_EQ;
252 * Cistron Compatibility mode.
254 * Re-write selected attributes
255 * to be '+=', instead of '='.
257 * All others get set to '=='
261 * Non-wire attributes become +=
263 * On the write attributes
266 if ((vp->attribute >= 0x100) &&
267 (vp->attribute <= 0xffff) &&
268 (vp->attribute != PW_HINT) &&
269 (vp->attribute != PW_HUNTGROUP_NAME)) {
270 DEBUG("\tChanging '%s =' to '%s +='",
272 vp->operator = T_OP_ADD;
274 DEBUG("\tChanging '%s =' to '%s =='",
276 vp->operator = T_OP_CMP_EQ;
280 } /* end of loop over check items */
284 * Look for server configuration items
287 * It's a common enough mistake, that it's
290 for (vp = entry->reply; vp != NULL; vp = vp->next) {
292 * If it's NOT a vendor attribute,
293 * and it's NOT a wire protocol
294 * and we ignore Fall-Through,
295 * then bitch about it, giving a
296 * good warning message.
298 if (!(vp->attribute & ~0xffff) &&
299 (vp->attribute > 0xff) &&
300 (vp->attribute > 1000)) {
301 log_debug("[%s]:%d WARNING! Check item \"%s\"\n"
302 "\tfound in reply item list for user \"%s\".\n"
303 "\tThis attribute MUST go on the first line"
304 " with the other check items",
305 filename, entry->lineno, vp->name,
320 * (Re-)read the "users" file into memory.
322 static int file_instantiate(CONF_SECTION *conf, void **instance)
324 struct file_instance *inst;
327 inst = malloc(sizeof *inst);
329 radlog(L_ERR|L_CONS, "Out of memory\n");
333 if (cf_section_parse(conf, module_config) < 0) {
338 inst->usersfile = config.usersfile;
339 inst->acctusersfile = config.acctusersfile;
340 config.usersfile = NULL;
341 config.acctusersfile = NULL;
343 rcode = getusersfile(inst->usersfile, &inst->users);
345 radlog(L_ERR|L_CONS, "Errors reading %s", inst->usersfile);
346 free(inst->usersfile);
347 free(inst->acctusersfile);
352 rcode = getusersfile(inst->acctusersfile, &inst->acctusers);
354 radlog(L_ERR|L_CONS, "Errors reading %s", inst->acctusersfile);
355 pairlist_free(&inst->users);
356 free(inst->usersfile);
357 free(inst->acctusersfile);
367 * Find the named user in the database. Create the
368 * set of attribute-value pairs to check and reply with
369 * for this user from the database. The main code only
370 * needs to check the password, the rest is done here.
372 static int file_authorize(void *instance, REQUEST *request)
374 VALUE_PAIR *namepair;
375 VALUE_PAIR *request_pairs;
376 VALUE_PAIR *check_tmp;
377 VALUE_PAIR *reply_tmp;
380 #if defined(WITH_DBM) || defined(WITH_NDBM)
385 struct file_instance *inst = instance;
386 VALUE_PAIR **check_pairs, **reply_pairs;
387 VALUE_PAIR *check_save;
390 request_pairs = request->packet->vps;
391 check_pairs = &request->config_items;
392 reply_pairs = &request->reply->vps;
395 * Grab the canonical user name.
397 namepair = request->username;
398 name = namepair ? (char *) namepair->strvalue : "NONE";
401 * Find the entry for the user.
403 #if defined(WITH_DBM) || defined(WITH_NDBM)
405 * FIXME: move to rlm_dbm.c
409 * FIXME: No Prefix / Suffix support for DBM.
412 if (dbminit(inst->usersfile) != 0)
415 if ((dbmfile = dbm_open(inst->usersfile, O_RDONLY, 0)) == NULL)
418 radlog(L_ERR|L_CONS, "cannot open dbm file %s",
420 return RLM_MODULE_FAIL;
423 r = dbm_find(dbmfile, name, request_pairs, check_pairs,
425 if (r > 0) found = 1;
426 if (r <= 0 || fallthrough(*reply_pairs)) {
428 pairdelete(reply_pairs, PW_FALL_THROUGH);
430 sprintf(buffer, "DEFAULT");
432 while ((r = dbm_find(dbmfile, buffer, request_pairs,
433 check_pairs, reply_pairs)) >= 0 || i < 2) {
436 if (!fallthrough(*reply_pairs))
438 pairdelete(reply_pairs,PW_FALL_THROUGH);
440 sprintf(buffer, "DEFAULT%d", i++);
451 * Note the fallthrough through the #endif.
455 for(pl = inst->users; pl; pl = pl->next) {
457 * If the current entry is NOT a default,
458 * AND the name does NOT match the current entry,
459 * then skip to the next entry.
461 if ((strcmp(pl->name, "DEFAULT") != 0) &&
462 (strcmp(name, pl->name) != 0)) {
467 * If the current request matches against the
468 * check pairs, then add the reply pairs from the
469 * entry to the current list of reply pairs.
471 if ((paircmp(request_pairs, pl->check, reply_pairs) == 0)) {
473 if((mainconfig.do_usercollide) && (strcmp(pl->name, "DEFAULT"))) {
476 * We have to make sure the password
480 /* Save the orginal config items */
481 check_save = paircopy(request->config_items);
483 /* Copy this users check pairs to the request */
484 check_tmp = paircopy(pl->check);
485 pairmove(check_pairs, &check_tmp);
488 DEBUG2(" users: Checking %s at %d", pl->name, pl->lineno);
489 /* Check the req to see if we matched */
490 if(rad_check_password(request)==0) {
491 DEBUG2(" users: Matched %s at %d", pl->name, pl->lineno);
495 /* Free our saved config items */
496 pairfree(check_save);
499 * Already copied check items, so
500 * just copy reply here
502 reply_tmp = paircopy(pl->reply);
503 pairmove(reply_pairs, &reply_tmp);
506 /* We didn't match here */
508 /* Restore check items */
509 pairfree(request->config_items);
510 request->config_items = paircopy(check_save);
511 check_pairs = &request->config_items;
518 DEBUG2(" users: Matched %s at %d", pl->name, pl->lineno);
520 check_tmp = paircopy(pl->check);
521 reply_tmp = paircopy(pl->reply);
522 pairmove(reply_pairs, &reply_tmp);
523 pairmove(check_pairs, &check_tmp);
525 pairfree(check_tmp); /* should be NULL */
530 if (!fallthrough(pl->reply))
536 * See if we succeeded. If we didn't find the user,
537 * then exit from the module.
540 return RLM_MODULE_NOTFOUND;
543 * Remove server internal parameters.
545 pairdelete(reply_pairs, PW_FALL_THROUGH);
547 return RLM_MODULE_OK;
551 * Authentication - unused.
553 static int file_authenticate(void *instance, REQUEST *request)
557 return RLM_MODULE_OK;
561 * Pre-Accounting - read the acct_users file for check_items and
562 * config_items. Reply items are Not Recommended(TM) in acct_users,
563 * except for Fallthrough, which should work
565 * This function is mostly a copy of file_authorize
567 static int file_preacct(void *instance, REQUEST *request)
569 VALUE_PAIR *namepair;
571 VALUE_PAIR *request_pairs;
572 VALUE_PAIR **config_pairs;
573 VALUE_PAIR *reply_pairs = NULL;
574 VALUE_PAIR *check_tmp;
575 VALUE_PAIR *reply_tmp;
578 #if defined(WITH_DBM) || defined(WITH_NDBM)
582 struct file_instance *inst = instance;
584 namepair = request->username;
585 name = namepair ? (char *) namepair->strvalue : "NONE";
586 request_pairs = request->packet->vps;
587 config_pairs = &request->config_items;
590 * Find the entry for the user.
592 #if defined(WITH_DBM) || defined(WITH_NDBM)
594 * FIXME: move to rlm_dbm.c
598 * FIXME: No Prefix / Suffix support for DBM.
601 if (dbminit(inst->acctusersfile) != 0)
604 if ((dbmfile = dbm_open(inst->acctusersfile, O_RDONLY, 0)) == NULL)
607 radlog(L_ERR|L_CONS, "cannot open dbm file %s",
609 return RLM_MODULE_FAIL;
612 r = dbm_find(dbmfile, name, request_pairs, config_pairs,
614 if (r > 0) found = 1;
615 if (r <= 0 || fallthrough(*reply_pairs)) {
617 pairdelete(reply_pairs, PW_FALL_THROUGH);
619 sprintf(buffer, "DEFAULT");
621 while ((r = dbm_find(dbmfile, buffer, request_pairs,
622 config_pairs, &reply_pairs)) >= 0 || i < 2) {
625 if (!fallthrough(*reply_pairs))
627 pairdelete(reply_pairs,PW_FALL_THROUGH);
629 sprintf(buffer, "DEFAULT%d", i++);
640 * Note the fallthrough through the #endif.
644 for(pl = inst->acctusers; pl; pl = pl->next) {
646 if (strcmp(name, pl->name) && strcmp(pl->name, "DEFAULT"))
649 if (paircmp(request_pairs, pl->check, &reply_pairs) == 0) {
650 DEBUG2(" acct_users: Matched %s at %d",
651 pl->name, pl->lineno);
653 check_tmp = paircopy(pl->check);
654 reply_tmp = paircopy(pl->reply);
655 pairmove(&reply_pairs, &reply_tmp);
656 pairmove(config_pairs, &check_tmp);
658 pairfree(check_tmp); /* should be NULL */
662 if (!fallthrough(pl->reply))
668 * See if we succeeded.
671 return RLM_MODULE_NOOP; /* on to the next module */
674 * FIXME: log a warning if there are any reply items other than
677 pairfree(reply_pairs); /* Don't need these */
679 return RLM_MODULE_OK;
685 static int file_detach(void *instance)
687 struct file_instance *inst = instance;
688 pairlist_free(&inst->users);
689 pairlist_free(&inst->acctusers);
690 free(inst->usersfile);
691 free(inst->acctusersfile);
697 /* globally exported name */
698 module_t rlm_files = {
700 0, /* type: reserved */
701 file_init, /* initialization */
702 file_instantiate, /* instantiation */
703 file_authorize, /* authorization */
704 file_authenticate, /* authentication */
705 file_preacct, /* preaccounting */
706 NULL, /* accounting */
707 NULL, /* checksimul */
708 file_detach, /* detach */