Massive changes to configuration handling (re-arranging, mostly)
[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 #if 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 /*
65  *      Fixup a check line.
66  *      If User-Password or Crypt-Password is set, but there is no
67  *      Auth-Type, add one (kludge!).
68  */
69 static void auth_type_fixup(VALUE_PAIR **check)
70 {
71         VALUE_PAIR *vp;
72         VALUE_PAIR *c = NULL;
73         int n = 0;
74
75         /*
76          *      See if a password is present. Return right away
77          *      if we see Auth-Type.
78          */
79         for (vp = *check; vp; vp = vp->next) {
80                 if (vp->attribute == PW_AUTHTYPE)
81                         return;
82                 if (vp->attribute == PW_PASSWORD) {
83                         c = vp;
84                         n = PW_AUTHTYPE_LOCAL;
85                 }
86                 if (vp->attribute == PW_CRYPT_PASSWORD) {
87                         c = vp;
88                         n = PW_AUTHTYPE_CRYPT;
89                 }
90         }
91
92         if (c == NULL)
93                 return;
94
95         /*
96          *      Add an Auth-Type attribute.
97          *      
98          */
99         if ((vp = paircreate(PW_AUTHTYPE, PW_TYPE_INTEGER)) == NULL) {
100                 radlog(L_CONS|L_ERR, "no memory");
101                 exit(1);
102         }
103         vp->lvalue = n;
104         vp->operator = T_OP_ADD;
105         strcpy(vp->strvalue, "Local");
106
107         vp->next = *check;
108         *check = vp;
109
110         for(vp = *check; vp; vp = vp->next) {
111                 DEBUG2("  auth_type_fixup: %s [%d]", vp->name, vp->attribute);
112         }
113
114 }
115
116
117 #define FIND_MODE_NAME  0
118 #define FIND_MODE_REPLY 1
119
120 /*
121  *      Read the users, huntgroups or hints file.
122  *      Return a PAIR_LIST.
123  */
124 int pairlist_read(const char *file, PAIR_LIST **list, int complain)
125 {
126         FILE *fp;
127         int mode = FIND_MODE_NAME;
128         char entry[256];
129         char buffer[256];
130         char *ptr, *s;
131         VALUE_PAIR *check_tmp;
132         VALUE_PAIR *reply_tmp;
133         PAIR_LIST *pl = NULL, *last = NULL, *t;
134         int lineno = 0;
135         int old_lineno = 0;
136         LRAD_TOKEN parsecode;
137         char newfile[8192];
138
139         /*
140          *      Open the file.  The error message should be a little
141          *      more useful...
142          */
143         if ((fp = fopen(file, "r")) == NULL) {
144                 if (!complain) 
145                         return -1;
146                 radlog(L_CONS|L_ERR, "Couldn't open %s for reading: %s",
147                                 file, strerror(errno));
148                 return -1;
149         }
150
151         parsecode = T_EOL;
152         /*
153          *      Read the entire file into memory for speed.
154          */
155         while(fgets(buffer, sizeof(buffer), fp) != NULL) {
156                 lineno++;
157                 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
158                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
159                         pairlist_free(&pl);
160                         return -1;
161                 }
162                 if (buffer[0] == '#' || buffer[0] == '\n') continue;
163 parse_again:
164                 if(mode == FIND_MODE_NAME) {
165                         /*
166                          *      Find the entry starting with the users name
167                          */
168                         if (isspace((int) buffer[0]))  {
169                                 if (parsecode != T_EOL) {
170                                         radlog(L_ERR|L_CONS,
171                                                         "%s[%d]: Unexpected trailing comma for entry %s",
172                                                         file, lineno, entry);
173                                         fclose(fp);
174                                         return -1;
175                                 }
176                                 continue;
177                         }
178
179                         ptr = buffer;
180                         getword(&ptr, entry, sizeof(entry));
181
182                         /*
183                          *      Include another file if we see
184                          *      $INCLUDE filename
185                          */
186                         if (strcasecmp(entry, "$include") == 0) {
187                                 while(isspace((int) *ptr))
188                                         ptr++;
189                                 s = ptr;
190                                 while (!isspace((int) *ptr))
191                                         ptr++;
192                                 *ptr = 0;
193
194                                 /*
195                                  *      If it's an absolute pathname,
196                                  *      then use it verbatim.
197                                  *
198                                  *      If not, then make the $include
199                                  *      files *relative* to the current
200                                  *      file.
201                                  */
202                                 if (*s != '/') {
203                                         strNcpy(newfile, file,
204                                                 sizeof(newfile));
205                                         ptr = strrchr(newfile, '/');
206                                         strcpy(ptr + 1, s);
207                                         s = newfile;
208                                 }
209
210                                 t = NULL;
211                                 if (pairlist_read(s, &t, 0) != 0) {
212                                         pairlist_free(&pl);
213                                         radlog(L_ERR|L_CONS,
214                                                         "%s[%d]: Could not open included file %s: %s",
215                                                         file, lineno, s, strerror(errno));
216                                         fclose(fp);
217                                 return -1;
218                                 }
219                                 if (last)
220                                         last->next = t;
221                                 else
222                                         pl = t;
223                                 last = t;
224                                 while (last && last->next)
225                                         last = last->next;
226                                 continue;
227                         }
228
229                         /*
230                          *      Parse the check values
231                          */
232                         check_tmp = NULL;
233                         reply_tmp = NULL;
234                         old_lineno = lineno;
235                         parsecode = userparse(ptr, &check_tmp);
236                         if (parsecode == T_INVALID) {
237                                 pairlist_free(&pl);
238                                 radlog(L_ERR|L_CONS,
239                                 "%s[%d]: Parse error (check) for entry %s: %s",
240                                         file, lineno, entry, librad_errstr);
241                                 fclose(fp);
242                                 return -1;
243                         } else if (parsecode == T_COMMA) {
244                                 radlog(L_ERR|L_CONS,
245                                                 "%s[%d]: Unexpected trailing comma in check item list for entry %s",
246                                                 file, lineno, entry);
247                                 fclose(fp);
248                                 return -1;
249                         }
250                         mode = FIND_MODE_REPLY;
251                         parsecode = T_COMMA;
252                 }
253                 else {
254                         if(*buffer == ' ' || *buffer == '\t') {
255                                 if (parsecode != T_COMMA) {
256                                         radlog(L_ERR|L_CONS,
257                                                         "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
258                                                         file, lineno, entry);
259                                         fclose(fp);
260                                         return -1;
261                                 }
262
263                                 /*
264                                  *      Parse the reply values
265                                  */
266                                 parsecode = userparse(buffer, &reply_tmp);
267                                 /* valid tokens are 1 or greater */
268                                 if (parsecode < 1) {
269                                         pairlist_free(&pl);
270                                         radlog(L_ERR|L_CONS,
271                                                         "%s[%d]: Parse error (reply) for entry %s: %s",
272                                                         file, lineno, entry, librad_errstr);
273                                         fclose(fp);
274                                         return -1;
275                                 }
276                         }
277                         else {
278                                 /*
279                                  *      Done with this entry...
280                                  */
281                                 t = rad_malloc(sizeof(PAIR_LIST));
282
283                                 auth_type_fixup(&check_tmp);
284                                 memset(t, 0, sizeof(*t));
285                                 t->name = strdup(entry);
286                                 t->check = check_tmp;
287                                 t->reply = reply_tmp;
288                                 t->lineno = old_lineno;
289                                 check_tmp = NULL;
290                                 reply_tmp = NULL;
291                                 if (last)
292                                         last->next = t;
293                                 else
294                                         pl = t;
295                                 last = t;
296
297                                 mode = FIND_MODE_NAME;
298                                 if (buffer[0] != 0)
299                                         goto parse_again;
300                         }
301                 }
302         }
303         /*
304          *      Make sure that we also read the last line of the file!
305          */
306         if (mode == FIND_MODE_REPLY) {
307                 buffer[0] = 0;
308                 goto parse_again;
309         }
310         fclose(fp);
311
312         *list = pl;
313         return 0;
314 }
315
316
317 /*
318  *      Debug code.
319  */
320 #if 0
321 static void debug_pair_list(PAIR_LIST *pl)
322 {
323         VALUE_PAIR *vp;
324
325         while(pl) {
326                 printf("Pair list: %s\n", pl->name);
327                 printf("** Check:\n");
328                 for(vp = pl->check; vp; vp = vp->next) {
329                         printf("    ");
330                         fprint_attr_val(stdout, vp);
331                         printf("\n");
332                 }
333                 printf("** Reply:\n");
334                 for(vp = pl->reply; vp; vp = vp->next) {
335                         printf("    ");
336                         fprint_attr_val(stdout, vp);
337                         printf("\n");
338                 }
339                 pl = pl->next;
340         }
341 }
342 #endif
343
344 #ifndef BUILDDBM /* HACK HACK */
345
346 /*
347  *      Free a REALM list.
348  */
349 void realm_free(REALM *cl)
350 {
351         REALM *next;
352
353         while(cl) {
354                 next = cl->next;
355                 free(cl);
356                 cl = next;
357         }
358 }
359
360 /*
361  *      Read the realms file.
362  */
363 int read_realms_file(const char *file)
364 {
365         FILE *fp;
366         char buffer[256];
367         char realm[256];
368         char hostnm[256];
369         char opts[256];
370         char *s, *p;
371         int lineno = 0;
372         REALM *c, **tail;
373
374         realm_free(mainconfig.realms);
375         mainconfig.realms = NULL;
376         tail = &mainconfig.realms;
377
378         if ((fp = fopen(file, "r")) == NULL) {
379                 /* The realms file is not mandatory.  If it exists it will
380                    be used, however, since the new style config files are
381                    more robust and flexible they are more likely to get used.
382                    So this is a non-fatal error.  */
383                 return 0;
384         }
385         while(fgets(buffer, 256, fp) != NULL) {
386                 lineno++;
387                 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
388                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
389                         return -1;
390                 }
391                 if (buffer[0] == '#' || buffer[0] == '\n')
392                         continue;
393                 p = buffer;
394                 if (!getword(&p, realm, sizeof(realm)) ||
395                                 !getword(&p, hostnm, sizeof(hostnm))) {
396                         radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
397                         continue;
398                 }
399
400                 c = rad_malloc(sizeof(REALM));
401                 memset(c, 0, sizeof(REALM));
402
403                 if ((s = strchr(hostnm, ':')) != NULL) {
404                         *s++ = 0;
405                         c->auth_port = atoi(s);
406                         c->acct_port = c->auth_port + 1;
407                 } else {
408                         c->auth_port = PW_AUTH_UDP_PORT;
409                         c->acct_port = PW_ACCT_UDP_PORT;
410                 }
411
412                 if (strcmp(hostnm, "LOCAL") == 0) {
413                         /*
414                          *      Local realms don't have an IP address,
415                          *      secret, or port.
416                          */
417                         c->acct_ipaddr = c->ipaddr = htonl(INADDR_NONE);
418                         c->secret[0] = '\0';
419                         c->auth_port = auth_port;
420                         c->acct_port = acct_port;
421
422                 } else {
423                         RADCLIENT *client;
424                         c->ipaddr = ip_getaddr(hostnm);
425                         c->acct_ipaddr = c->ipaddr;
426
427                         if (c->ipaddr == htonl(INADDR_NONE)) {
428                                 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
429                                        file, lineno, hostnm);
430                                 return -1;
431                         }
432
433                         /*
434                          *      Find the remote server in the "clients" list.
435                          *      If we can't find it, there's a big problem...
436                          */
437                         client = client_find(c->ipaddr);
438                         if (client == NULL) {
439                           radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
440                                  file, lineno, hostnm, realm);
441                           return -1;
442                         }
443                         memcpy(c->secret, client->secret, sizeof(c->secret));
444                 }
445
446                 /*
447                  *      Double-check lengths to be sure they're sane
448                  */
449                 if (strlen(hostnm) >= sizeof(c->server)) {
450                         radlog(L_ERR, "%s[%d]: server name of length %d is greater than the allowed maximum of %d.",
451                                         file, lineno,
452                                         strlen(hostnm), sizeof(c->server) - 1);
453                         return -1;
454                 }
455                 if (strlen(realm) > sizeof(c->realm)) {
456                         radlog(L_ERR, "%s[%d]: realm of length %d is greater than the allowed maximum of %d.",
457                                         file, lineno,
458                                         strlen(realm), sizeof(c->realm) - 1);
459                         return -1;
460                 }
461
462                 /*
463                  *      OK, they're sane, copy them over.
464                  */
465                 strcpy(c->realm, realm);
466                 strcpy(c->server, hostnm);
467                 c->striprealm = TRUE;
468                 c->active = TRUE;
469                 c->acct_active = TRUE;
470
471                 while (getword(&p, opts, sizeof(opts))) {
472                         if (strcmp(opts, "nostrip") == 0)
473                                 c->striprealm = FALSE;
474                         if (strstr(opts, "noacct") != NULL)
475                                 c->acct_port = 0;
476                         if (strstr(opts, "trusted") != NULL)
477                                 c->trusted = 1;
478                         if (strstr(opts, "notrealm") != NULL)
479                                 c->notrealm = 1;
480                         if (strstr(opts, "notsuffix") != NULL)
481                                 c->notrealm = 1;
482                 }
483
484                 c->next = NULL;
485                 *tail = c;
486                 tail = &c->next;
487         }
488         fclose(fp);
489
490         return 0;
491 }
492 #endif /* BUILDDBM */
493
494 /*
495  * Mark a host inactive
496  */
497 void realm_disable(uint32_t ipaddr, int port)
498 {
499         REALM *cl;
500         time_t now;
501
502         now = time(NULL);
503         for(cl = mainconfig.realms; cl; cl = cl->next)
504                 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
505                         cl->active = FALSE;
506                         cl->wakeup = now + mainconfig.proxy_dead_time;
507                         radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
508                                 cl->server, port, cl->realm);
509                 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {
510                         cl->acct_active = FALSE;
511                         cl->acct_wakeup = now + mainconfig.proxy_dead_time;
512                         radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
513                                 cl->server, port, cl->realm);
514                 }
515 }
516
517 /*
518  *      Find a realm in the REALM list.
519  */
520 REALM *realm_find(const char *realm, int acct)
521 {
522         REALM *cl, *realmptr;
523         REALM *default_realm = NULL;
524         REALM *rr_array[maximum_proxies];
525         time_t now;
526         int dead_match = 0, efform = 0, p = 0, i = 0;
527         static int proxy_return;
528
529         now = time(NULL);
530         /*
531          *      If we're passed a NULL realm pointer,
532          *      then look for a "NULL" realm string.
533          */
534         if (realm == NULL) {
535                 realm = "NULL";
536         }
537         
538         for (cl = mainconfig.realms; cl; cl = cl->next) {
539                 /*
540                  *      Wake up any sleeping realm.
541                  */
542                 if (cl->wakeup <= now) {
543                         cl->active = TRUE;
544                 }
545                 if (cl->acct_wakeup <= now) {
546                         cl->acct_active = TRUE;
547                 }
548
549                 /*
550                  *      Asked for auth/acct, and the auth/acct server
551                  *      is not active.  Skip it.
552                  */
553                 if ((!acct && !cl->active) ||
554                     (acct && !cl->acct_active)) {
555
556                         /*
557                          *      We've been asked to NOT fall through
558                          *      to the DEFAULT realm if there are
559                          *      exact matches for this realm which are
560                          *      dead.
561                          */
562                         if ((!mainconfig.proxy_fallback) &&
563                             (strcasecmp(cl->realm, realm) == 0)) {
564                                 dead_match = 1;
565                         }
566                         continue;
567                 }
568
569                 /*
570                  *      If it matches exactly, return it.
571                  */
572                 if (strcasecmp(cl->realm, realm) == 0) {
573                         /*
574                          *    Seperate parsing of list for round-robin
575                          */
576                         if(cl->total > 1 && cl->ldflag == 1) {
577                                 /*Get all of the realms from initial list*/
578                                 for(i = 1; i <= cl->total; i++) {
579                                         for(realmptr = mainconfig.realms; realmptr; 
580                                             realmptr = realmptr->next) {
581                                                 if((strcasecmp(realmptr->realm,
582                                                    cl->realm)) == 0 &&
583                                                    (realmptr->node == i)) {
584                                                         rr_array[i] = realmptr;
585                                                         if(realmptr->active == TRUE)
586                                                                 p++;
587                                                         break;
588                                                 }
589                                                 else {
590                                                         continue;
591                                                 }
592                                         }
593                                 }
594                                 /*if only one realm active, return it*/
595                                 if(p == 1) {
596                                         for (p = 1; p < i; p++) {
597                                                 realmptr = rr_array[p];
598                                                 if(realmptr->active == TRUE) {
599                                                         return realmptr;
600                                                 }
601                                         }
602                                 }
603                                 for(p = 1; p < i; p++) {
604                                         realmptr = rr_array[p];
605                                         if(realmptr->chose == 1) {
606                                                 efform++;
607                                         }
608                                 }
609
610                                 /*    
611                                  *    if none available, 
612                                  *    and last realm has
613                                  *    been returned as access-reply
614                                  */
615
616                                 if(efform == cl->total && proxy_return == 0) {
617                                         for(p = 1; p < i; p++) {
618                                                 realmptr = rr_array[p];
619                                                 realmptr->chose = 0;
620                                         }
621                                 }
622
623                                 /*    
624                                  *    if this realm hasn't been chosen and 
625                                  *    this is not being returned for
626                                  *    access reply
627                                  *    else if the realm has been chosen 
628                                  *    and this is for access-reply
629                                  *    else this realm is dead (reset if
630                                  *    this is the last realm in the list)
631                                  */
632
633                                 for(p = 1; p < i; p++) {
634                                         realmptr = rr_array[p];
635                                         if((realmptr->chose == 0) &&
636                                            (proxy_return < realmptr->node) &&
637                                            (realmptr->active == TRUE && realmptr->acct_active == TRUE)
638                                         ) {
639                                                 proxy_return = realmptr->node;
640                                                 realmptr->chose = 1;
641                                                 return realmptr;
642                                         }
643                                         else if ((realmptr->chose == 1) &&
644                                                  (proxy_return == realmptr->node)
645                                         ) {
646                                                 if(proxy_return + 1 > realmptr->total) {
647                                                         proxy_return = 0;
648                                                 }
649                                                 realmptr->chose = 0;
650                                                 return realmptr;
651                                         }
652                                         else 
653                                         {
654                                                 if((realmptr->active == FALSE ||
655                                                     realmptr->acct_active == FALSE ) &&
656                                                    (realmptr->node == realmptr->total)
657                                                 ) {
658                                                         proxy_return = 0;
659                                                 }
660                                                 continue;
661                                         }
662                                 }
663                         }
664                         
665                         /*    
666                          *    else we have single 
667                          *    realm or non-rr realm
668                          */
669                         
670                         else {
671                                 return cl;
672                         }
673                 }
674
675                 /*
676                  *      No default realm, try to set one.
677                  */
678                 if ((default_realm == NULL) &&
679                     (strcmp(cl->realm, "DEFAULT") == 0)) {
680                   default_realm = cl;
681                 }
682         } /* loop over all realms */
683
684         /*
685          *      There WAS one or more matches which were marked dead,
686          *      AND there were NO live matches, AND we've been asked
687          *      to NOT fall through to the DEFAULT realm.  Therefore,
688          *      we return NULL, which means "no match found".
689          */
690         if (!mainconfig.proxy_fallback && dead_match) {
691                 return NULL;
692         }
693
694         /*
695          *      Didn't find anything that matched exactly, return the
696          *      DEFAULT realm.  We also return the DEFAULT realm if
697          *      all matching realms were marked dead, and we were
698          *      asked to fall through to the DEFAULT realm in this
699          *      case.
700          */
701         return default_realm;
702 }
703
704 /*
705  *      Find a realm for a proxy reply by proxy's IP
706  */
707 REALM *realm_findbyaddr(uint32_t ipaddr, int port)
708 {
709         REALM *cl;
710
711         /*
712          *      Note that we do NOT check for inactive realms!
713          *
714          *      If we get a packet from an end server, then we mark it
715          *      as active, and return the realm.
716          */
717         for(cl = mainconfig.realms; cl != NULL; cl = cl->next)
718                 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
719                         cl->active = TRUE;
720                         return cl;
721                 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {
722                         cl->acct_active = TRUE;
723                         return cl;
724                 }
725
726         return NULL;
727 }
728
729 /*
730  *    Catch any errors in set-up of RR list or exceeding max_proxies
731  */
732 void check_proxies(int max_config) {
733         REALM *next, *rptr;
734         if(max_config > 0) {
735                 maximum_proxies = max_config;
736                 for(rptr=mainconfig.realms; rptr; rptr = rptr->next) {
737                         if(rptr->total > max_config || rptr->total >= 1) {
738                                 for(next = mainconfig.realms; next; next = next->next) {
739                                         if(next->ldflag != 0 &&
740                                            next->ldflag != 1) {
741                                                 radlog(L_ERR,
742                                                     "Invalid load balance flag on realm: %s entry %d\n",
743                                                      next->realm, next->node);
744                                                 exit(1);
745                                         }
746                                         else if((strcasecmp(rptr->realm, next->realm) == 0) && 
747                                             rptr->ldflag != next->ldflag){ 
748                                                radlog(L_ERR,
749                                                    "Inconsistent ldflag for realm: %s entries %d and %d.\n", 
750                                                     rptr->realm, rptr->node, next->node);
751                                                exit(1);
752                                         }
753                                         else if (next->total > max_config) {
754                                             radlog(L_ERR,
755                                                 "Too many entries (%d) for realm: %s. Max is %d.\n",
756                                                  next->total, next->realm, max_config);
757                                             exit(1);
758                                         }
759                                 }
760                         }
761                 }
762                 maximum_proxies++;
763         }
764 }