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