import from HEAD
[freeradius.git] / src / main / files.c
1 /*
2  * files.c      Read config files into memory.
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  */
24
25 static const char rcsid[] = "$Id$";
26
27 #include "autoconf.h"
28 #include "libradius.h"
29
30 #include <sys/stat.h>
31
32 #ifdef HAVE_NETINET_IN_H
33 #       include <netinet/in.h>
34 #endif
35
36 #include <stdlib.h>
37 #include <string.h>
38 #include <netdb.h>
39 #include <ctype.h>
40 #include <fcntl.h>
41
42 #include "radiusd.h"
43
44 int maximum_proxies;
45
46 /*
47  *      Free a PAIR_LIST
48  */
49 void pairlist_free(PAIR_LIST **pl)
50 {
51         PAIR_LIST *p, *next;
52
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);
57                 next = p->next;
58                 free(p);
59         }
60         *pl = NULL;
61 }
62
63
64 #define FIND_MODE_NAME  0
65 #define FIND_MODE_REPLY 1
66
67 /*
68  *      Read the users, huntgroups or hints file.
69  *      Return a PAIR_LIST.
70  */
71 int pairlist_read(const char *file, PAIR_LIST **list, int complain)
72 {
73         FILE *fp;
74         int mode = FIND_MODE_NAME;
75         char entry[256];
76         char buffer[8192];
77         char *ptr, *s;
78         VALUE_PAIR *check_tmp;
79         VALUE_PAIR *reply_tmp;
80         PAIR_LIST *pl = NULL, *t;
81         PAIR_LIST **last = &pl;
82         int lineno = 0;
83         int old_lineno = 0;
84         LRAD_TOKEN parsecode;
85         char newfile[8192];
86
87         /*
88          *      Open the file.  The error message should be a little
89          *      more useful...
90          */
91         if ((fp = fopen(file, "r")) == NULL) {
92                 if (!complain)
93                         return -1;
94                 radlog(L_CONS|L_ERR, "Couldn't open %s for reading: %s",
95                                 file, strerror(errno));
96                 return -1;
97         }
98
99         parsecode = T_EOL;
100         /*
101          *      Read the entire file into memory for speed.
102          */
103         while(fgets(buffer, sizeof(buffer), fp) != NULL) {
104                 lineno++;
105                 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
106                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
107                         pairlist_free(&pl);
108                         return -1;
109                 }
110                 if (buffer[0] == '#' || buffer[0] == '\n') continue;
111
112                 /*
113                  *      If the line contains nothing but whitespace,
114                  *      ignore it.
115                  */
116                 ptr = buffer;
117                 while ((ptr[0] == ' ') ||
118                        (ptr[0] == '\t') ||
119                        (ptr[0] == '\r') ||
120                        (ptr[0] == '\n')) {
121                         ptr++;
122                 }
123                 if (ptr[0] == '\0') continue;
124
125 parse_again:
126                 if(mode == FIND_MODE_NAME) {
127                         /*
128                          *      Find the entry starting with the users name
129                          */
130                         if (isspace((int) buffer[0]))  {
131                                 if (parsecode != T_EOL) {
132                                         radlog(L_ERR|L_CONS,
133                                                "%s[%d]: Unexpected trailing comma for entry %s",
134                                                file, lineno, entry);
135                                         fclose(fp);
136                                         return -1;
137                                 }
138                                 continue;
139                         }
140
141                         ptr = buffer;
142                         getword(&ptr, entry, sizeof(entry));
143
144                         /*
145                          *      Include another file if we see
146                          *      $INCLUDE filename
147                          */
148                         if (strcasecmp(entry, "$include") == 0) {
149                                 while(isspace((int) *ptr))
150                                         ptr++;
151                                 s = ptr;
152                                 while (!isspace((int) *ptr))
153                                         ptr++;
154                                 *ptr = 0;
155
156                                 /*
157                                  *      If it's an absolute pathname,
158                                  *      then use it verbatim.
159                                  *
160                                  *      If not, then make the $include
161                                  *      files *relative* to the current
162                                  *      file.
163                                  */
164                                 if (*s != '/') {
165                                         strNcpy(newfile, file,
166                                                 sizeof(newfile));
167                                         ptr = strrchr(newfile, '/');
168                                         strcpy(ptr + 1, s);
169                                         s = newfile;
170                                 }
171
172                                 t = NULL;
173                                 if (pairlist_read(s, &t, 0) != 0) {
174                                         pairlist_free(&pl);
175                                         radlog(L_ERR|L_CONS,
176                                                "%s[%d]: Could not open included file %s: %s",
177                                                file, lineno, s, strerror(errno));
178                                         fclose(fp);
179                                 return -1;
180                                 }
181                                 *last = t;
182
183                                 /*
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
187                                  *      list.
188                                  */
189                                 while (*last)
190                                         last = &((*last)->next);
191                                 continue;
192                         }
193
194                         /*
195                          *      Parse the check values
196                          */
197                         check_tmp = NULL;
198                         reply_tmp = NULL;
199                         old_lineno = lineno;
200                         parsecode = userparse(ptr, &check_tmp);
201                         if (parsecode == T_INVALID) {
202                                 pairlist_free(&pl);
203                                 radlog(L_ERR|L_CONS,
204                                 "%s[%d]: Parse error (check) for entry %s: %s",
205                                         file, lineno, entry, librad_errstr);
206                                 fclose(fp);
207                                 return -1;
208                         } else if (parsecode == T_COMMA) {
209                                 radlog(L_ERR|L_CONS,
210                                        "%s[%d]: Unexpected trailing comma in check item list for entry %s",
211                                        file, lineno, entry);
212                                 fclose(fp);
213                                 return -1;
214                         }
215                         mode = FIND_MODE_REPLY;
216                         parsecode = T_COMMA;
217                 }
218                 else {
219                         if(*buffer == ' ' || *buffer == '\t') {
220                                 if (parsecode != T_COMMA) {
221                                         radlog(L_ERR|L_CONS,
222                                                "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
223                                                file, lineno, entry);
224                                         fclose(fp);
225                                         return -1;
226                                 }
227
228                                 /*
229                                  *      Parse the reply values
230                                  */
231                                 parsecode = userparse(buffer, &reply_tmp);
232                                 /* valid tokens are 1 or greater */
233                                 if (parsecode < 1) {
234                                         pairlist_free(&pl);
235                                         radlog(L_ERR|L_CONS,
236                                                "%s[%d]: Parse error (reply) for entry %s: %s",
237                                                file, lineno, entry, librad_errstr);
238                                         fclose(fp);
239                                         return -1;
240                                 }
241                         }
242                         else {
243                                 /*
244                                  *      Done with this entry...
245                                  */
246                                 t = rad_malloc(sizeof(PAIR_LIST));
247
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;
253                                 check_tmp = NULL;
254                                 reply_tmp = NULL;
255
256                                 *last = t;
257                                 last = &(t->next);
258
259                                 mode = FIND_MODE_NAME;
260                                 if (buffer[0] != 0)
261                                         goto parse_again;
262                         }
263                 }
264         }
265         /*
266          *      Make sure that we also read the last line of the file!
267          */
268         if (mode == FIND_MODE_REPLY) {
269                 buffer[0] = 0;
270                 goto parse_again;
271         }
272         fclose(fp);
273
274         *list = pl;
275         return 0;
276 }
277
278
279 /*
280  *      Debug code.
281  */
282 #if 0
283 static void debug_pair_list(PAIR_LIST *pl)
284 {
285         VALUE_PAIR *vp;
286
287         while(pl) {
288                 printf("Pair list: %s\n", pl->name);
289                 printf("** Check:\n");
290                 for(vp = pl->check; vp; vp = vp->next) {
291                         printf("    ");
292                         fprint_attr_val(stdout, vp);
293                         printf("\n");
294                 }
295                 printf("** Reply:\n");
296                 for(vp = pl->reply; vp; vp = vp->next) {
297                         printf("    ");
298                         fprint_attr_val(stdout, vp);
299                         printf("\n");
300                 }
301                 pl = pl->next;
302         }
303 }
304 #endif
305
306 #ifndef BUILDDBM /* HACK HACK */
307
308 /*
309  *      Free a REALM list.
310  */
311 void realm_free(REALM *cl)
312 {
313         REALM *next;
314
315         while(cl) {
316                 next = cl->next;
317                 free(cl);
318                 cl = next;
319         }
320 }
321
322 /*
323  *      Read the realms file.
324  */
325 int read_realms_file(const char *file)
326 {
327         FILE *fp;
328         char buffer[256];
329         char realm[256];
330         char hostnm[256];
331         char opts[256];
332         char *s, *p;
333         int lineno = 0;
334         REALM *c, **tail;
335         int got_realm = FALSE;
336
337         realm_free(mainconfig.realms);
338         mainconfig.realms = NULL;
339         tail = &mainconfig.realms;
340
341         if ((fp = fopen(file, "r")) == NULL) {
342                 /* The realms file is not mandatory.  If it exists it will
343                    be used, however, since the new style config files are
344                    more robust and flexible they are more likely to get used.
345                    So this is a non-fatal error.  */
346                 return 0;
347         }
348
349         while(fgets(buffer, 256, fp) != NULL) {
350                 lineno++;
351                 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
352                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
353                         return -1;
354                 }
355                 if (buffer[0] == '#' || buffer[0] == '\n')
356                         continue;
357                 p = buffer;
358                 if (!getword(&p, realm, sizeof(realm)) ||
359                                 !getword(&p, hostnm, sizeof(hostnm))) {
360                         radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
361                         continue;
362                 }
363
364                 got_realm = TRUE;
365                 c = rad_malloc(sizeof(REALM));
366                 memset(c, 0, sizeof(REALM));
367
368                 if ((s = strchr(hostnm, ':')) != NULL) {
369                         *s++ = 0;
370                         c->auth_port = atoi(s);
371                         c->acct_port = c->auth_port + 1;
372                 } else {
373                         c->auth_port = PW_AUTH_UDP_PORT;
374                         c->acct_port = PW_ACCT_UDP_PORT;
375                 }
376
377                 if (strcmp(hostnm, "LOCAL") == 0) {
378                         /*
379                          *      Local realms don't have an IP address,
380                          *      secret, or port.
381                          */
382                         c->acct_ipaddr = c->ipaddr = htonl(INADDR_NONE);
383                         c->secret[0] = '\0';
384                         c->auth_port = 0;
385                         c->acct_port = 0;
386
387                 } else {
388                         RADCLIENT *client;
389                         c->ipaddr = ip_getaddr(hostnm);
390                         c->acct_ipaddr = c->ipaddr;
391
392                         if (c->ipaddr == htonl(INADDR_NONE)) {
393                                 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
394                                        file, lineno, hostnm);
395                                 return -1;
396                         }
397
398                         /*
399                          *      Find the remote server in the "clients" list.
400                          *      If we can't find it, there's a big problem...
401                          */
402                         client = client_find(c->ipaddr);
403                         if (client == NULL) {
404                           radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
405                                  file, lineno, hostnm, realm);
406                           return -1;
407                         }
408                         memcpy(c->secret, client->secret, sizeof(c->secret));
409                 }
410
411                 /*
412                  *      Double-check lengths to be sure they're sane
413                  */
414                 if (strlen(hostnm) >= sizeof(c->server)) {
415                         radlog(L_ERR, "%s[%d]: server name of length %d is greater than the allowed maximum of %d.",
416                                file, lineno,
417                                (int) strlen(hostnm),
418                                (int) sizeof(c->server) - 1);
419                         return -1;
420                 }
421                 if (strlen(realm) > sizeof(c->realm)) {
422                         radlog(L_ERR, "%s[%d]: realm of length %d is greater than the allowed maximum of %d.",
423                                file, lineno,
424                                (int) strlen(realm),
425                                (int) sizeof(c->realm) - 1);
426                         return -1;
427                 }
428
429                 /*
430                  *      OK, they're sane, copy them over.
431                  */
432                 strcpy(c->realm, realm);
433                 strcpy(c->server, hostnm);
434                 c->striprealm = TRUE;
435                 c->active = TRUE;
436                 c->acct_active = TRUE;
437
438                 while (getword(&p, opts, sizeof(opts))) {
439                         if (strcmp(opts, "nostrip") == 0)
440                                 c->striprealm = FALSE;
441                         if (strstr(opts, "noacct") != NULL)
442                                 c->acct_port = 0;
443                         if (strstr(opts, "trusted") != NULL)
444                                 c->trusted = 1;
445                         if (strstr(opts, "notrealm") != NULL)
446                                 c->notrealm = 1;
447                         if (strstr(opts, "notsuffix") != NULL)
448                                 c->notrealm = 1;
449                 }
450
451                 c->next = NULL;
452                 *tail = c;
453                 tail = &c->next;
454         }
455         fclose(fp);
456
457         /*
458          *      Complain only if the realms file has content.
459          */
460         if (got_realm) {
461                 radlog(L_INFO, "Using deprecated realms file.  Support for this will go away soon.");
462         }
463
464         return 0;
465 }
466 #endif /* BUILDDBM */
467
468 /*
469  * Mark a host inactive
470  */
471 void realm_disable(uint32_t ipaddr, int port)
472 {
473         REALM *cl;
474         time_t now;
475
476         now = time(NULL);
477         for(cl = mainconfig.realms; cl; cl = cl->next) {
478                 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
479                         /*
480                          *      If we've received a reply (any reply)
481                          *      from the home server in the time spent
482                          *      re-sending this request, then don't mark
483                          *      the realm as dead.
484                          */
485                         if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
486                                 continue;
487                         }
488
489                         cl->active = FALSE;
490                         cl->wakeup = now + mainconfig.proxy_dead_time;
491                         radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
492                                 cl->server, port, cl->realm);
493                 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {
494                         if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
495                                 continue;
496                         }
497
498                         cl->acct_active = FALSE;
499                         cl->acct_wakeup = now + mainconfig.proxy_dead_time;
500                         radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
501                                 cl->acct_server, port, cl->realm);
502                 }
503         }
504 }
505
506 /*
507  *      Find a realm in the REALM list.
508  */
509 REALM *realm_find(const char *realm, int accounting)
510 {
511         REALM *cl;
512         REALM *default_realm = NULL;
513         time_t now;
514         int dead_match = 0;
515
516         now = time(NULL);
517
518         /*
519          *      If we're passed a NULL realm pointer,
520          *      then look for a "NULL" realm string.
521          */
522         if (realm == NULL) {
523                 realm = "NULL";
524         }
525
526         for (cl = mainconfig.realms; cl; cl = cl->next) {
527                 /*
528                  *      Wake up any sleeping realm.
529                  */
530                 if (cl->wakeup <= now) {
531                         cl->active = TRUE;
532                 }
533                 if (cl->acct_wakeup <= now) {
534                         cl->acct_active = TRUE;
535                 }
536
537                 /*
538                  *      Asked for auth/acct, and the auth/acct server
539                  *      is not active.  Skip it.
540                  */
541                 if ((!accounting && !cl->active) ||
542                     (accounting && !cl->acct_active)) {
543
544                         /*
545                          *      We've been asked to NOT fall through
546                          *      to the DEFAULT realm if there are
547                          *      exact matches for this realm which are
548                          *      dead.
549                          */
550                         if ((!mainconfig.proxy_fallback) &&
551                             (strcasecmp(cl->realm, realm) == 0)) {
552                                 dead_match = 1;
553                         }
554                         continue;
555                 }
556
557                 /*
558                  *      If it matches exactly, return it.
559                  *
560                  *      Note that we just want ONE live realm
561                  *      here.  We don't care about round-robin, or
562                  *      scatter techniques, as that's more properly
563                  *      the responsibility of the proxying code.
564                  */
565                 if (strcasecmp(cl->realm, realm) == 0) {
566                         return cl;
567                 }
568
569                 /*
570                  *      No default realm, try to set one.
571                  */
572                 if ((default_realm == NULL) &&
573                     (strcmp(cl->realm, "DEFAULT") == 0)) {
574                   default_realm = cl;
575                 }
576         } /* loop over all realms */
577
578         /*
579          *      There WAS one or more matches which were marked dead,
580          *      AND there were NO live matches, AND we've been asked
581          *      to NOT fall through to the DEFAULT realm.  Therefore,
582          *      we return NULL, which means "no match found".
583          */
584         if (!mainconfig.proxy_fallback && dead_match) {
585                 if (mainconfig.wake_all_if_all_dead) {
586                         REALM *rcl = NULL;
587                         for (cl = mainconfig.realms; cl; cl = cl->next) {
588                                 if(strcasecmp(cl->realm,realm) == 0) {
589                                         if (!accounting && !cl->active) {
590                                                 cl->active = TRUE;
591                                                 rcl = cl;
592                                         }
593                                         else if (accounting &&
594                                                  !cl->acct_active) {
595                                                 cl->acct_active = TRUE;
596                                                 rcl = cl;
597                                         }
598                                 }
599                         }
600                         return rcl;
601                 }
602                 else {
603                         return NULL;
604                 }
605         }
606
607         /*      If we didn't find the realm 'NULL' don't return the
608          *      DEFAULT entry.
609          */
610         if ((strcmp(realm, "NULL")) == 0) {
611           return NULL;
612         }
613
614         /*
615          *      Didn't find anything that matched exactly, return the
616          *      DEFAULT realm.  We also return the DEFAULT realm if
617          *      all matching realms were marked dead, and we were
618          *      asked to fall through to the DEFAULT realm in this
619          *      case.
620          */
621         return default_realm;
622 }
623
624 /*
625  *      Find a realm for a proxy reply by proxy's IP
626  *
627  *      Note that we don't do anything else.
628  */
629 REALM *realm_findbyaddr(uint32_t ipaddr, int port)
630 {
631         REALM *cl;
632
633         /*
634          *      Note that we do NOT check for inactive realms!
635          *
636          *      The purpose of this code is simply to find a matching
637          *      source IP/Port pair, for a home server which is allowed
638          *      to send us proxy replies.  If we get a reply, then it
639          *      doesn't matter if we think the realm is inactive.
640          */
641         for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
642                 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
643                         return cl;
644
645                 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {
646                         return cl;
647                 }
648         }
649
650         return NULL;
651 }
652