add new header ident.h
[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  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 <freeradius-devel/autoconf.h>
28
29 #include <sys/stat.h>
30
31 #ifdef HAVE_NETINET_IN_H
32 #       include <netinet/in.h>
33 #endif
34
35 #include <stdlib.h>
36 #include <string.h>
37 #include <netdb.h>
38 #include <ctype.h>
39 #include <fcntl.h>
40
41 #include <freeradius-devel/radiusd.h>
42 #include <freeradius-devel/rad_assert.h>
43
44 /*
45  *      Free a PAIR_LIST
46  */
47 void pairlist_free(PAIR_LIST **pl)
48 {
49         PAIR_LIST *p, *next;
50
51         for (p = *pl; p; p = next) {
52                 if (p->name) free(p->name);
53                 if (p->check) pairfree(&p->check);
54                 if (p->reply) pairfree(&p->reply);
55                 next = p->next;
56                 free(p);
57         }
58         *pl = NULL;
59 }
60
61
62 #define FIND_MODE_NAME  0
63 #define FIND_MODE_REPLY 1
64
65 /*
66  *      Read the users, huntgroups or hints file.
67  *      Return a PAIR_LIST.
68  */
69 int pairlist_read(const char *file, PAIR_LIST **list, int complain)
70 {
71         FILE *fp;
72         int mode = FIND_MODE_NAME;
73         char entry[256];
74         char buffer[8192];
75         char *ptr, *s;
76         VALUE_PAIR *check_tmp;
77         VALUE_PAIR *reply_tmp;
78         PAIR_LIST *pl = NULL, *t;
79         PAIR_LIST **last = &pl;
80         int lineno = 0;
81         int old_lineno = 0;
82         LRAD_TOKEN parsecode;
83         char newfile[8192];
84
85         /*
86          *      Open the file.  The error message should be a little
87          *      more useful...
88          */
89         if ((fp = fopen(file, "r")) == NULL) {
90                 if (!complain)
91                         return -1;
92                 radlog(L_CONS|L_ERR, "Couldn't open %s for reading: %s",
93                                 file, strerror(errno));
94                 return -1;
95         }
96
97         parsecode = T_EOL;
98         /*
99          *      Read the entire file into memory for speed.
100          */
101         while(fgets(buffer, sizeof(buffer), fp) != NULL) {
102                 lineno++;
103                 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
104                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
105                         pairlist_free(&pl);
106                         return -1;
107                 }
108                 if (buffer[0] == '#' || buffer[0] == '\n') continue;
109
110                 /*
111                  *      If the line contains nothing but whitespace,
112                  *      ignore it.
113                  */
114                 ptr = buffer;
115                 while ((ptr[0] == ' ') ||
116                        (ptr[0] == '\t') ||
117                        (ptr[0] == '\r') ||
118                        (ptr[0] == '\n')) {
119                         ptr++;
120                 }
121                 if (ptr[0] == '\0') continue;
122
123 parse_again:
124                 if(mode == FIND_MODE_NAME) {
125                         /*
126                          *      Find the entry starting with the users name
127                          */
128                         if (isspace((int) buffer[0]))  {
129                                 if (parsecode != T_EOL) {
130                                         radlog(L_ERR|L_CONS,
131                                                "%s[%d]: Unexpected trailing comma for entry %s",
132                                                file, lineno, entry);
133                                         fclose(fp);
134                                         return -1;
135                                 }
136                                 continue;
137                         }
138
139                         ptr = buffer;
140                         getword(&ptr, entry, sizeof(entry));
141
142                         /*
143                          *      Include another file if we see
144                          *      $INCLUDE filename
145                          */
146                         if (strcasecmp(entry, "$include") == 0) {
147                                 while(isspace((int) *ptr))
148                                         ptr++;
149                                 s = ptr;
150                                 while (!isspace((int) *ptr))
151                                         ptr++;
152                                 *ptr = 0;
153
154                                 /*
155                                  *      If it's an absolute pathname,
156                                  *      then use it verbatim.
157                                  *
158                                  *      If not, then make the $include
159                                  *      files *relative* to the current
160                                  *      file.
161                                  */
162                                 if (*s != '/') {
163                                         strNcpy(newfile, file,
164                                                 sizeof(newfile));
165                                         ptr = strrchr(newfile, '/');
166                                         strcpy(ptr + 1, s);
167                                         s = newfile;
168                                 }
169
170                                 t = NULL;
171                                 if (pairlist_read(s, &t, 0) != 0) {
172                                         pairlist_free(&pl);
173                                         radlog(L_ERR|L_CONS,
174                                                "%s[%d]: Could not open included file %s: %s",
175                                                file, lineno, s, strerror(errno));
176                                         fclose(fp);
177                                 return -1;
178                                 }
179                                 *last = t;
180
181                                 /*
182                                  *      t may be NULL, it may have one
183                                  *      entry, or it may be a linked list
184                                  *      of entries.  Go to the end of the
185                                  *      list.
186                                  */
187                                 while (*last)
188                                         last = &((*last)->next);
189                                 continue;
190                         }
191
192                         /*
193                          *      Parse the check values
194                          */
195                         check_tmp = NULL;
196                         reply_tmp = NULL;
197                         old_lineno = lineno;
198                         parsecode = userparse(ptr, &check_tmp);
199                         if (parsecode == T_OP_INVALID) {
200                                 pairlist_free(&pl);
201                                 radlog(L_ERR|L_CONS,
202                                 "%s[%d]: Parse error (check) for entry %s: %s",
203                                         file, lineno, entry, librad_errstr);
204                                 fclose(fp);
205                                 return -1;
206                         } else if (parsecode == T_COMMA) {
207                                 radlog(L_ERR|L_CONS,
208                                        "%s[%d]: Unexpected trailing comma in check item list for entry %s",
209                                        file, lineno, entry);
210                                 fclose(fp);
211                                 return -1;
212                         }
213                         mode = FIND_MODE_REPLY;
214                         parsecode = T_COMMA;
215                 }
216                 else {
217                         if(*buffer == ' ' || *buffer == '\t') {
218                                 if (parsecode != T_COMMA) {
219                                         radlog(L_ERR|L_CONS,
220                                                "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
221                                                file, lineno, entry);
222                                         fclose(fp);
223                                         return -1;
224                                 }
225
226                                 /*
227                                  *      Parse the reply values
228                                  */
229                                 parsecode = userparse(buffer, &reply_tmp);
230                                 /* valid tokens are 1 or greater */
231                                 if (parsecode < 1) {
232                                         pairlist_free(&pl);
233                                         radlog(L_ERR|L_CONS,
234                                                "%s[%d]: Parse error (reply) for entry %s: %s",
235                                                file, lineno, entry, librad_errstr);
236                                         fclose(fp);
237                                         return -1;
238                                 }
239                         }
240                         else {
241                                 /*
242                                  *      Done with this entry...
243                                  */
244                                 t = rad_malloc(sizeof(PAIR_LIST));
245
246                                 memset(t, 0, sizeof(*t));
247                                 t->name = strdup(entry);
248                                 t->check = check_tmp;
249                                 t->reply = reply_tmp;
250                                 t->lineno = old_lineno;
251                                 check_tmp = NULL;
252                                 reply_tmp = NULL;
253
254                                 *last = t;
255                                 last = &(t->next);
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         int got_realm = FALSE;
334
335         realm_free(mainconfig.realms);
336         mainconfig.realms = NULL;
337         tail = &mainconfig.realms;
338
339         if ((fp = fopen(file, "r")) == NULL) {
340                 /* The realms file is not mandatory.  If it exists it will
341                    be used, however, since the new style config files are
342                    more robust and flexible they are more likely to get used.
343                    So this is a non-fatal error.  */
344                 return 0;
345         }
346
347         while(fgets(buffer, 256, fp) != NULL) {
348                 lineno++;
349                 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
350                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
351                         return -1;
352                 }
353                 if (buffer[0] == '#' || buffer[0] == '\n')
354                         continue;
355                 p = buffer;
356                 if (!getword(&p, realm, sizeof(realm)) ||
357                                 !getword(&p, hostnm, sizeof(hostnm))) {
358                         radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
359                         continue;
360                 }
361
362                 got_realm = TRUE;
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->ipaddr.af = c->acct_ipaddr.af = AF_INET;
381                         c->ipaddr.ipaddr.ip4addr.s_addr = c->acct_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
382                         c->secret[0] = '\0';
383                         c->auth_port = 0;
384                         c->acct_port = 0;
385
386                 } else {
387                         RADCLIENT *client;
388
389                         if (ip_hton(hostnm, AF_INET, &c->ipaddr) < 0) {
390                                 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
391                                        file, lineno, hostnm);
392                                 return -1;
393                         }
394                         c->acct_ipaddr = c->ipaddr;
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_old(&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 %u is greater than the allowed maximum of %u.",
414                                file, lineno,
415                                strlen(hostnm),
416                                sizeof(c->server) - 1);
417                         return -1;
418                 }
419                 if (strlen(realm) > sizeof(c->realm)) {
420                         radlog(L_ERR, "%s[%d]: realm of length %u is greater than the allowed maximum of %u.",
421                                file, lineno,
422                                strlen(realm),
423                                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         /*
456          *      Complain only if the realms file has content.
457          */
458         if (got_realm) {
459                 radlog(L_INFO, "Using deprecated realms file.  Support for this will go away soon.");
460         }
461
462         return 0;
463 }
464 #endif /* BUILDDBM */
465
466 /*
467  * Mark a host inactive
468  */
469 void realm_disable(REQUEST *request)
470 {
471         REALM *cl;
472         time_t now;
473         lrad_ipaddr_t *ipaddr;
474         int port;
475
476         now = time(NULL);
477
478         if (!request || !request->proxy ||
479             (request->proxy->dst_ipaddr.af != AF_INET)) return;
480         
481         ipaddr = &request->proxy->dst_ipaddr;
482         port = request->proxy->dst_port;
483
484         for (cl = mainconfig.realms; cl; cl = cl->next) {
485                 if (ipaddr->af != cl->ipaddr.af) continue;
486
487                 /*
488                  *      FIXME: Switch over AF
489                  */
490                 if ((ipaddr->ipaddr.ip4addr.s_addr == cl->ipaddr.ipaddr.ip4addr.s_addr) &&
491                     (port == cl->auth_port)) {
492                         /*
493                          *      If we've received a reply (any reply)
494                          *      from the home server in the time spent
495                          *      re-sending this request, then don't mark
496                          *      the realm as dead.
497                          */
498                         if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
499                                 continue;
500                         }
501
502                         cl->active = FALSE;
503                         cl->wakeup = now + mainconfig.proxy_dead_time;
504                         radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
505                                 cl->server, port, cl->realm);
506                 } else if ((ipaddr->ipaddr.ip4addr.s_addr == cl->acct_ipaddr.ipaddr.ip4addr.s_addr) &&
507                            (port == cl->acct_port)) {
508                         if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
509                                 continue;
510                         }
511
512                         cl->acct_active = FALSE;
513                         cl->acct_wakeup = now + mainconfig.proxy_dead_time;
514                         radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
515                                 cl->acct_server, port, cl->realm);
516                 }
517         }
518 }
519
520 /*
521  *      Find a realm in the REALM list.
522  */
523 REALM *realm_find(const char *realm, int accounting)
524 {
525         REALM *cl;
526         REALM *default_realm = NULL;
527         time_t now;
528         int dead_match = 0;
529
530         now = time(NULL);
531
532         /*
533          *      If we're passed a NULL realm pointer,
534          *      then look for a "NULL" realm string.
535          */
536         if (realm == NULL) {
537                 realm = "NULL";
538         }
539
540         for (cl = mainconfig.realms; cl; cl = cl->next) {
541                 /*
542                  *      Wake up any sleeping realm.
543                  */
544                 if (cl->wakeup <= now) {
545                         cl->active = TRUE;
546                 }
547                 if (cl->acct_wakeup <= now) {
548                         cl->acct_active = TRUE;
549                 }
550
551                 /*
552                  *      Asked for auth/acct, and the auth/acct server
553                  *      is not active.  Skip it.
554                  */
555                 if ((!accounting && !cl->active) ||
556                     (accounting && !cl->acct_active)) {
557
558                         /*
559                          *      We've been asked to NOT fall through
560                          *      to the DEFAULT realm if there are
561                          *      exact matches for this realm which are
562                          *      dead.
563                          */
564                         if ((!mainconfig.proxy_fallback) &&
565                             (strcasecmp(cl->realm, realm) == 0)) {
566                                 dead_match = 1;
567                         }
568                         continue;
569                 }
570
571                 /*
572                  *      If it matches exactly, return it.
573                  *
574                  *      Note that we just want ONE live realm
575                  *      here.  We don't care about round-robin, or
576                  *      scatter techniques, as that's more properly
577                  *      the responsibility of the proxying code.
578                  */
579                 if (strcasecmp(cl->realm, realm) == 0) {
580                         return cl;
581                 }
582
583                 /*
584                  *      No default realm, try to set one.
585                  */
586                 if ((default_realm == NULL) &&
587                     (strcmp(cl->realm, "DEFAULT") == 0)) {
588                   default_realm = cl;
589                 }
590         } /* loop over all realms */
591
592         /*
593          *      There WAS one or more matches which were marked dead,
594          *      AND there were NO live matches, AND we've been asked
595          *      to NOT fall through to the DEFAULT realm.  Therefore,
596          *      we return NULL, which means "no match found".
597          */
598         if (!mainconfig.proxy_fallback && dead_match) {
599                 if (mainconfig.wake_all_if_all_dead) {
600                         REALM *rcl = NULL;
601                         for (cl = mainconfig.realms; cl; cl = cl->next) {
602                                 if(strcasecmp(cl->realm,realm) == 0) {
603                                         if (!accounting && !cl->active) {
604                                                 cl->active = TRUE;
605                                                 rcl = cl;
606                                         }
607                                         else if (accounting &&
608                                                  !cl->acct_active) {
609                                                 cl->acct_active = TRUE;
610                                                 rcl = cl;
611                                         }
612                                 }
613                         }
614                         return rcl;
615                 }
616                 else {
617                         return NULL;
618                 }
619         }
620
621         /*      If we didn't find the realm 'NULL' don't return the
622          *      DEFAULT entry.
623          */
624         if ((strcmp(realm, "NULL")) == 0) {
625           return NULL;
626         }
627
628         /*
629          *      Didn't find anything that matched exactly, return the
630          *      DEFAULT realm.  We also return the DEFAULT realm if
631          *      all matching realms were marked dead, and we were
632          *      asked to fall through to the DEFAULT realm in this
633          *      case.
634          */
635         return default_realm;
636 }
637
638 /*
639  *      Find a realm for a proxy reply by proxy's IP
640  *
641  *      Note that we don't do anything else.
642  */
643 REALM *realm_findbyaddr(uint32_t ipaddr, int port)
644 {
645         REALM *cl;
646
647         /*
648          *      Note that we do NOT check for inactive realms!
649          *
650          *      The purpose of this code is simply to find a matching
651          *      source IP/Port pair, for a home server which is allowed
652          *      to send us proxy replies.  If we get a reply, then it
653          *      doesn't matter if we think the realm is inactive.
654          */
655         for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
656                 if (cl->ipaddr.af != AF_INET) rad_assert(0 == 1);
657
658                 if ((ipaddr == cl->ipaddr.ipaddr.ip4addr.s_addr) &&
659                     (port == cl->auth_port)) {
660                         return cl;
661
662                 } else if ((ipaddr == cl->acct_ipaddr.ipaddr.ip4addr.s_addr) &&
663                            (port == cl->acct_port)) {
664                         return cl;
665                 }
666         }
667
668         return NULL;
669 }
670