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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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$";
27 #include <freeradius-devel/autoconf.h>
31 #ifdef HAVE_NETINET_IN_H
32 # include <netinet/in.h>
41 #include <freeradius-devel/radiusd.h>
42 #include <freeradius-devel/rad_assert.h>
47 void pairlist_free(PAIR_LIST **pl)
51 for (p = *pl; p; p = next) {
52 if (p->name) free(p->name);
53 if (p->check) pairfree(&p->check);
54 if (p->reply) pairfree(&p->reply);
62 #define FIND_MODE_NAME 0
63 #define FIND_MODE_REPLY 1
66 * Read the users, huntgroups or hints file.
69 int pairlist_read(const char *file, PAIR_LIST **list, int complain)
72 int mode = FIND_MODE_NAME;
76 VALUE_PAIR *check_tmp;
77 VALUE_PAIR *reply_tmp;
78 PAIR_LIST *pl = NULL, *t;
79 PAIR_LIST **last = &pl;
86 * Open the file. The error message should be a little
89 if ((fp = fopen(file, "r")) == NULL) {
92 radlog(L_CONS|L_ERR, "Couldn't open %s for reading: %s",
93 file, strerror(errno));
99 * Read the entire file into memory for speed.
101 while(fgets(buffer, sizeof(buffer), fp) != NULL) {
103 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
104 radlog(L_ERR, "%s[%d]: line too long", file, lineno);
108 if (buffer[0] == '#' || buffer[0] == '\n') continue;
111 * If the line contains nothing but whitespace,
115 while ((ptr[0] == ' ') ||
121 if (ptr[0] == '\0') continue;
124 if(mode == FIND_MODE_NAME) {
126 * Find the entry starting with the users name
128 if (isspace((int) buffer[0])) {
129 if (parsecode != T_EOL) {
131 "%s[%d]: Unexpected trailing comma for entry %s",
132 file, lineno, entry);
140 getword(&ptr, entry, sizeof(entry));
143 * Include another file if we see
146 if (strcasecmp(entry, "$include") == 0) {
147 while(isspace((int) *ptr))
150 while (!isspace((int) *ptr))
155 * If it's an absolute pathname,
156 * then use it verbatim.
158 * If not, then make the $include
159 * files *relative* to the current
163 strNcpy(newfile, file,
165 ptr = strrchr(newfile, '/');
171 if (pairlist_read(s, &t, 0) != 0) {
174 "%s[%d]: Could not open included file %s: %s",
175 file, lineno, s, strerror(errno));
182 * t may be NULL, it may have one
183 * entry, or it may be a linked list
184 * of entries. Go to the end of the
188 last = &((*last)->next);
193 * Parse the check values
198 parsecode = userparse(ptr, &check_tmp);
199 if (parsecode == T_OP_INVALID) {
202 "%s[%d]: Parse error (check) for entry %s: %s",
203 file, lineno, entry, librad_errstr);
206 } else if (parsecode == T_COMMA) {
208 "%s[%d]: Unexpected trailing comma in check item list for entry %s",
209 file, lineno, entry);
213 mode = FIND_MODE_REPLY;
217 if(*buffer == ' ' || *buffer == '\t') {
218 if (parsecode != T_COMMA) {
220 "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
221 file, lineno, entry);
227 * Parse the reply values
229 parsecode = userparse(buffer, &reply_tmp);
230 /* valid tokens are 1 or greater */
234 "%s[%d]: Parse error (reply) for entry %s: %s",
235 file, lineno, entry, librad_errstr);
242 * Done with this entry...
244 t = rad_malloc(sizeof(PAIR_LIST));
246 memset(t, 0, sizeof(*t));
247 t->name = strdup(entry);
248 t->check = check_tmp;
249 t->reply = reply_tmp;
250 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)
333 int got_realm = FALSE;
335 realm_free(mainconfig.realms);
336 mainconfig.realms = NULL;
337 tail = &mainconfig.realms;
339 if ((fp = fopen(file, "r")) == NULL) {
340 /* The realms file is not mandatory. If it exists it will
341 be used, however, since the new style config files are
342 more robust and flexible they are more likely to get used.
343 So this is a non-fatal error. */
347 while(fgets(buffer, 256, fp) != NULL) {
349 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
350 radlog(L_ERR, "%s[%d]: line too long", file, lineno);
353 if (buffer[0] == '#' || buffer[0] == '\n')
356 if (!getword(&p, realm, sizeof(realm)) ||
357 !getword(&p, hostnm, sizeof(hostnm))) {
358 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->ipaddr.af = c->acct_ipaddr.af = AF_INET;
381 c->ipaddr.ipaddr.ip4addr.s_addr = c->acct_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
389 if (ip_hton(hostnm, AF_INET, &c->ipaddr) < 0) {
390 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
391 file, lineno, hostnm);
394 c->acct_ipaddr = c->ipaddr;
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_old(&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 %u is greater than the allowed maximum of %u.",
416 sizeof(c->server) - 1);
419 if (strlen(realm) > sizeof(c->realm)) {
420 radlog(L_ERR, "%s[%d]: realm of length %u is greater than the allowed maximum of %u.",
423 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)
456 * Complain only if the realms file has content.
459 radlog(L_INFO, "Using deprecated realms file. Support for this will go away soon.");
464 #endif /* BUILDDBM */
467 * Mark a host inactive
469 void realm_disable(REQUEST *request)
473 lrad_ipaddr_t *ipaddr;
478 if (!request || !request->proxy ||
479 (request->proxy->dst_ipaddr.af != AF_INET)) return;
481 ipaddr = &request->proxy->dst_ipaddr;
482 port = request->proxy->dst_port;
484 for (cl = mainconfig.realms; cl; cl = cl->next) {
485 if (ipaddr->af != cl->ipaddr.af) continue;
488 * FIXME: Switch over AF
490 if ((ipaddr->ipaddr.ip4addr.s_addr == cl->ipaddr.ipaddr.ip4addr.s_addr) &&
491 (port == cl->auth_port)) {
493 * If we've received a reply (any reply)
494 * from the home server in the time spent
495 * re-sending this request, then don't mark
498 if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
503 cl->wakeup = now + mainconfig.proxy_dead_time;
504 radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
505 cl->server, port, cl->realm);
506 } else if ((ipaddr->ipaddr.ip4addr.s_addr == cl->acct_ipaddr.ipaddr.ip4addr.s_addr) &&
507 (port == cl->acct_port)) {
508 if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
512 cl->acct_active = FALSE;
513 cl->acct_wakeup = now + mainconfig.proxy_dead_time;
514 radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
515 cl->acct_server, port, cl->realm);
521 * Find a realm in the REALM list.
523 REALM *realm_find(const char *realm, int accounting)
526 REALM *default_realm = NULL;
533 * If we're passed a NULL realm pointer,
534 * then look for a "NULL" realm string.
540 for (cl = mainconfig.realms; cl; cl = cl->next) {
542 * Wake up any sleeping realm.
544 if (cl->wakeup <= now) {
547 if (cl->acct_wakeup <= now) {
548 cl->acct_active = TRUE;
552 * Asked for auth/acct, and the auth/acct server
553 * is not active. Skip it.
555 if ((!accounting && !cl->active) ||
556 (accounting && !cl->acct_active)) {
559 * We've been asked to NOT fall through
560 * to the DEFAULT realm if there are
561 * exact matches for this realm which are
564 if ((!mainconfig.proxy_fallback) &&
565 (strcasecmp(cl->realm, realm) == 0)) {
572 * If it matches exactly, return it.
574 * Note that we just want ONE live realm
575 * here. We don't care about round-robin, or
576 * scatter techniques, as that's more properly
577 * the responsibility of the proxying code.
579 if (strcasecmp(cl->realm, realm) == 0) {
584 * No default realm, try to set one.
586 if ((default_realm == NULL) &&
587 (strcmp(cl->realm, "DEFAULT") == 0)) {
590 } /* loop over all realms */
593 * There WAS one or more matches which were marked dead,
594 * AND there were NO live matches, AND we've been asked
595 * to NOT fall through to the DEFAULT realm. Therefore,
596 * we return NULL, which means "no match found".
598 if (!mainconfig.proxy_fallback && dead_match) {
599 if (mainconfig.wake_all_if_all_dead) {
601 for (cl = mainconfig.realms; cl; cl = cl->next) {
602 if(strcasecmp(cl->realm,realm) == 0) {
603 if (!accounting && !cl->active) {
607 else if (accounting &&
609 cl->acct_active = TRUE;
621 /* If we didn't find the realm 'NULL' don't return the
624 if ((strcmp(realm, "NULL")) == 0) {
629 * Didn't find anything that matched exactly, return the
630 * DEFAULT realm. We also return the DEFAULT realm if
631 * all matching realms were marked dead, and we were
632 * asked to fall through to the DEFAULT realm in this
635 return default_realm;
639 * Find a realm for a proxy reply by proxy's IP
641 * Note that we don't do anything else.
643 REALM *realm_findbyaddr(uint32_t ipaddr, int port)
648 * Note that we do NOT check for inactive realms!
650 * The purpose of this code is simply to find a matching
651 * source IP/Port pair, for a home server which is allowed
652 * to send us proxy replies. If we get a reply, then it
653 * doesn't matter if we think the realm is inactive.
655 for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
656 if (cl->ipaddr.af != AF_INET) rad_assert(0 == 1);
658 if ((ipaddr == cl->ipaddr.ipaddr.ip4addr.s_addr) &&
659 (port == cl->auth_port)) {
662 } else if ((ipaddr == cl->acct_ipaddr.ipaddr.ip4addr.s_addr) &&
663 (port == cl->acct_port)) {