2 * files.c Read config files into memory.
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 2000 The FreeRADIUS server project
21 * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
22 * Copyright 2000 Alan DeKok <aland@ox.org>
25 static const char rcsid[] = "$Id$";
28 #include "libradius.h"
32 #ifdef HAVE_NETINET_IN_H
33 # include <netinet/in.h>
49 void pairlist_free(PAIR_LIST **pl)
53 for (p = *pl; p; p = next) {
54 if (p->name) free(p->name);
55 if (p->check) pairfree(&p->check);
56 if (p->reply) pairfree(&p->reply);
64 #define FIND_MODE_NAME 0
65 #define FIND_MODE_REPLY 1
68 * Read the users, huntgroups or hints file.
71 int pairlist_read(const char *file, PAIR_LIST **list, int complain)
74 int mode = FIND_MODE_NAME;
78 VALUE_PAIR *check_tmp;
79 VALUE_PAIR *reply_tmp;
80 PAIR_LIST *pl = NULL, *t;
81 PAIR_LIST **last = &pl;
88 * Open the file. The error message should be a little
91 if ((fp = fopen(file, "r")) == NULL) {
94 radlog(L_CONS|L_ERR, "Couldn't open %s for reading: %s",
95 file, strerror(errno));
101 * Read the entire file into memory for speed.
103 while(fgets(buffer, sizeof(buffer), fp) != NULL) {
105 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
106 radlog(L_ERR, "%s[%d]: line too long", file, lineno);
110 if (buffer[0] == '#' || buffer[0] == '\n') continue;
113 * If the line contains nothing but whitespace,
117 while ((ptr[0] == ' ') ||
123 if (ptr[0] == '\0') continue;
126 if(mode == FIND_MODE_NAME) {
128 * Find the entry starting with the users name
130 if (isspace((int) buffer[0])) {
131 if (parsecode != T_EOL) {
133 "%s[%d]: Unexpected trailing comma for entry %s",
134 file, lineno, entry);
142 getword(&ptr, entry, sizeof(entry));
145 * Include another file if we see
148 if (strcasecmp(entry, "$include") == 0) {
149 while(isspace((int) *ptr))
152 while (!isspace((int) *ptr))
157 * If it's an absolute pathname,
158 * then use it verbatim.
160 * If not, then make the $include
161 * files *relative* to the current
165 strNcpy(newfile, file,
167 ptr = strrchr(newfile, '/');
173 if (pairlist_read(s, &t, 0) != 0) {
176 "%s[%d]: Could not open included file %s: %s",
177 file, lineno, s, strerror(errno));
184 * t may be NULL, it may have one
185 * entry, or it may be a linked list
186 * of entries. Go to the end of the
190 last = &((*last)->next);
195 * Parse the check values
200 parsecode = userparse(ptr, &check_tmp);
201 if (parsecode == T_INVALID) {
204 "%s[%d]: Parse error (check) for entry %s: %s",
205 file, lineno, entry, librad_errstr);
208 } else if (parsecode == T_COMMA) {
210 "%s[%d]: Unexpected trailing comma in check item list for entry %s",
211 file, lineno, entry);
215 mode = FIND_MODE_REPLY;
219 if(*buffer == ' ' || *buffer == '\t') {
220 if (parsecode != T_COMMA) {
222 "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
223 file, lineno, entry);
229 * Parse the reply values
231 parsecode = userparse(buffer, &reply_tmp);
232 /* valid tokens are 1 or greater */
236 "%s[%d]: Parse error (reply) for entry %s: %s",
237 file, lineno, entry, librad_errstr);
244 * Done with this entry...
246 t = rad_malloc(sizeof(PAIR_LIST));
248 memset(t, 0, sizeof(*t));
249 t->name = strdup(entry);
250 t->check = check_tmp;
251 t->reply = reply_tmp;
252 t->lineno = old_lineno;
259 mode = FIND_MODE_NAME;
266 * Make sure that we also read the last line of the file!
268 if (mode == FIND_MODE_REPLY) {
283 static void debug_pair_list(PAIR_LIST *pl)
288 printf("Pair list: %s\n", pl->name);
289 printf("** Check:\n");
290 for(vp = pl->check; vp; vp = vp->next) {
292 fprint_attr_val(stdout, vp);
295 printf("** Reply:\n");
296 for(vp = pl->reply; vp; vp = vp->next) {
298 fprint_attr_val(stdout, vp);
306 #ifndef BUILDDBM /* HACK HACK */
311 void realm_free(REALM *cl)
323 * Read the realms file.
325 int read_realms_file(const char *file)
335 int got_realm = FALSE;
337 realm_free(mainconfig.realms);
338 mainconfig.realms = NULL;
339 tail = &mainconfig.realms;
341 if ((fp = fopen(file, "r")) == NULL) {
342 /* The realms file is not mandatory. If it exists it will
343 be used, however, since the new style config files are
344 more robust and flexible they are more likely to get used.
345 So this is a non-fatal error. */
349 while(fgets(buffer, 256, fp) != NULL) {
351 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
352 radlog(L_ERR, "%s[%d]: line too long", file, lineno);
355 if (buffer[0] == '#' || buffer[0] == '\n')
358 if (!getword(&p, realm, sizeof(realm)) ||
359 !getword(&p, hostnm, sizeof(hostnm))) {
360 radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
365 c = rad_malloc(sizeof(REALM));
366 memset(c, 0, sizeof(REALM));
368 if ((s = strchr(hostnm, ':')) != NULL) {
370 c->auth_port = atoi(s);
371 c->acct_port = c->auth_port + 1;
373 c->auth_port = PW_AUTH_UDP_PORT;
374 c->acct_port = PW_ACCT_UDP_PORT;
377 if (strcmp(hostnm, "LOCAL") == 0) {
379 * Local realms don't have an IP address,
382 c->acct_ipaddr = c->ipaddr = htonl(INADDR_NONE);
389 c->ipaddr = ip_getaddr(hostnm);
390 c->acct_ipaddr = c->ipaddr;
392 if (c->ipaddr == htonl(INADDR_NONE)) {
393 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
394 file, lineno, hostnm);
399 * Find the remote server in the "clients" list.
400 * If we can't find it, there's a big problem...
402 client = client_find(c->ipaddr);
403 if (client == NULL) {
404 radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
405 file, lineno, hostnm, realm);
408 memcpy(c->secret, client->secret, sizeof(c->secret));
412 * Double-check lengths to be sure they're sane
414 if (strlen(hostnm) >= sizeof(c->server)) {
415 radlog(L_ERR, "%s[%d]: server name of length %d is greater than the allowed maximum of %d.",
417 (int) strlen(hostnm),
418 (int) sizeof(c->server) - 1);
421 if (strlen(realm) > sizeof(c->realm)) {
422 radlog(L_ERR, "%s[%d]: realm of length %d is greater than the allowed maximum of %d.",
425 (int) sizeof(c->realm) - 1);
430 * OK, they're sane, copy them over.
432 strcpy(c->realm, realm);
433 strcpy(c->server, hostnm);
434 c->striprealm = TRUE;
436 c->acct_active = TRUE;
438 while (getword(&p, opts, sizeof(opts))) {
439 if (strcmp(opts, "nostrip") == 0)
440 c->striprealm = FALSE;
441 if (strstr(opts, "noacct") != NULL)
443 if (strstr(opts, "trusted") != NULL)
445 if (strstr(opts, "notrealm") != NULL)
447 if (strstr(opts, "notsuffix") != NULL)
458 * Complain only if the realms file has content.
461 radlog(L_INFO, "Using deprecated realms file. Support for this will go away soon.");
466 #endif /* BUILDDBM */
469 * Mark a host inactive
471 void realm_disable(uint32_t ipaddr, int port)
477 for(cl = mainconfig.realms; cl; cl = cl->next) {
478 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
480 * If we've received a reply (any reply)
481 * from the home server in the time spent
482 * re-sending this request, then don't mark
485 if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
490 cl->wakeup = now + mainconfig.proxy_dead_time;
491 radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
492 cl->server, port, cl->realm);
493 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {
494 if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
498 cl->acct_active = FALSE;
499 cl->acct_wakeup = now + mainconfig.proxy_dead_time;
500 radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
501 cl->acct_server, port, cl->realm);
507 * Find a realm in the REALM list.
509 REALM *realm_find(const char *realm, int accounting)
512 REALM *default_realm = NULL;
519 * If we're passed a NULL realm pointer,
520 * then look for a "NULL" realm string.
526 for (cl = mainconfig.realms; cl; cl = cl->next) {
528 * Wake up any sleeping realm.
530 if (cl->wakeup <= now) {
533 if (cl->acct_wakeup <= now) {
534 cl->acct_active = TRUE;
538 * Asked for auth/acct, and the auth/acct server
539 * is not active. Skip it.
541 if ((!accounting && !cl->active) ||
542 (accounting && !cl->acct_active)) {
545 * We've been asked to NOT fall through
546 * to the DEFAULT realm if there are
547 * exact matches for this realm which are
550 if ((!mainconfig.proxy_fallback) &&
551 (strcasecmp(cl->realm, realm) == 0)) {
558 * If it matches exactly, return it.
560 * Note that we just want ONE live realm
561 * here. We don't care about round-robin, or
562 * scatter techniques, as that's more properly
563 * the responsibility of the proxying code.
565 if (strcasecmp(cl->realm, realm) == 0) {
570 * No default realm, try to set one.
572 if ((default_realm == NULL) &&
573 (strcmp(cl->realm, "DEFAULT") == 0)) {
576 } /* loop over all realms */
579 * There WAS one or more matches which were marked dead,
580 * AND there were NO live matches, AND we've been asked
581 * to NOT fall through to the DEFAULT realm. Therefore,
582 * we return NULL, which means "no match found".
584 if (!mainconfig.proxy_fallback && dead_match) {
585 if (mainconfig.wake_all_if_all_dead) {
587 for (cl = mainconfig.realms; cl; cl = cl->next) {
588 if(strcasecmp(cl->realm,realm) == 0) {
589 if (!accounting && !cl->active) {
593 else if (accounting &&
595 cl->acct_active = TRUE;
607 /* If we didn't find the realm 'NULL' don't return the
610 if ((strcmp(realm, "NULL")) == 0) {
615 * Didn't find anything that matched exactly, return the
616 * DEFAULT realm. We also return the DEFAULT realm if
617 * all matching realms were marked dead, and we were
618 * asked to fall through to the DEFAULT realm in this
621 return default_realm;
625 * Find a realm for a proxy reply by proxy's IP
627 * Note that we don't do anything else.
629 REALM *realm_findbyaddr(uint32_t ipaddr, int port)
634 * Note that we do NOT check for inactive realms!
636 * The purpose of this code is simply to find a matching
637 * source IP/Port pair, for a home server which is allowed
638 * to send us proxy replies. If we get a reply, then it
639 * doesn't matter if we think the realm is inactive.
641 for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
642 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
645 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {