port fix from branch_1_1
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2006  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  */
24
25 #include <freeradius-devel/ident.h>
26 RCSID("$Id$")
27
28 #include <freeradius-devel/autoconf.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 <freeradius-devel/radiusd.h>
43 #include <freeradius-devel/rad_assert.h>
44
45 /*
46  *      Free a PAIR_LIST
47  */
48 void pairlist_free(PAIR_LIST **pl)
49 {
50         PAIR_LIST *p, *next;
51
52         for (p = *pl; p; p = next) {
53                 if (p->name) free(p->name);
54                 if (p->check) pairfree(&p->check);
55                 if (p->reply) pairfree(&p->reply);
56                 next = p->next;
57                 free(p);
58         }
59         *pl = NULL;
60 }
61
62
63 #define FIND_MODE_NAME  0
64 #define FIND_MODE_REPLY 1
65
66 /*
67  *      Read the users, huntgroups or hints file.
68  *      Return a PAIR_LIST.
69  */
70 int pairlist_read(const char *file, PAIR_LIST **list, int complain)
71 {
72         FILE *fp;
73         int mode = FIND_MODE_NAME;
74         char entry[256];
75         char buffer[8192];
76         char *ptr, *s;
77         VALUE_PAIR *check_tmp;
78         VALUE_PAIR *reply_tmp;
79         PAIR_LIST *pl = NULL, *t;
80         PAIR_LIST **last = &pl;
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                                         strlcpy(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                                 *last = t;
181
182                                 /*
183                                  *      t may be NULL, it may have one
184                                  *      entry, or it may be a linked list
185                                  *      of entries.  Go to the end of the
186                                  *      list.
187                                  */
188                                 while (*last)
189                                         last = &((*last)->next);
190                                 continue;
191                         }
192
193                         /*
194                          *      Parse the check values
195                          */
196                         check_tmp = NULL;
197                         reply_tmp = NULL;
198                         old_lineno = lineno;
199                         parsecode = userparse(ptr, &check_tmp);
200                         if (parsecode == T_OP_INVALID) {
201                                 pairlist_free(&pl);
202                                 radlog(L_ERR|L_CONS,
203                                 "%s[%d]: Parse error (check) for entry %s: %s",
204                                         file, lineno, entry, librad_errstr);
205                                 fclose(fp);
206                                 return -1;
207                         } else if (parsecode == T_COMMA) {
208                                 radlog(L_ERR|L_CONS,
209                                        "%s[%d]: Unexpected trailing comma in check item list for entry %s",
210                                        file, lineno, entry);
211                                 fclose(fp);
212                                 return -1;
213                         }
214                         mode = FIND_MODE_REPLY;
215                         parsecode = T_COMMA;
216                 }
217                 else {
218                         if(*buffer == ' ' || *buffer == '\t') {
219                                 if (parsecode != T_COMMA) {
220                                         radlog(L_ERR|L_CONS,
221                                                "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
222                                                file, lineno, entry);
223                                         fclose(fp);
224                                         return -1;
225                                 }
226
227                                 /*
228                                  *      Parse the reply values
229                                  */
230                                 parsecode = userparse(buffer, &reply_tmp);
231                                 /* valid tokens are 1 or greater */
232                                 if (parsecode < 1) {
233                                         pairlist_free(&pl);
234                                         radlog(L_ERR|L_CONS,
235                                                "%s[%d]: Parse error (reply) for entry %s: %s",
236                                                file, lineno, entry, librad_errstr);
237                                         fclose(fp);
238                                         return -1;
239                                 }
240                         }
241                         else {
242                                 /*
243                                  *      Done with this entry...
244                                  */
245                                 t = rad_malloc(sizeof(PAIR_LIST));
246
247                                 memset(t, 0, sizeof(*t));
248                                 t->name = strdup(entry);
249                                 t->check = check_tmp;
250                                 t->reply = reply_tmp;
251                                 t->lineno = old_lineno;
252                                 check_tmp = NULL;
253                                 reply_tmp = NULL;
254
255                                 *last = t;
256                                 last = &(t->next);
257
258                                 mode = FIND_MODE_NAME;
259                                 if (buffer[0] != 0)
260                                         goto parse_again;
261                         }
262                 }
263         }
264         /*
265          *      Make sure that we also read the last line of the file!
266          */
267         if (mode == FIND_MODE_REPLY) {
268                 buffer[0] = 0;
269                 goto parse_again;
270         }
271         fclose(fp);
272
273         *list = pl;
274         return 0;
275 }
276
277
278 /*
279  *      Debug code.
280  */
281 #if 0
282 static void debug_pair_list(PAIR_LIST *pl)
283 {
284         VALUE_PAIR *vp;
285
286         while(pl) {
287                 printf("Pair list: %s\n", pl->name);
288                 printf("** Check:\n");
289                 for(vp = pl->check; vp; vp = vp->next) {
290                         printf("    ");
291                         fprint_attr_val(stdout, vp);
292                         printf("\n");
293                 }
294                 printf("** Reply:\n");
295                 for(vp = pl->reply; vp; vp = vp->next) {
296                         printf("    ");
297                         fprint_attr_val(stdout, vp);
298                         printf("\n");
299                 }
300                 pl = pl->next;
301         }
302 }
303 #endif
304
305 #ifndef BUILDDBM /* HACK HACK */
306
307 /*
308  *      Free a REALM list.
309  */
310 void realm_free(REALM *cl)
311 {
312         REALM *next;
313
314         while(cl) {
315                 next = cl->next;
316                 free(cl);
317                 cl = next;
318         }
319 }
320
321 /*
322  *      Read the realms file.
323  */
324 int read_realms_file(const char *file)
325 {
326         FILE *fp;
327         char buffer[256];
328         char realm[256];
329         char hostnm[256];
330         char opts[256];
331         char *s, *p;
332         int lineno = 0;
333         REALM *c, **tail;
334         int got_realm = FALSE;
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
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                 got_realm = TRUE;
364                 c = rad_malloc(sizeof(REALM));
365                 memset(c, 0, sizeof(REALM));
366
367                 if ((s = strchr(hostnm, ':')) != NULL) {
368                         *s++ = 0;
369                         c->auth_port = atoi(s);
370                         c->acct_port = c->auth_port + 1;
371                 } else {
372                         c->auth_port = PW_AUTH_UDP_PORT;
373                         c->acct_port = PW_ACCT_UDP_PORT;
374                 }
375
376                 if (strcmp(hostnm, "LOCAL") == 0) {
377                         /*
378                          *      Local realms don't have an IP address,
379                          *      secret, or port.
380                          */
381                         c->ipaddr.af = c->acct_ipaddr.af = AF_INET;
382                         c->ipaddr.ipaddr.ip4addr.s_addr = c->acct_ipaddr.ipaddr.ip4addr.s_addr = 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
390                         if (ip_hton(hostnm, AF_INET, &c->ipaddr) < 0) {
391                                 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
392                                        file, lineno, hostnm);
393                                 return -1;
394                         }
395                         c->acct_ipaddr = c->ipaddr;
396
397                         /*
398                          *      Find the remote server in the "clients" list.
399                          *      If we can't find it, there's a big problem...
400                          */
401                         client = client_find_old(&c->ipaddr);
402                         if (client == NULL) {
403                           radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
404                                  file, lineno, hostnm, realm);
405                           return -1;
406                         }
407                         memcpy(c->secret, client->secret, sizeof(c->secret));
408                 }
409
410                 /*
411                  *      Double-check lengths to be sure they're sane
412                  */
413                 if (strlen(hostnm) >= sizeof(c->server)) {
414                         radlog(L_ERR, "%s[%d]: server name of length %u is greater than the allowed maximum of %u.",
415                                file, lineno,
416                                strlen(hostnm),
417                                sizeof(c->server) - 1);
418                         return -1;
419                 }
420                 if (strlen(realm) > sizeof(c->realm)) {
421                         radlog(L_ERR, "%s[%d]: realm of length %u is greater than the allowed maximum of %u.",
422                                file, lineno,
423                                strlen(realm),
424                                sizeof(c->realm) - 1);
425                         return -1;
426                 }
427
428                 /*
429                  *      OK, they're sane, copy them over.
430                  */
431                 strcpy(c->realm, realm);
432                 strcpy(c->server, hostnm);
433                 c->striprealm = TRUE;
434                 c->active = TRUE;
435                 c->acct_active = TRUE;
436
437                 while (getword(&p, opts, sizeof(opts))) {
438                         if (strcmp(opts, "nostrip") == 0)
439                                 c->striprealm = FALSE;
440                         if (strstr(opts, "noacct") != NULL)
441                                 c->acct_port = 0;
442                         if (strstr(opts, "trusted") != NULL)
443                                 c->trusted = 1;
444                         if (strstr(opts, "notrealm") != NULL)
445                                 c->notrealm = 1;
446                         if (strstr(opts, "notsuffix") != NULL)
447                                 c->notrealm = 1;
448                 }
449
450                 c->next = NULL;
451                 *tail = c;
452                 tail = &c->next;
453         }
454         fclose(fp);
455
456         /*
457          *      Complain only if the realms file has content.
458          */
459         if (got_realm) {
460                 radlog(L_INFO, "Using deprecated realms file.  Support for this will go away soon.");
461         }
462
463         return 0;
464 }
465 #endif /* BUILDDBM */
466
467 /*
468  * Mark a host inactive
469  */
470 void realm_disable(REQUEST *request)
471 {
472         REALM *cl;
473         time_t now;
474         lrad_ipaddr_t *ipaddr;
475         int port;
476
477         now = time(NULL);
478
479         if (!request || !request->proxy ||
480             (request->proxy->dst_ipaddr.af != AF_INET)) return;
481         
482         ipaddr = &request->proxy->dst_ipaddr;
483         port = request->proxy->dst_port;
484
485         for (cl = mainconfig.realms; cl; cl = cl->next) {
486                 if (ipaddr->af != cl->ipaddr.af) continue;
487
488                 /*
489                  *      FIXME: Switch over AF
490                  */
491                 if ((ipaddr->ipaddr.ip4addr.s_addr == cl->ipaddr.ipaddr.ip4addr.s_addr) &&
492                     (port == cl->auth_port)) {
493                         /*
494                          *      If we've received a reply (any reply)
495                          *      from the home server in the time spent
496                          *      re-sending this request, then don't mark
497                          *      the realm as dead.
498                          */
499                         if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
500                                 continue;
501                         }
502
503                         cl->active = FALSE;
504                         cl->wakeup = now + mainconfig.proxy_dead_time;
505                         radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
506                                 cl->server, port, cl->realm);
507                 } else if ((ipaddr->ipaddr.ip4addr.s_addr == cl->acct_ipaddr.ipaddr.ip4addr.s_addr) &&
508                            (port == cl->acct_port)) {
509                         if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
510                                 continue;
511                         }
512
513                         cl->acct_active = FALSE;
514                         cl->acct_wakeup = now + mainconfig.proxy_dead_time;
515                         radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
516                                 cl->acct_server, port, cl->realm);
517                 }
518         }
519 }
520
521 /*
522  *      Find a realm in the REALM list.
523  */
524 REALM *realm_find(const char *realm, int accounting)
525 {
526         REALM *cl;
527         REALM *default_realm = NULL;
528         time_t now;
529         int dead_match = 0;
530
531         now = time(NULL);
532
533         /*
534          *      If we're passed a NULL realm pointer,
535          *      then look for a "NULL" realm string.
536          */
537         if (realm == NULL) {
538                 realm = "NULL";
539         }
540
541         for (cl = mainconfig.realms; cl; cl = cl->next) {
542                 /*
543                  *      Wake up any sleeping realm.
544                  */
545                 if (cl->wakeup <= now) {
546                         cl->active = TRUE;
547                 }
548                 if (cl->acct_wakeup <= now) {
549                         cl->acct_active = TRUE;
550                 }
551
552                 /*
553                  *      Asked for auth/acct, and the auth/acct server
554                  *      is not active.  Skip it.
555                  */
556                 if ((!accounting && !cl->active) ||
557                     (accounting && !cl->acct_active)) {
558
559                         /*
560                          *      We've been asked to NOT fall through
561                          *      to the DEFAULT realm if there are
562                          *      exact matches for this realm which are
563                          *      dead.
564                          */
565                         if ((!mainconfig.proxy_fallback) &&
566                             (strcasecmp(cl->realm, realm) == 0)) {
567                                 dead_match = 1;
568                         }
569                         continue;
570                 }
571
572                 /*
573                  *      If it matches exactly, return it.
574                  *
575                  *      Note that we just want ONE live realm
576                  *      here.  We don't care about round-robin, or
577                  *      scatter techniques, as that's more properly
578                  *      the responsibility of the proxying code.
579                  */
580                 if (strcasecmp(cl->realm, realm) == 0) {
581                         return cl;
582                 }
583
584                 /*
585                  *      No default realm, try to set one.
586                  */
587                 if ((default_realm == NULL) &&
588                     (strcmp(cl->realm, "DEFAULT") == 0)) {
589                   default_realm = cl;
590                 }
591         } /* loop over all realms */
592
593         /*
594          *      There WAS one or more matches which were marked dead,
595          *      AND there were NO live matches, AND we've been asked
596          *      to NOT fall through to the DEFAULT realm.  Therefore,
597          *      we return NULL, which means "no match found".
598          */
599         if (!mainconfig.proxy_fallback && dead_match) {
600                 if (mainconfig.wake_all_if_all_dead) {
601                         REALM *rcl = NULL;
602                         for (cl = mainconfig.realms; cl; cl = cl->next) {
603                                 if(strcasecmp(cl->realm,realm) == 0) {
604                                         if (!accounting && !cl->active) {
605                                                 cl->active = TRUE;
606                                                 rcl = cl;
607                                         }
608                                         else if (accounting &&
609                                                  !cl->acct_active) {
610                                                 cl->acct_active = TRUE;
611                                                 rcl = cl;
612                                         }
613                                 }
614                         }
615                         return rcl;
616                 }
617                 else {
618                         return NULL;
619                 }
620         }
621
622         /*      If we didn't find the realm 'NULL' don't return the
623          *      DEFAULT entry.
624          */
625         if ((strcmp(realm, "NULL")) == 0) {
626           return NULL;
627         }
628
629         /*
630          *      Didn't find anything that matched exactly, return the
631          *      DEFAULT realm.  We also return the DEFAULT realm if
632          *      all matching realms were marked dead, and we were
633          *      asked to fall through to the DEFAULT realm in this
634          *      case.
635          */
636         return default_realm;
637 }
638
639 /*
640  *      Find a realm for a proxy reply by proxy's IP
641  *
642  *      Note that we don't do anything else.
643  */
644 REALM *realm_findbyaddr(uint32_t ipaddr, int port)
645 {
646         REALM *cl;
647
648         /*
649          *      Note that we do NOT check for inactive realms!
650          *
651          *      The purpose of this code is simply to find a matching
652          *      source IP/Port pair, for a home server which is allowed
653          *      to send us proxy replies.  If we get a reply, then it
654          *      doesn't matter if we think the realm is inactive.
655          */
656         for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
657                 if (cl->ipaddr.af != AF_INET) rad_assert(0 == 1);
658
659                 if ((ipaddr == cl->ipaddr.ipaddr.ip4addr.s_addr) &&
660                     (port == cl->auth_port)) {
661                         return cl;
662
663                 } else if ((ipaddr == cl->acct_ipaddr.ipaddr.ip4addr.s_addr) &&
664                            (port == cl->acct_port)) {
665                         return cl;
666                 }
667         }
668
669         return NULL;
670 }
671