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);
66 * If User-Password or Crypt-Password is set, but there is no
67 * Auth-Type, add one (kludge!).
69 static void auth_type_fixup(VALUE_PAIR **check)
76 * See if a password is present. Return right away
77 * if we see Auth-Type.
79 for (vp = *check; vp; vp = vp->next) {
80 if (vp->attribute == PW_AUTHTYPE)
82 if (vp->attribute == PW_PASSWORD) {
84 n = PW_AUTHTYPE_LOCAL;
86 if (vp->attribute == PW_CRYPT_PASSWORD) {
88 n = PW_AUTHTYPE_CRYPT;
96 * Add an Auth-Type attribute.
99 if ((vp = paircreate(PW_AUTHTYPE, PW_TYPE_INTEGER)) == NULL) {
100 radlog(L_CONS|L_ERR, "no memory");
104 vp->operator = T_OP_ADD;
105 strcpy(vp->strvalue, "Local");
110 for(vp = *check; vp; vp = vp->next) {
111 DEBUG2(" auth_type_fixup: %s [%d]", vp->name, vp->attribute);
117 #define FIND_MODE_NAME 0
118 #define FIND_MODE_REPLY 1
121 * Read the users, huntgroups or hints file.
122 * Return a PAIR_LIST.
124 int pairlist_read(const char *file, PAIR_LIST **list, int complain)
127 int mode = FIND_MODE_NAME;
131 VALUE_PAIR *check_tmp;
132 VALUE_PAIR *reply_tmp;
133 PAIR_LIST *pl = NULL, *last = NULL, *t;
136 LRAD_TOKEN parsecode;
140 * Open the file. The error message should be a little
143 if ((fp = fopen(file, "r")) == NULL) {
146 radlog(L_CONS|L_ERR, "Couldn't open %s for reading: %s",
147 file, strerror(errno));
153 * Read the entire file into memory for speed.
155 while(fgets(buffer, sizeof(buffer), fp) != NULL) {
157 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
158 radlog(L_ERR, "%s[%d]: line too long", file, lineno);
162 if (buffer[0] == '#' || buffer[0] == '\n') continue;
165 * If the line contains nothing but whitespace,
169 while ((ptr[0] == ' ') ||
175 if (ptr[0] == '\0') continue;
178 if(mode == FIND_MODE_NAME) {
180 * Find the entry starting with the users name
182 if (isspace((int) buffer[0])) {
183 if (parsecode != T_EOL) {
185 "%s[%d]: Unexpected trailing comma for entry %s",
186 file, lineno, entry);
194 getword(&ptr, entry, sizeof(entry));
197 * Include another file if we see
200 if (strcasecmp(entry, "$include") == 0) {
201 while(isspace((int) *ptr))
204 while (!isspace((int) *ptr))
209 * If it's an absolute pathname,
210 * then use it verbatim.
212 * If not, then make the $include
213 * files *relative* to the current
217 strNcpy(newfile, file,
219 ptr = strrchr(newfile, '/');
225 if (pairlist_read(s, &t, 0) != 0) {
228 "%s[%d]: Could not open included file %s: %s",
229 file, lineno, s, strerror(errno));
238 while (last && last->next)
244 * Parse the check values
249 parsecode = userparse(ptr, &check_tmp);
250 if (parsecode == T_INVALID) {
253 "%s[%d]: Parse error (check) for entry %s: %s",
254 file, lineno, entry, librad_errstr);
257 } else if (parsecode == T_COMMA) {
259 "%s[%d]: Unexpected trailing comma in check item list for entry %s",
260 file, lineno, entry);
264 mode = FIND_MODE_REPLY;
268 if(*buffer == ' ' || *buffer == '\t') {
269 if (parsecode != T_COMMA) {
271 "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
272 file, lineno, entry);
278 * Parse the reply values
280 parsecode = userparse(buffer, &reply_tmp);
281 /* valid tokens are 1 or greater */
285 "%s[%d]: Parse error (reply) for entry %s: %s",
286 file, lineno, entry, librad_errstr);
293 * Done with this entry...
295 t = rad_malloc(sizeof(PAIR_LIST));
297 auth_type_fixup(&check_tmp);
298 memset(t, 0, sizeof(*t));
299 t->name = strdup(entry);
300 t->check = check_tmp;
301 t->reply = reply_tmp;
302 t->lineno = old_lineno;
311 mode = FIND_MODE_NAME;
318 * Make sure that we also read the last line of the file!
320 if (mode == FIND_MODE_REPLY) {
335 static void debug_pair_list(PAIR_LIST *pl)
340 printf("Pair list: %s\n", pl->name);
341 printf("** Check:\n");
342 for(vp = pl->check; vp; vp = vp->next) {
344 fprint_attr_val(stdout, vp);
347 printf("** Reply:\n");
348 for(vp = pl->reply; vp; vp = vp->next) {
350 fprint_attr_val(stdout, vp);
358 #ifndef BUILDDBM /* HACK HACK */
363 void realm_free(REALM *cl)
375 * Read the realms file.
377 int read_realms_file(const char *file)
388 realm_free(mainconfig.realms);
389 mainconfig.realms = NULL;
390 tail = &mainconfig.realms;
392 if ((fp = fopen(file, "r")) == NULL) {
393 /* The realms file is not mandatory. If it exists it will
394 be used, however, since the new style config files are
395 more robust and flexible they are more likely to get used.
396 So this is a non-fatal error. */
399 while(fgets(buffer, 256, fp) != NULL) {
401 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
402 radlog(L_ERR, "%s[%d]: line too long", file, lineno);
405 if (buffer[0] == '#' || buffer[0] == '\n')
408 if (!getword(&p, realm, sizeof(realm)) ||
409 !getword(&p, hostnm, sizeof(hostnm))) {
410 radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
414 c = rad_malloc(sizeof(REALM));
415 memset(c, 0, sizeof(REALM));
417 if ((s = strchr(hostnm, ':')) != NULL) {
419 c->auth_port = atoi(s);
420 c->acct_port = c->auth_port + 1;
422 c->auth_port = PW_AUTH_UDP_PORT;
423 c->acct_port = PW_ACCT_UDP_PORT;
426 if (strcmp(hostnm, "LOCAL") == 0) {
428 * Local realms don't have an IP address,
431 c->acct_ipaddr = c->ipaddr = htonl(INADDR_NONE);
433 c->auth_port = auth_port;
434 c->acct_port = acct_port;
438 c->ipaddr = ip_getaddr(hostnm);
439 c->acct_ipaddr = c->ipaddr;
441 if (c->ipaddr == htonl(INADDR_NONE)) {
442 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
443 file, lineno, hostnm);
448 * Find the remote server in the "clients" list.
449 * If we can't find it, there's a big problem...
451 client = client_find(c->ipaddr);
452 if (client == NULL) {
453 radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
454 file, lineno, hostnm, realm);
457 memcpy(c->secret, client->secret, sizeof(c->secret));
461 * Double-check lengths to be sure they're sane
463 if (strlen(hostnm) >= sizeof(c->server)) {
464 radlog(L_ERR, "%s[%d]: server name of length %d is greater than the allowed maximum of %d.",
466 strlen(hostnm), sizeof(c->server) - 1);
469 if (strlen(realm) > sizeof(c->realm)) {
470 radlog(L_ERR, "%s[%d]: realm of length %d is greater than the allowed maximum of %d.",
472 strlen(realm), sizeof(c->realm) - 1);
477 * OK, they're sane, copy them over.
479 strcpy(c->realm, realm);
480 strcpy(c->server, hostnm);
481 c->striprealm = TRUE;
483 c->acct_active = TRUE;
485 while (getword(&p, opts, sizeof(opts))) {
486 if (strcmp(opts, "nostrip") == 0)
487 c->striprealm = FALSE;
488 if (strstr(opts, "noacct") != NULL)
490 if (strstr(opts, "trusted") != NULL)
492 if (strstr(opts, "notrealm") != NULL)
494 if (strstr(opts, "notsuffix") != NULL)
506 #endif /* BUILDDBM */
509 * Mark a host inactive
511 void realm_disable(uint32_t ipaddr, int port)
517 for(cl = mainconfig.realms; cl; cl = cl->next) {
518 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
520 * If we've received a reply (any reply)
521 * from the home server in the time spent
522 * re-sending this request, then don't mark
525 if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
530 cl->wakeup = now + mainconfig.proxy_dead_time;
531 radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
532 cl->server, port, cl->realm);
533 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {
534 if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
538 cl->acct_active = FALSE;
539 cl->acct_wakeup = now + mainconfig.proxy_dead_time;
540 radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
541 cl->server, port, cl->realm);
547 * Find a realm in the REALM list.
549 REALM *realm_find(const char *realm, int accounting)
552 REALM *default_realm = NULL;
559 * If we're passed a NULL realm pointer,
560 * then look for a "NULL" realm string.
566 for (cl = mainconfig.realms; cl; cl = cl->next) {
568 * Wake up any sleeping realm.
570 if (cl->wakeup <= now) {
573 if (cl->acct_wakeup <= now) {
574 cl->acct_active = TRUE;
578 * Asked for auth/acct, and the auth/acct server
579 * is not active. Skip it.
581 if ((!accounting && !cl->active) ||
582 (accounting && !cl->acct_active)) {
585 * We've been asked to NOT fall through
586 * to the DEFAULT realm if there are
587 * exact matches for this realm which are
590 if ((!mainconfig.proxy_fallback) &&
591 (strcasecmp(cl->realm, realm) == 0)) {
598 * If it matches exactly, return it.
600 * Note that we just want ONE live realm
601 * here. We don't care about round-robin, or
602 * scatter techniques, as that's more properly
603 * the responsibility of the proxying code.
605 if (strcasecmp(cl->realm, realm) == 0) {
610 * No default realm, try to set one.
612 if ((default_realm == NULL) &&
613 (strcmp(cl->realm, "DEFAULT") == 0)) {
616 } /* loop over all realms */
619 * There WAS one or more matches which were marked dead,
620 * AND there were NO live matches, AND we've been asked
621 * to NOT fall through to the DEFAULT realm. Therefore,
622 * we return NULL, which means "no match found".
624 if (!mainconfig.proxy_fallback && dead_match) {
628 /* If we didn't find the realm 'NULL' don't return the
631 if ((strcmp(realm, "NULL")) == 0) {
636 * Didn't find anything that matched exactly, return the
637 * DEFAULT realm. We also return the DEFAULT realm if
638 * all matching realms were marked dead, and we were
639 * asked to fall through to the DEFAULT realm in this
642 return default_realm;
646 * Find a realm for a proxy reply by proxy's IP
648 * Note that we don't do anything else.
650 REALM *realm_findbyaddr(uint32_t ipaddr, int port)
655 * Note that we do NOT check for inactive realms!
657 * The purpose of this code is simply to find a matching
658 * source IP/Port pair, for a home server which is allowed
659 * to send us proxy replies. If we get a reply, then it
660 * doesn't matter if we think the realm is inactive.
662 for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
663 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
666 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {