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)
336 realm_free(mainconfig.realms);
337 mainconfig.realms = NULL;
338 tail = &mainconfig.realms;
340 if ((fp = fopen(file, "r")) == NULL) {
341 /* The realms file is not mandatory. If it exists it will
342 be used, however, since the new style config files are
343 more robust and flexible they are more likely to get used.
344 So this is a non-fatal error. */
347 radlog(L_INFO, "Using deprecated realms file. Support for this will go away soon.");
348 while(fgets(buffer, 256, fp) != NULL) {
350 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
351 radlog(L_ERR, "%s[%d]: line too long", file, lineno);
354 if (buffer[0] == '#' || buffer[0] == '\n')
357 if (!getword(&p, realm, sizeof(realm)) ||
358 !getword(&p, hostnm, sizeof(hostnm))) {
359 radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
363 c = rad_malloc(sizeof(REALM));
364 memset(c, 0, sizeof(REALM));
366 if ((s = strchr(hostnm, ':')) != NULL) {
368 c->auth_port = atoi(s);
369 c->acct_port = c->auth_port + 1;
371 c->auth_port = PW_AUTH_UDP_PORT;
372 c->acct_port = PW_ACCT_UDP_PORT;
375 if (strcmp(hostnm, "LOCAL") == 0) {
377 * Local realms don't have an IP address,
380 c->acct_ipaddr = c->ipaddr = htonl(INADDR_NONE);
382 c->auth_port = auth_port;
383 c->acct_port = acct_port;
387 c->ipaddr = ip_getaddr(hostnm);
388 c->acct_ipaddr = c->ipaddr;
390 if (c->ipaddr == htonl(INADDR_NONE)) {
391 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
392 file, lineno, hostnm);
397 * Find the remote server in the "clients" list.
398 * If we can't find it, there's a big problem...
400 client = client_find(c->ipaddr);
401 if (client == NULL) {
402 radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
403 file, lineno, hostnm, realm);
406 memcpy(c->secret, client->secret, sizeof(c->secret));
410 * Double-check lengths to be sure they're sane
412 if (strlen(hostnm) >= sizeof(c->server)) {
413 radlog(L_ERR, "%s[%d]: server name of length %d is greater than the allowed maximum of %d.",
415 (int) strlen(hostnm),
416 (int) sizeof(c->server) - 1);
419 if (strlen(realm) > sizeof(c->realm)) {
420 radlog(L_ERR, "%s[%d]: realm of length %d is greater than the allowed maximum of %d.",
423 (int) sizeof(c->realm) - 1);
428 * OK, they're sane, copy them over.
430 strcpy(c->realm, realm);
431 strcpy(c->server, hostnm);
432 c->striprealm = TRUE;
434 c->acct_active = TRUE;
436 while (getword(&p, opts, sizeof(opts))) {
437 if (strcmp(opts, "nostrip") == 0)
438 c->striprealm = FALSE;
439 if (strstr(opts, "noacct") != NULL)
441 if (strstr(opts, "trusted") != NULL)
443 if (strstr(opts, "notrealm") != NULL)
445 if (strstr(opts, "notsuffix") != NULL)
457 #endif /* BUILDDBM */
460 * Mark a host inactive
462 void realm_disable(uint32_t ipaddr, int port)
468 for(cl = mainconfig.realms; cl; cl = cl->next) {
469 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
471 * If we've received a reply (any reply)
472 * from the home server in the time spent
473 * re-sending this request, then don't mark
476 if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
481 cl->wakeup = now + mainconfig.proxy_dead_time;
482 radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
483 cl->server, port, cl->realm);
484 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {
485 if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
489 cl->acct_active = FALSE;
490 cl->acct_wakeup = now + mainconfig.proxy_dead_time;
491 radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
492 cl->acct_server, port, cl->realm);
498 * Find a realm in the REALM list.
500 REALM *realm_find(const char *realm, int accounting)
503 REALM *default_realm = NULL;
510 * If we're passed a NULL realm pointer,
511 * then look for a "NULL" realm string.
517 for (cl = mainconfig.realms; cl; cl = cl->next) {
519 * Wake up any sleeping realm.
521 if (cl->wakeup <= now) {
524 if (cl->acct_wakeup <= now) {
525 cl->acct_active = TRUE;
529 * Asked for auth/acct, and the auth/acct server
530 * is not active. Skip it.
532 if ((!accounting && !cl->active) ||
533 (accounting && !cl->acct_active)) {
536 * We've been asked to NOT fall through
537 * to the DEFAULT realm if there are
538 * exact matches for this realm which are
541 if ((!mainconfig.proxy_fallback) &&
542 (strcasecmp(cl->realm, realm) == 0)) {
549 * If it matches exactly, return it.
551 * Note that we just want ONE live realm
552 * here. We don't care about round-robin, or
553 * scatter techniques, as that's more properly
554 * the responsibility of the proxying code.
556 if (strcasecmp(cl->realm, realm) == 0) {
561 * No default realm, try to set one.
563 if ((default_realm == NULL) &&
564 (strcmp(cl->realm, "DEFAULT") == 0)) {
567 } /* loop over all realms */
570 * There WAS one or more matches which were marked dead,
571 * AND there were NO live matches, AND we've been asked
572 * to NOT fall through to the DEFAULT realm. Therefore,
573 * we return NULL, which means "no match found".
575 if (!mainconfig.proxy_fallback && dead_match) {
576 if (mainconfig.wake_all_if_all_dead) {
578 for (cl = mainconfig.realms; cl; cl = cl->next) {
579 if(strcasecmp(cl->realm,realm) == 0) {
580 if (!accounting && !cl->active) {
584 else if (accounting &&
586 cl->acct_active = TRUE;
598 /* If we didn't find the realm 'NULL' don't return the
601 if ((strcmp(realm, "NULL")) == 0) {
606 * Didn't find anything that matched exactly, return the
607 * DEFAULT realm. We also return the DEFAULT realm if
608 * all matching realms were marked dead, and we were
609 * asked to fall through to the DEFAULT realm in this
612 return default_realm;
616 * Find a realm for a proxy reply by proxy's IP
618 * Note that we don't do anything else.
620 REALM *realm_findbyaddr(uint32_t ipaddr, int port)
625 * Note that we do NOT check for inactive realms!
627 * The purpose of this code is simply to find a matching
628 * source IP/Port pair, for a home server which is allowed
629 * to send us proxy replies. If we get a reply, then it
630 * doesn't matter if we think the realm is inactive.
632 for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
633 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
636 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {