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,2006 The FreeRADIUS server project
21 * Copyright 2000 Miquel van Smoorenburg <miquels@cistron.nl>
22 * Copyright 2000 Alan DeKok <aland@ox.org>
25 #include <freeradius-devel/ident.h>
28 #include <freeradius-devel/autoconf.h>
32 #ifdef HAVE_NETINET_IN_H
33 # include <netinet/in.h>
42 #include <freeradius-devel/radiusd.h>
43 #include <freeradius-devel/rad_assert.h>
48 void pairlist_free(PAIR_LIST **pl)
52 for (p = *pl; p; p = next) {
53 if (p->name) free(p->name);
54 if (p->check) pairfree(&p->check);
55 if (p->reply) pairfree(&p->reply);
63 #define FIND_MODE_NAME 0
64 #define FIND_MODE_REPLY 1
67 * Read the users, huntgroups or hints file.
70 int pairlist_read(const char *file, PAIR_LIST **list, int complain)
73 int mode = FIND_MODE_NAME;
77 VALUE_PAIR *check_tmp;
78 VALUE_PAIR *reply_tmp;
79 PAIR_LIST *pl = NULL, *t;
80 PAIR_LIST **last = &pl;
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 strlcpy(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));
183 * t may be NULL, it may have one
184 * entry, or it may be a linked list
185 * of entries. Go to the end of the
189 last = &((*last)->next);
194 * Parse the check values
199 parsecode = userparse(ptr, &check_tmp);
200 if (parsecode == T_OP_INVALID) {
203 "%s[%d]: Parse error (check) for entry %s: %s",
204 file, lineno, entry, librad_errstr);
207 } else if (parsecode == T_COMMA) {
209 "%s[%d]: Unexpected trailing comma in check item list for entry %s",
210 file, lineno, entry);
214 mode = FIND_MODE_REPLY;
218 if(*buffer == ' ' || *buffer == '\t') {
219 if (parsecode != T_COMMA) {
221 "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
222 file, lineno, entry);
228 * Parse the reply values
230 parsecode = userparse(buffer, &reply_tmp);
231 /* valid tokens are 1 or greater */
235 "%s[%d]: Parse error (reply) for entry %s: %s",
236 file, lineno, entry, librad_errstr);
243 * Done with this entry...
245 t = rad_malloc(sizeof(PAIR_LIST));
247 memset(t, 0, sizeof(*t));
248 t->name = strdup(entry);
249 t->check = check_tmp;
250 t->reply = reply_tmp;
251 t->lineno = old_lineno;
258 mode = FIND_MODE_NAME;
265 * Make sure that we also read the last line of the file!
267 if (mode == FIND_MODE_REPLY) {
282 static void debug_pair_list(PAIR_LIST *pl)
287 printf("Pair list: %s\n", pl->name);
288 printf("** Check:\n");
289 for(vp = pl->check; vp; vp = vp->next) {
291 fprint_attr_val(stdout, vp);
294 printf("** Reply:\n");
295 for(vp = pl->reply; vp; vp = vp->next) {
297 fprint_attr_val(stdout, vp);
305 #ifndef BUILDDBM /* HACK HACK */
310 void realm_free(REALM *cl)
322 * Read the realms file.
324 int read_realms_file(const char *file)
334 int got_realm = FALSE;
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. */
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);
364 c = rad_malloc(sizeof(REALM));
365 memset(c, 0, sizeof(REALM));
367 if ((s = strchr(hostnm, ':')) != NULL) {
369 c->auth_port = atoi(s);
370 c->acct_port = c->auth_port + 1;
372 c->auth_port = PW_AUTH_UDP_PORT;
373 c->acct_port = PW_ACCT_UDP_PORT;
376 if (strcmp(hostnm, "LOCAL") == 0) {
378 * Local realms don't have an IP address,
381 c->ipaddr.af = c->acct_ipaddr.af = AF_INET;
382 c->ipaddr.ipaddr.ip4addr.s_addr = c->acct_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
390 if (ip_hton(hostnm, AF_INET, &c->ipaddr) < 0) {
391 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
392 file, lineno, hostnm);
395 c->acct_ipaddr = c->ipaddr;
398 * Find the remote server in the "clients" list.
399 * If we can't find it, there's a big problem...
401 client = client_find_old(&c->ipaddr);
402 if (client == NULL) {
403 radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
404 file, lineno, hostnm, realm);
407 memcpy(c->secret, client->secret, sizeof(c->secret));
411 * Double-check lengths to be sure they're sane
413 if (strlen(hostnm) >= sizeof(c->server)) {
414 radlog(L_ERR, "%s[%d]: server name of length %u is greater than the allowed maximum of %u.",
417 sizeof(c->server) - 1);
420 if (strlen(realm) > sizeof(c->realm)) {
421 radlog(L_ERR, "%s[%d]: realm of length %u is greater than the allowed maximum of %u.",
424 sizeof(c->realm) - 1);
429 * OK, they're sane, copy them over.
431 strcpy(c->realm, realm);
432 strcpy(c->server, hostnm);
433 c->striprealm = TRUE;
435 c->acct_active = TRUE;
437 while (getword(&p, opts, sizeof(opts))) {
438 if (strcmp(opts, "nostrip") == 0)
439 c->striprealm = FALSE;
440 if (strstr(opts, "noacct") != NULL)
442 if (strstr(opts, "trusted") != NULL)
444 if (strstr(opts, "notrealm") != NULL)
446 if (strstr(opts, "notsuffix") != NULL)
457 * Complain only if the realms file has content.
460 radlog(L_INFO, "Using deprecated realms file. Support for this will go away soon.");
465 #endif /* BUILDDBM */
468 * Mark a host inactive
470 void realm_disable(REQUEST *request)
474 lrad_ipaddr_t *ipaddr;
479 if (!request || !request->proxy ||
480 (request->proxy->dst_ipaddr.af != AF_INET)) return;
482 ipaddr = &request->proxy->dst_ipaddr;
483 port = request->proxy->dst_port;
485 for (cl = mainconfig.realms; cl; cl = cl->next) {
486 if (ipaddr->af != cl->ipaddr.af) continue;
489 * FIXME: Switch over AF
491 if ((ipaddr->ipaddr.ip4addr.s_addr == cl->ipaddr.ipaddr.ip4addr.s_addr) &&
492 (port == cl->auth_port)) {
494 * If we've received a reply (any reply)
495 * from the home server in the time spent
496 * re-sending this request, then don't mark
499 if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
504 cl->wakeup = now + mainconfig.proxy_dead_time;
505 radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
506 cl->server, port, cl->realm);
507 } else if ((ipaddr->ipaddr.ip4addr.s_addr == cl->acct_ipaddr.ipaddr.ip4addr.s_addr) &&
508 (port == cl->acct_port)) {
509 if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
513 cl->acct_active = FALSE;
514 cl->acct_wakeup = now + mainconfig.proxy_dead_time;
515 radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
516 cl->acct_server, port, cl->realm);
522 * Find a realm in the REALM list.
524 REALM *realm_find(const char *realm, int accounting)
527 REALM *default_realm = NULL;
534 * If we're passed a NULL realm pointer,
535 * then look for a "NULL" realm string.
541 for (cl = mainconfig.realms; cl; cl = cl->next) {
543 * Wake up any sleeping realm.
545 if (cl->wakeup <= now) {
548 if (cl->acct_wakeup <= now) {
549 cl->acct_active = TRUE;
553 * Asked for auth/acct, and the auth/acct server
554 * is not active. Skip it.
556 if ((!accounting && !cl->active) ||
557 (accounting && !cl->acct_active)) {
560 * We've been asked to NOT fall through
561 * to the DEFAULT realm if there are
562 * exact matches for this realm which are
565 if ((!mainconfig.proxy_fallback) &&
566 (strcasecmp(cl->realm, realm) == 0)) {
573 * If it matches exactly, return it.
575 * Note that we just want ONE live realm
576 * here. We don't care about round-robin, or
577 * scatter techniques, as that's more properly
578 * the responsibility of the proxying code.
580 if (strcasecmp(cl->realm, realm) == 0) {
585 * No default realm, try to set one.
587 if ((default_realm == NULL) &&
588 (strcmp(cl->realm, "DEFAULT") == 0)) {
591 } /* loop over all realms */
594 * There WAS one or more matches which were marked dead,
595 * AND there were NO live matches, AND we've been asked
596 * to NOT fall through to the DEFAULT realm. Therefore,
597 * we return NULL, which means "no match found".
599 if (!mainconfig.proxy_fallback && dead_match) {
600 if (mainconfig.wake_all_if_all_dead) {
602 for (cl = mainconfig.realms; cl; cl = cl->next) {
603 if(strcasecmp(cl->realm,realm) == 0) {
604 if (!accounting && !cl->active) {
608 else if (accounting &&
610 cl->acct_active = TRUE;
622 /* If we didn't find the realm 'NULL' don't return the
625 if ((strcmp(realm, "NULL")) == 0) {
630 * Didn't find anything that matched exactly, return the
631 * DEFAULT realm. We also return the DEFAULT realm if
632 * all matching realms were marked dead, and we were
633 * asked to fall through to the DEFAULT realm in this
636 return default_realm;
640 * Find a realm for a proxy reply by proxy's IP
642 * Note that we don't do anything else.
644 REALM *realm_findbyaddr(uint32_t ipaddr, int port)
649 * Note that we do NOT check for inactive realms!
651 * The purpose of this code is simply to find a matching
652 * source IP/Port pair, for a home server which is allowed
653 * to send us proxy replies. If we get a reply, then it
654 * doesn't matter if we think the realm is inactive.
656 for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
657 if (cl->ipaddr.af != AF_INET) rad_assert(0 == 1);
659 if ((ipaddr == cl->ipaddr.ipaddr.ip4addr.s_addr) &&
660 (port == cl->auth_port)) {
663 } else if ((ipaddr == cl->acct_ipaddr.ipaddr.ip4addr.s_addr) &&
664 (port == cl->acct_port)) {