When reading the 'users' file, if the line contains nothing
[freeradius.git] / src / main / files.c
1 /*
2  * files.c      Read config files into memory.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
22  * Copyright 2000  Alan DeKok <aland@ox.org>
23  */
24
25 static const char rcsid[] = "$Id$";
26
27 #include "autoconf.h"
28 #include "libradius.h"
29
30 #include <sys/stat.h>
31
32 #if HAVE_NETINET_IN_H
33 #       include <netinet/in.h>
34 #endif
35
36 #include <stdlib.h>
37 #include <string.h>
38 #include <netdb.h>
39 #include <ctype.h>
40 #include <fcntl.h>
41
42 #include "radiusd.h"
43
44 int maximum_proxies;
45
46 /*
47  *      Free a PAIR_LIST
48  */
49 void pairlist_free(PAIR_LIST **pl)
50 {
51         PAIR_LIST *p, *next;
52
53         for (p = *pl; p; p = next) {
54                 if (p->name) free(p->name);
55                 if (p->check) pairfree(&p->check);
56                 if (p->reply) pairfree(&p->reply);
57                 next = p->next;
58                 free(p);
59         }
60         *pl = NULL;
61 }
62
63
64 /*
65  *      Fixup a check line.
66  *      If User-Password or Crypt-Password is set, but there is no
67  *      Auth-Type, add one (kludge!).
68  */
69 static void auth_type_fixup(VALUE_PAIR **check)
70 {
71         VALUE_PAIR *vp;
72         VALUE_PAIR *c = NULL;
73         int n = 0;
74
75         /*
76          *      See if a password is present. Return right away
77          *      if we see Auth-Type.
78          */
79         for (vp = *check; vp; vp = vp->next) {
80                 if (vp->attribute == PW_AUTHTYPE)
81                         return;
82                 if (vp->attribute == PW_PASSWORD) {
83                         c = vp;
84                         n = PW_AUTHTYPE_LOCAL;
85                 }
86                 if (vp->attribute == PW_CRYPT_PASSWORD) {
87                         c = vp;
88                         n = PW_AUTHTYPE_CRYPT;
89                 }
90         }
91
92         if (c == NULL)
93                 return;
94
95         /*
96          *      Add an Auth-Type attribute.
97          *      
98          */
99         if ((vp = paircreate(PW_AUTHTYPE, PW_TYPE_INTEGER)) == NULL) {
100                 radlog(L_CONS|L_ERR, "no memory");
101                 exit(1);
102         }
103         vp->lvalue = n;
104         vp->operator = T_OP_ADD;
105         strcpy(vp->strvalue, "Local");
106
107         vp->next = *check;
108         *check = vp;
109
110         for(vp = *check; vp; vp = vp->next) {
111                 DEBUG2("  auth_type_fixup: %s [%d]", vp->name, vp->attribute);
112         }
113
114 }
115
116
117 #define FIND_MODE_NAME  0
118 #define FIND_MODE_REPLY 1
119
120 /*
121  *      Read the users, huntgroups or hints file.
122  *      Return a PAIR_LIST.
123  */
124 int pairlist_read(const char *file, PAIR_LIST **list, int complain)
125 {
126         FILE *fp;
127         int mode = FIND_MODE_NAME;
128         char entry[256];
129         char buffer[8192];
130         char *ptr, *s;
131         VALUE_PAIR *check_tmp;
132         VALUE_PAIR *reply_tmp;
133         PAIR_LIST *pl = NULL, *last = NULL, *t;
134         int lineno = 0;
135         int old_lineno = 0;
136         LRAD_TOKEN parsecode;
137         char newfile[8192];
138
139         /*
140          *      Open the file.  The error message should be a little
141          *      more useful...
142          */
143         if ((fp = fopen(file, "r")) == NULL) {
144                 if (!complain) 
145                         return -1;
146                 radlog(L_CONS|L_ERR, "Couldn't open %s for reading: %s",
147                                 file, strerror(errno));
148                 return -1;
149         }
150
151         parsecode = T_EOL;
152         /*
153          *      Read the entire file into memory for speed.
154          */
155         while(fgets(buffer, sizeof(buffer), fp) != NULL) {
156                 lineno++;
157                 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
158                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
159                         pairlist_free(&pl);
160                         return -1;
161                 }
162                 if (buffer[0] == '#' || buffer[0] == '\n') continue;
163
164                 /*
165                  *      If the line contains nothing but whitespace,
166                  *      ignore it.
167                  */
168                 ptr = buffer;
169                 while ((ptr[0] == ' ') ||
170                        (ptr[0] == '\t') ||
171                        (ptr[0] == '\r') ||
172                        (ptr[0] == '\n')) {
173                         ptr++;
174                 }
175                 if (ptr[0] == '\0') continue;
176
177 parse_again:
178                 if(mode == FIND_MODE_NAME) {
179                         /*
180                          *      Find the entry starting with the users name
181                          */
182                         if (isspace((int) buffer[0]))  {
183                                 if (parsecode != T_EOL) {
184                                         radlog(L_ERR|L_CONS,
185                                                         "%s[%d]: Unexpected trailing comma for entry %s",
186                                                         file, lineno, entry);
187                                         fclose(fp);
188                                         return -1;
189                                 }
190                                 continue;
191                         }
192
193                         ptr = buffer;
194                         getword(&ptr, entry, sizeof(entry));
195
196                         /*
197                          *      Include another file if we see
198                          *      $INCLUDE filename
199                          */
200                         if (strcasecmp(entry, "$include") == 0) {
201                                 while(isspace((int) *ptr))
202                                         ptr++;
203                                 s = ptr;
204                                 while (!isspace((int) *ptr))
205                                         ptr++;
206                                 *ptr = 0;
207
208                                 /*
209                                  *      If it's an absolute pathname,
210                                  *      then use it verbatim.
211                                  *
212                                  *      If not, then make the $include
213                                  *      files *relative* to the current
214                                  *      file.
215                                  */
216                                 if (*s != '/') {
217                                         strNcpy(newfile, file,
218                                                 sizeof(newfile));
219                                         ptr = strrchr(newfile, '/');
220                                         strcpy(ptr + 1, s);
221                                         s = newfile;
222                                 }
223
224                                 t = NULL;
225                                 if (pairlist_read(s, &t, 0) != 0) {
226                                         pairlist_free(&pl);
227                                         radlog(L_ERR|L_CONS,
228                                                         "%s[%d]: Could not open included file %s: %s",
229                                                         file, lineno, s, strerror(errno));
230                                         fclose(fp);
231                                 return -1;
232                                 }
233                                 if (last)
234                                         last->next = t;
235                                 else
236                                         pl = t;
237                                 last = t;
238                                 while (last && last->next)
239                                         last = last->next;
240                                 continue;
241                         }
242
243                         /*
244                          *      Parse the check values
245                          */
246                         check_tmp = NULL;
247                         reply_tmp = NULL;
248                         old_lineno = lineno;
249                         parsecode = userparse(ptr, &check_tmp);
250                         if (parsecode == T_INVALID) {
251                                 pairlist_free(&pl);
252                                 radlog(L_ERR|L_CONS,
253                                 "%s[%d]: Parse error (check) for entry %s: %s",
254                                         file, lineno, entry, librad_errstr);
255                                 fclose(fp);
256                                 return -1;
257                         } else if (parsecode == T_COMMA) {
258                                 radlog(L_ERR|L_CONS,
259                                                 "%s[%d]: Unexpected trailing comma in check item list for entry %s",
260                                                 file, lineno, entry);
261                                 fclose(fp);
262                                 return -1;
263                         }
264                         mode = FIND_MODE_REPLY;
265                         parsecode = T_COMMA;
266                 }
267                 else {
268                         if(*buffer == ' ' || *buffer == '\t') {
269                                 if (parsecode != T_COMMA) {
270                                         radlog(L_ERR|L_CONS,
271                                                         "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
272                                                         file, lineno, entry);
273                                         fclose(fp);
274                                         return -1;
275                                 }
276
277                                 /*
278                                  *      Parse the reply values
279                                  */
280                                 parsecode = userparse(buffer, &reply_tmp);
281                                 /* valid tokens are 1 or greater */
282                                 if (parsecode < 1) {
283                                         pairlist_free(&pl);
284                                         radlog(L_ERR|L_CONS,
285                                                         "%s[%d]: Parse error (reply) for entry %s: %s",
286                                                         file, lineno, entry, librad_errstr);
287                                         fclose(fp);
288                                         return -1;
289                                 }
290                         }
291                         else {
292                                 /*
293                                  *      Done with this entry...
294                                  */
295                                 t = rad_malloc(sizeof(PAIR_LIST));
296
297                                 auth_type_fixup(&check_tmp);
298                                 memset(t, 0, sizeof(*t));
299                                 t->name = strdup(entry);
300                                 t->check = check_tmp;
301                                 t->reply = reply_tmp;
302                                 t->lineno = old_lineno;
303                                 check_tmp = NULL;
304                                 reply_tmp = NULL;
305                                 if (last)
306                                         last->next = t;
307                                 else
308                                         pl = t;
309                                 last = t;
310
311                                 mode = FIND_MODE_NAME;
312                                 if (buffer[0] != 0)
313                                         goto parse_again;
314                         }
315                 }
316         }
317         /*
318          *      Make sure that we also read the last line of the file!
319          */
320         if (mode == FIND_MODE_REPLY) {
321                 buffer[0] = 0;
322                 goto parse_again;
323         }
324         fclose(fp);
325
326         *list = pl;
327         return 0;
328 }
329
330
331 /*
332  *      Debug code.
333  */
334 #if 0
335 static void debug_pair_list(PAIR_LIST *pl)
336 {
337         VALUE_PAIR *vp;
338
339         while(pl) {
340                 printf("Pair list: %s\n", pl->name);
341                 printf("** Check:\n");
342                 for(vp = pl->check; vp; vp = vp->next) {
343                         printf("    ");
344                         fprint_attr_val(stdout, vp);
345                         printf("\n");
346                 }
347                 printf("** Reply:\n");
348                 for(vp = pl->reply; vp; vp = vp->next) {
349                         printf("    ");
350                         fprint_attr_val(stdout, vp);
351                         printf("\n");
352                 }
353                 pl = pl->next;
354         }
355 }
356 #endif
357
358 #ifndef BUILDDBM /* HACK HACK */
359
360 /*
361  *      Free a REALM list.
362  */
363 void realm_free(REALM *cl)
364 {
365         REALM *next;
366
367         while(cl) {
368                 next = cl->next;
369                 free(cl);
370                 cl = next;
371         }
372 }
373
374 /*
375  *      Read the realms file.
376  */
377 int read_realms_file(const char *file)
378 {
379         FILE *fp;
380         char buffer[256];
381         char realm[256];
382         char hostnm[256];
383         char opts[256];
384         char *s, *p;
385         int lineno = 0;
386         REALM *c, **tail;
387
388         realm_free(mainconfig.realms);
389         mainconfig.realms = NULL;
390         tail = &mainconfig.realms;
391
392         if ((fp = fopen(file, "r")) == NULL) {
393                 /* The realms file is not mandatory.  If it exists it will
394                    be used, however, since the new style config files are
395                    more robust and flexible they are more likely to get used.
396                    So this is a non-fatal error.  */
397                 return 0;
398         }
399         while(fgets(buffer, 256, fp) != NULL) {
400                 lineno++;
401                 if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
402                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
403                         return -1;
404                 }
405                 if (buffer[0] == '#' || buffer[0] == '\n')
406                         continue;
407                 p = buffer;
408                 if (!getword(&p, realm, sizeof(realm)) ||
409                                 !getword(&p, hostnm, sizeof(hostnm))) {
410                         radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
411                         continue;
412                 }
413
414                 c = rad_malloc(sizeof(REALM));
415                 memset(c, 0, sizeof(REALM));
416
417                 if ((s = strchr(hostnm, ':')) != NULL) {
418                         *s++ = 0;
419                         c->auth_port = atoi(s);
420                         c->acct_port = c->auth_port + 1;
421                 } else {
422                         c->auth_port = PW_AUTH_UDP_PORT;
423                         c->acct_port = PW_ACCT_UDP_PORT;
424                 }
425
426                 if (strcmp(hostnm, "LOCAL") == 0) {
427                         /*
428                          *      Local realms don't have an IP address,
429                          *      secret, or port.
430                          */
431                         c->acct_ipaddr = c->ipaddr = htonl(INADDR_NONE);
432                         c->secret[0] = '\0';
433                         c->auth_port = auth_port;
434                         c->acct_port = acct_port;
435
436                 } else {
437                         RADCLIENT *client;
438                         c->ipaddr = ip_getaddr(hostnm);
439                         c->acct_ipaddr = c->ipaddr;
440
441                         if (c->ipaddr == htonl(INADDR_NONE)) {
442                                 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
443                                        file, lineno, hostnm);
444                                 return -1;
445                         }
446
447                         /*
448                          *      Find the remote server in the "clients" list.
449                          *      If we can't find it, there's a big problem...
450                          */
451                         client = client_find(c->ipaddr);
452                         if (client == NULL) {
453                           radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
454                                  file, lineno, hostnm, realm);
455                           return -1;
456                         }
457                         memcpy(c->secret, client->secret, sizeof(c->secret));
458                 }
459
460                 /*
461                  *      Double-check lengths to be sure they're sane
462                  */
463                 if (strlen(hostnm) >= sizeof(c->server)) {
464                         radlog(L_ERR, "%s[%d]: server name of length %d is greater than the allowed maximum of %d.",
465                                         file, lineno,
466                                         strlen(hostnm), sizeof(c->server) - 1);
467                         return -1;
468                 }
469                 if (strlen(realm) > sizeof(c->realm)) {
470                         radlog(L_ERR, "%s[%d]: realm of length %d is greater than the allowed maximum of %d.",
471                                         file, lineno,
472                                         strlen(realm), sizeof(c->realm) - 1);
473                         return -1;
474                 }
475
476                 /*
477                  *      OK, they're sane, copy them over.
478                  */
479                 strcpy(c->realm, realm);
480                 strcpy(c->server, hostnm);
481                 c->striprealm = TRUE;
482                 c->active = TRUE;
483                 c->acct_active = TRUE;
484
485                 while (getword(&p, opts, sizeof(opts))) {
486                         if (strcmp(opts, "nostrip") == 0)
487                                 c->striprealm = FALSE;
488                         if (strstr(opts, "noacct") != NULL)
489                                 c->acct_port = 0;
490                         if (strstr(opts, "trusted") != NULL)
491                                 c->trusted = 1;
492                         if (strstr(opts, "notrealm") != NULL)
493                                 c->notrealm = 1;
494                         if (strstr(opts, "notsuffix") != NULL)
495                                 c->notrealm = 1;
496                 }
497
498                 c->next = NULL;
499                 *tail = c;
500                 tail = &c->next;
501         }
502         fclose(fp);
503
504         return 0;
505 }
506 #endif /* BUILDDBM */
507
508 /*
509  * Mark a host inactive
510  */
511 void realm_disable(uint32_t ipaddr, int port)
512 {
513         REALM *cl;
514         time_t now;
515
516         now = time(NULL);
517         for(cl = mainconfig.realms; cl; cl = cl->next) {
518                 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
519                         /*
520                          *      If we've received a reply (any reply)
521                          *      from the home server in the time spent
522                          *      re-sending this request, then don't mark
523                          *      the realm as dead.
524                          */
525                         if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
526                                 continue;
527                         }
528
529                         cl->active = FALSE;
530                         cl->wakeup = now + mainconfig.proxy_dead_time;
531                         radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
532                                 cl->server, port, cl->realm);
533                 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {
534                         if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
535                                 continue;
536                         }
537
538                         cl->acct_active = FALSE;
539                         cl->acct_wakeup = now + mainconfig.proxy_dead_time;
540                         radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
541                                 cl->server, port, cl->realm);
542                 }
543         }
544 }
545
546 /*
547  *      Find a realm in the REALM list.
548  */
549 REALM *realm_find(const char *realm, int accounting)
550 {
551         REALM *cl;
552         REALM *default_realm = NULL;
553         time_t now;
554         int dead_match = 0;
555
556         now = time(NULL);
557
558         /*
559          *      If we're passed a NULL realm pointer,
560          *      then look for a "NULL" realm string.
561          */
562         if (realm == NULL) {
563                 realm = "NULL";
564         }
565         
566         for (cl = mainconfig.realms; cl; cl = cl->next) {
567                 /*
568                  *      Wake up any sleeping realm.
569                  */
570                 if (cl->wakeup <= now) {
571                         cl->active = TRUE;
572                 }
573                 if (cl->acct_wakeup <= now) {
574                         cl->acct_active = TRUE;
575                 }
576
577                 /*
578                  *      Asked for auth/acct, and the auth/acct server
579                  *      is not active.  Skip it.
580                  */
581                 if ((!accounting && !cl->active) ||
582                     (accounting && !cl->acct_active)) {
583
584                         /*
585                          *      We've been asked to NOT fall through
586                          *      to the DEFAULT realm if there are
587                          *      exact matches for this realm which are
588                          *      dead.
589                          */
590                         if ((!mainconfig.proxy_fallback) &&
591                             (strcasecmp(cl->realm, realm) == 0)) {
592                                 dead_match = 1;
593                         }
594                         continue;
595                 }
596
597                 /*
598                  *      If it matches exactly, return it.
599                  *
600                  *      Note that we just want ONE live realm
601                  *      here.  We don't care about round-robin, or
602                  *      scatter techniques, as that's more properly
603                  *      the responsibility of the proxying code.
604                  */
605                 if (strcasecmp(cl->realm, realm) == 0) {
606                         return cl;
607                 }
608
609                 /*
610                  *      No default realm, try to set one.
611                  */
612                 if ((default_realm == NULL) &&
613                     (strcmp(cl->realm, "DEFAULT") == 0)) {
614                   default_realm = cl;
615                 }
616         } /* loop over all realms */
617
618         /*
619          *      There WAS one or more matches which were marked dead,
620          *      AND there were NO live matches, AND we've been asked
621          *      to NOT fall through to the DEFAULT realm.  Therefore,
622          *      we return NULL, which means "no match found".
623          */
624         if (!mainconfig.proxy_fallback && dead_match) {
625                 return NULL;
626         }
627
628         /*      If we didn't find the realm 'NULL' don't return the 
629          *      DEFAULT entry.
630          */
631         if ((strcmp(realm, "NULL")) == 0) {
632           return NULL;
633         }
634
635         /*
636          *      Didn't find anything that matched exactly, return the
637          *      DEFAULT realm.  We also return the DEFAULT realm if
638          *      all matching realms were marked dead, and we were
639          *      asked to fall through to the DEFAULT realm in this
640          *      case.
641          */
642         return default_realm;
643 }
644
645 /*
646  *      Find a realm for a proxy reply by proxy's IP
647  *
648  *      Note that we don't do anything else.
649  */
650 REALM *realm_findbyaddr(uint32_t ipaddr, int port)
651 {
652         REALM *cl;
653         
654         /*
655          *      Note that we do NOT check for inactive realms!
656          *
657          *      The purpose of this code is simply to find a matching
658          *      source IP/Port pair, for a home server which is allowed
659          *      to send us proxy replies.  If we get a reply, then it
660          *      doesn't matter if we think the realm is inactive.
661          */
662         for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
663                 if ((ipaddr == cl->ipaddr) && (port == cl->auth_port)) {
664                         return cl;
665
666                 } else if ((ipaddr == cl->acct_ipaddr) && (port == cl->acct_port)) {
667                         return cl;
668                 }
669         }
670
671         return NULL;
672 }
673