perl -i -npe "s/[ \t]+$//g" `find src -name "*.[ch]" -print`
[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
336         realm_free(mainconfig.realms);
337         mainconfig.realms = NULL;
338         tail = &mainconfig.realms;
339
340         if ((fp = fopen(file, "r")) == NULL) {
341                 /* The realms file is not mandatory.  If it exists it will
342                    be used, however, since the new style config files are
343                    more robust and flexible they are more likely to get used.
344                    So this is a non-fatal error.  */
345                 return 0;
346         }
347         radlog(L_INFO, "Using deprecated realms file.  Support for this will go away soon.");
348         while(fgets(buffer, 256, fp) != NULL) {
349                 lineno++;
350                 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
351                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
352                         return -1;
353                 }
354                 if (buffer[0] == '#' || buffer[0] == '\n')
355                         continue;
356                 p = buffer;
357                 if (!getword(&p, realm, sizeof(realm)) ||
358                                 !getword(&p, hostnm, sizeof(hostnm))) {
359                         radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
360                         continue;
361                 }
362
363                 c = rad_malloc(sizeof(REALM));
364                 memset(c, 0, sizeof(REALM));
365
366                 if ((s = strchr(hostnm, ':')) != NULL) {
367                         *s++ = 0;
368                         c->auth_port = atoi(s);
369                         c->acct_port = c->auth_port + 1;
370                 } else {
371                         c->auth_port = PW_AUTH_UDP_PORT;
372                         c->acct_port = PW_ACCT_UDP_PORT;
373                 }
374
375                 if (strcmp(hostnm, "LOCAL") == 0) {
376                         /*
377                          *      Local realms don't have an IP address,
378                          *      secret, or port.
379                          */
380                         c->acct_ipaddr = c->ipaddr = htonl(INADDR_NONE);
381                         c->secret[0] = '\0';
382                         c->auth_port = auth_port;
383                         c->acct_port = acct_port;
384
385                 } else {
386                         RADCLIENT *client;
387                         c->ipaddr = ip_getaddr(hostnm);
388                         c->acct_ipaddr = c->ipaddr;
389
390                         if (c->ipaddr == htonl(INADDR_NONE)) {
391                                 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
392                                        file, lineno, hostnm);
393                                 return -1;
394                         }
395
396                         /*
397                          *      Find the remote server in the "clients" list.
398                          *      If we can't find it, there's a big problem...
399                          */
400                         client = client_find(c->ipaddr);
401                         if (client == NULL) {
402                           radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
403                                  file, lineno, hostnm, realm);
404                           return -1;
405                         }
406                         memcpy(c->secret, client->secret, sizeof(c->secret));
407                 }
408
409                 /*
410                  *      Double-check lengths to be sure they're sane
411                  */
412                 if (strlen(hostnm) >= sizeof(c->server)) {
413                         radlog(L_ERR, "%s[%d]: server name of length %d is greater than the allowed maximum of %d.",
414                                file, lineno,
415                                (int) strlen(hostnm),
416                                (int) sizeof(c->server) - 1);
417                         return -1;
418                 }
419                 if (strlen(realm) > sizeof(c->realm)) {
420                         radlog(L_ERR, "%s[%d]: realm of length %d is greater than the allowed maximum of %d.",
421                                file, lineno,
422                                (int) strlen(realm),
423                                (int) sizeof(c->realm) - 1);
424                         return -1;
425                 }
426
427                 /*
428                  *      OK, they're sane, copy them over.
429                  */
430                 strcpy(c->realm, realm);
431                 strcpy(c->server, hostnm);
432                 c->striprealm = TRUE;
433                 c->active = TRUE;
434                 c->acct_active = TRUE;
435
436                 while (getword(&p, opts, sizeof(opts))) {
437                         if (strcmp(opts, "nostrip") == 0)
438                                 c->striprealm = FALSE;
439                         if (strstr(opts, "noacct") != NULL)
440                                 c->acct_port = 0;
441                         if (strstr(opts, "trusted") != NULL)
442                                 c->trusted = 1;
443                         if (strstr(opts, "notrealm") != NULL)
444                                 c->notrealm = 1;
445                         if (strstr(opts, "notsuffix") != NULL)
446                                 c->notrealm = 1;
447                 }
448
449                 c->next = NULL;
450                 *tail = c;
451                 tail = &c->next;
452         }
453         fclose(fp);
454
455         return 0;
456 }
457 #endif /* BUILDDBM */
458
459 /*
460  * Mark a host inactive
461  */
462 void realm_disable(uint32_t ipaddr, int port)
463 {
464         REALM *cl;
465         time_t now;
466
467         now = time(NULL);
468         for(cl = mainconfig.realms; cl; cl = cl->next) {
469                 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
470                         /*
471                          *      If we've received a reply (any reply)
472                          *      from the home server in the time spent
473                          *      re-sending this request, then don't mark
474                          *      the realm as dead.
475                          */
476                         if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
477                                 continue;
478                         }
479
480                         cl->active = FALSE;
481                         cl->wakeup = now + mainconfig.proxy_dead_time;
482                         radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
483                                 cl->server, port, cl->realm);
484                 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {
485                         if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
486                                 continue;
487                         }
488
489                         cl->acct_active = FALSE;
490                         cl->acct_wakeup = now + mainconfig.proxy_dead_time;
491                         radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
492                                 cl->acct_server, port, cl->realm);
493                 }
494         }
495 }
496
497 /*
498  *      Find a realm in the REALM list.
499  */
500 REALM *realm_find(const char *realm, int accounting)
501 {
502         REALM *cl;
503         REALM *default_realm = NULL;
504         time_t now;
505         int dead_match = 0;
506
507         now = time(NULL);
508
509         /*
510          *      If we're passed a NULL realm pointer,
511          *      then look for a "NULL" realm string.
512          */
513         if (realm == NULL) {
514                 realm = "NULL";
515         }
516
517         for (cl = mainconfig.realms; cl; cl = cl->next) {
518                 /*
519                  *      Wake up any sleeping realm.
520                  */
521                 if (cl->wakeup <= now) {
522                         cl->active = TRUE;
523                 }
524                 if (cl->acct_wakeup <= now) {
525                         cl->acct_active = TRUE;
526                 }
527
528                 /*
529                  *      Asked for auth/acct, and the auth/acct server
530                  *      is not active.  Skip it.
531                  */
532                 if ((!accounting && !cl->active) ||
533                     (accounting && !cl->acct_active)) {
534
535                         /*
536                          *      We've been asked to NOT fall through
537                          *      to the DEFAULT realm if there are
538                          *      exact matches for this realm which are
539                          *      dead.
540                          */
541                         if ((!mainconfig.proxy_fallback) &&
542                             (strcasecmp(cl->realm, realm) == 0)) {
543                                 dead_match = 1;
544                         }
545                         continue;
546                 }
547
548                 /*
549                  *      If it matches exactly, return it.
550                  *
551                  *      Note that we just want ONE live realm
552                  *      here.  We don't care about round-robin, or
553                  *      scatter techniques, as that's more properly
554                  *      the responsibility of the proxying code.
555                  */
556                 if (strcasecmp(cl->realm, realm) == 0) {
557                         return cl;
558                 }
559
560                 /*
561                  *      No default realm, try to set one.
562                  */
563                 if ((default_realm == NULL) &&
564                     (strcmp(cl->realm, "DEFAULT") == 0)) {
565                   default_realm = cl;
566                 }
567         } /* loop over all realms */
568
569         /*
570          *      There WAS one or more matches which were marked dead,
571          *      AND there were NO live matches, AND we've been asked
572          *      to NOT fall through to the DEFAULT realm.  Therefore,
573          *      we return NULL, which means "no match found".
574          */
575         if (!mainconfig.proxy_fallback && dead_match) {
576                 if (mainconfig.wake_all_if_all_dead) {
577                         REALM *rcl = NULL;
578                         for (cl = mainconfig.realms; cl; cl = cl->next) {
579                                 if(strcasecmp(cl->realm,realm) == 0) {
580                                         if (!accounting && !cl->active) {
581                                                 cl->active = TRUE;
582                                                 rcl = cl;
583                                         }
584                                         else if (accounting &&
585                                                  !cl->acct_active) {
586                                                 cl->acct_active = TRUE;
587                                                 rcl = cl;
588                                         }
589                                 }
590                         }
591                         return rcl;
592                 }
593                 else {
594                         return NULL;
595                 }
596         }
597
598         /*      If we didn't find the realm 'NULL' don't return the
599          *      DEFAULT entry.
600          */
601         if ((strcmp(realm, "NULL")) == 0) {
602           return NULL;
603         }
604
605         /*
606          *      Didn't find anything that matched exactly, return the
607          *      DEFAULT realm.  We also return the DEFAULT realm if
608          *      all matching realms were marked dead, and we were
609          *      asked to fall through to the DEFAULT realm in this
610          *      case.
611          */
612         return default_realm;
613 }
614
615 /*
616  *      Find a realm for a proxy reply by proxy's IP
617  *
618  *      Note that we don't do anything else.
619  */
620 REALM *realm_findbyaddr(uint32_t ipaddr, int port)
621 {
622         REALM *cl;
623
624         /*
625          *      Note that we do NOT check for inactive realms!
626          *
627          *      The purpose of this code is simply to find a matching
628          *      source IP/Port pair, for a home server which is allowed
629          *      to send us proxy replies.  If we get a reply, then it
630          *      doesn't matter if we think the realm is inactive.
631          */
632         for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
633                 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
634                         return cl;
635
636                 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {
637                         return cl;
638                 }
639         }
640
641         return NULL;
642 }
643