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"
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, *last = NULL, *t;
87 * Open the file. The error message should be a little
90 if ((fp = fopen(file, "r")) == NULL) {
93 radlog(L_CONS|L_ERR, "Couldn't open %s for reading: %s",
94 file, strerror(errno));
100 * Read the entire file into memory for speed.
102 while(fgets(buffer, sizeof(buffer), fp) != NULL) {
104 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
105 radlog(L_ERR, "%s[%d]: line too long", file, lineno);
109 if (buffer[0] == '#' || buffer[0] == '\n') continue;
112 * If the line contains nothing but whitespace,
116 while ((ptr[0] == ' ') ||
122 if (ptr[0] == '\0') continue;
125 if(mode == FIND_MODE_NAME) {
127 * Find the entry starting with the users name
129 if (isspace((int) buffer[0])) {
130 if (parsecode != T_EOL) {
132 "%s[%d]: Unexpected trailing comma for entry %s",
133 file, lineno, entry);
141 getword(&ptr, entry, sizeof(entry));
144 * Include another file if we see
147 if (strcasecmp(entry, "$include") == 0) {
148 while(isspace((int) *ptr))
151 while (!isspace((int) *ptr))
156 * If it's an absolute pathname,
157 * then use it verbatim.
159 * If not, then make the $include
160 * files *relative* to the current
164 strNcpy(newfile, file,
166 ptr = strrchr(newfile, '/');
172 if (pairlist_read(s, &t, 0) != 0) {
175 "%s[%d]: Could not open included file %s: %s",
176 file, lineno, s, strerror(errno));
185 while (last && last->next)
191 * Parse the check values
196 parsecode = userparse(ptr, &check_tmp);
197 if (parsecode == T_INVALID) {
200 "%s[%d]: Parse error (check) for entry %s: %s",
201 file, lineno, entry, librad_errstr);
204 } else if (parsecode == T_COMMA) {
206 "%s[%d]: Unexpected trailing comma in check item list for entry %s",
207 file, lineno, entry);
211 mode = FIND_MODE_REPLY;
215 if(*buffer == ' ' || *buffer == '\t') {
216 if (parsecode != T_COMMA) {
218 "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
219 file, lineno, entry);
225 * Parse the reply values
227 parsecode = userparse(buffer, &reply_tmp);
228 /* valid tokens are 1 or greater */
232 "%s[%d]: Parse error (reply) for entry %s: %s",
233 file, lineno, entry, librad_errstr);
240 * Done with this entry...
242 t = rad_malloc(sizeof(PAIR_LIST));
244 memset(t, 0, sizeof(*t));
245 t->name = strdup(entry);
246 t->check = check_tmp;
247 t->reply = reply_tmp;
248 t->lineno = old_lineno;
257 mode = FIND_MODE_NAME;
264 * Make sure that we also read the last line of the file!
266 if (mode == FIND_MODE_REPLY) {
281 static void debug_pair_list(PAIR_LIST *pl)
286 printf("Pair list: %s\n", pl->name);
287 printf("** Check:\n");
288 for(vp = pl->check; vp; vp = vp->next) {
290 fprint_attr_val(stdout, vp);
293 printf("** Reply:\n");
294 for(vp = pl->reply; vp; vp = vp->next) {
296 fprint_attr_val(stdout, vp);
304 #ifndef BUILDDBM /* HACK HACK */
309 void realm_free(REALM *cl)
321 * Read the realms file.
323 int read_realms_file(const char *file)
334 realm_free(mainconfig.realms);
335 mainconfig.realms = NULL;
336 tail = &mainconfig.realms;
338 if ((fp = fopen(file, "r")) == NULL) {
339 /* The realms file is not mandatory. If it exists it will
340 be used, however, since the new style config files are
341 more robust and flexible they are more likely to get used.
342 So this is a non-fatal error. */
345 radlog(L_INFO, "Using deprecated realms file. Support for this will go away soon.");
346 while(fgets(buffer, 256, fp) != NULL) {
348 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
349 radlog(L_ERR, "%s[%d]: line too long", file, lineno);
352 if (buffer[0] == '#' || buffer[0] == '\n')
355 if (!getword(&p, realm, sizeof(realm)) ||
356 !getword(&p, hostnm, sizeof(hostnm))) {
357 radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
361 c = rad_malloc(sizeof(REALM));
362 memset(c, 0, sizeof(REALM));
364 if ((s = strchr(hostnm, ':')) != NULL) {
366 c->auth_port = atoi(s);
367 c->acct_port = c->auth_port + 1;
369 c->auth_port = PW_AUTH_UDP_PORT;
370 c->acct_port = PW_ACCT_UDP_PORT;
373 if (strcmp(hostnm, "LOCAL") == 0) {
375 * Local realms don't have an IP address,
378 c->acct_ipaddr = c->ipaddr = htonl(INADDR_NONE);
380 c->auth_port = auth_port;
381 c->acct_port = acct_port;
385 c->ipaddr = ip_getaddr(hostnm);
386 c->acct_ipaddr = c->ipaddr;
388 if (c->ipaddr == htonl(INADDR_NONE)) {
389 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
390 file, lineno, hostnm);
395 * Find the remote server in the "clients" list.
396 * If we can't find it, there's a big problem...
398 client = client_find(c->ipaddr);
399 if (client == NULL) {
400 radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
401 file, lineno, hostnm, realm);
404 memcpy(c->secret, client->secret, sizeof(c->secret));
408 * Double-check lengths to be sure they're sane
410 if (strlen(hostnm) >= sizeof(c->server)) {
411 radlog(L_ERR, "%s[%d]: server name of length %d is greater than the allowed maximum of %d.",
413 strlen(hostnm), sizeof(c->server) - 1);
416 if (strlen(realm) > sizeof(c->realm)) {
417 radlog(L_ERR, "%s[%d]: realm of length %d is greater than the allowed maximum of %d.",
419 strlen(realm), sizeof(c->realm) - 1);
424 * OK, they're sane, copy them over.
426 strcpy(c->realm, realm);
427 strcpy(c->server, hostnm);
428 c->striprealm = TRUE;
430 c->acct_active = TRUE;
432 while (getword(&p, opts, sizeof(opts))) {
433 if (strcmp(opts, "nostrip") == 0)
434 c->striprealm = FALSE;
435 if (strstr(opts, "noacct") != NULL)
437 if (strstr(opts, "trusted") != NULL)
439 if (strstr(opts, "notrealm") != NULL)
441 if (strstr(opts, "notsuffix") != NULL)
453 #endif /* BUILDDBM */
456 * Mark a host inactive
458 void realm_disable(uint32_t ipaddr, int port)
464 for(cl = mainconfig.realms; cl; cl = cl->next) {
465 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
467 * If we've received a reply (any reply)
468 * from the home server in the time spent
469 * re-sending this request, then don't mark
472 if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
477 cl->wakeup = now + mainconfig.proxy_dead_time;
478 radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
479 cl->server, port, cl->realm);
480 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {
481 if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
485 cl->acct_active = FALSE;
486 cl->acct_wakeup = now + mainconfig.proxy_dead_time;
487 radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
488 cl->acct_server, port, cl->realm);
494 * Find a realm in the REALM list.
496 REALM *realm_find(const char *realm, int accounting)
499 REALM *default_realm = NULL;
506 * If we're passed a NULL realm pointer,
507 * then look for a "NULL" realm string.
513 for (cl = mainconfig.realms; cl; cl = cl->next) {
515 * Wake up any sleeping realm.
517 if (cl->wakeup <= now) {
520 if (cl->acct_wakeup <= now) {
521 cl->acct_active = TRUE;
525 * Asked for auth/acct, and the auth/acct server
526 * is not active. Skip it.
528 if ((!accounting && !cl->active) ||
529 (accounting && !cl->acct_active)) {
532 * We've been asked to NOT fall through
533 * to the DEFAULT realm if there are
534 * exact matches for this realm which are
537 if ((!mainconfig.proxy_fallback) &&
538 (strcasecmp(cl->realm, realm) == 0)) {
545 * If it matches exactly, return it.
547 * Note that we just want ONE live realm
548 * here. We don't care about round-robin, or
549 * scatter techniques, as that's more properly
550 * the responsibility of the proxying code.
552 if (strcasecmp(cl->realm, realm) == 0) {
557 * No default realm, try to set one.
559 if ((default_realm == NULL) &&
560 (strcmp(cl->realm, "DEFAULT") == 0)) {
563 } /* loop over all realms */
566 * There WAS one or more matches which were marked dead,
567 * AND there were NO live matches, AND we've been asked
568 * to NOT fall through to the DEFAULT realm. Therefore,
569 * we return NULL, which means "no match found".
571 if (!mainconfig.proxy_fallback && dead_match) {
572 if (mainconfig.wake_all_if_all_dead) {
574 for (cl = mainconfig.realms; cl; cl = cl->next) {
575 if(strcasecmp(cl->realm,realm) == 0) {
576 if (!accounting && !cl->active) {
580 else if (accounting &&
582 cl->acct_active = TRUE;
594 /* If we didn't find the realm 'NULL' don't return the
597 if ((strcmp(realm, "NULL")) == 0) {
602 * Didn't find anything that matched exactly, return the
603 * DEFAULT realm. We also return the DEFAULT realm if
604 * all matching realms were marked dead, and we were
605 * asked to fall through to the DEFAULT realm in this
608 return default_realm;
612 * Find a realm for a proxy reply by proxy's IP
614 * Note that we don't do anything else.
616 REALM *realm_findbyaddr(uint32_t ipaddr, int port)
621 * Note that we do NOT check for inactive realms!
623 * The purpose of this code is simply to find a matching
624 * source IP/Port pair, for a home server which is allowed
625 * to send us proxy replies. If we get a reply, then it
626 * doesn't matter if we think the realm is inactive.
628 for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
629 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
632 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {