Deprecate attribute 'Password' in favor of 'User-Password'.
[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 extern int proxy_dead_time;
45
46 REALM *realms = NULL;
47
48 /*
49  *      Free a PAIR_LIST
50  */
51 void pairlist_free(PAIR_LIST **pl)
52 {
53         PAIR_LIST *p, *next;
54
55         for (p = *pl; p; p = next) {
56                 if (p->name) free(p->name);
57                 if (p->check) pairfree(&p->check);
58                 if (p->reply) pairfree(&p->reply);
59                 next = p->next;
60                 free(p);
61         }
62         *pl = NULL;
63 }
64
65
66 /*
67  *      Fixup a check line.
68  *      If User-Password or Crypt-Password is set, but there is no
69  *      Auth-Type, add one (kludge!).
70  */
71 static void auth_type_fixup(VALUE_PAIR *check)
72 {
73         VALUE_PAIR *vp;
74         VALUE_PAIR *c = NULL;
75         int n = 0;
76
77         /*
78          *      See if a password is present. Return right away
79          *      if we see Auth-Type.
80          */
81         for (vp = check; vp; vp = vp->next) {
82                 if (vp->attribute == PW_AUTHTYPE)
83                         return;
84                 if (vp->attribute == PW_PASSWORD) {
85                         c = vp;
86                         n = PW_AUTHTYPE_LOCAL;
87                 }
88                 if (vp->attribute == PW_CRYPT_PASSWORD) {
89                         c = vp;
90                         n = PW_AUTHTYPE_CRYPT;
91                 }
92         }
93
94         if (c == NULL)
95                 return;
96
97         /*
98          *      Add an Auth-Type attribute.
99          *      
100          */
101         if ((vp = paircreate(PW_AUTHTYPE, PW_TYPE_INTEGER)) == NULL) {
102                 radlog(L_CONS|L_ERR, "no memory");
103                 exit(1);
104         }
105         vp->lvalue = n;
106         vp->operator = T_OP_ADD;
107
108 #if 0
109         vp->next = c->next;
110         c->next = vp;
111 #endif
112         vp->next = check;
113         check = vp;
114
115         for(vp = check; vp; vp = vp->next) {
116                 DEBUG2("  auth_type_fixup: %s [%d]", vp->name, vp->attribute);
117         }
118
119 }
120
121
122 #define FIND_MODE_NAME  0
123 #define FIND_MODE_REPLY 1
124
125 /*
126  *      Read the users, huntgroups or hints file.
127  *      Return a PAIR_LIST.
128  */
129 int pairlist_read(const char *file, PAIR_LIST **list, int complain)
130 {
131         FILE *fp;
132         int mode = FIND_MODE_NAME;
133         char entry[256];
134         char buffer[256];
135         char *ptr, *s;
136         VALUE_PAIR *check_tmp;
137         VALUE_PAIR *reply_tmp;
138         PAIR_LIST *pl = NULL, *last = NULL, *t;
139         int lineno = 0;
140         int old_lineno = 0;
141         LRAD_TOKEN parsecode;
142         char newfile[8192];
143
144         /*
145          *      Open the file.  The error message should be a little
146          *      more useful...
147          */
148         if ((fp = fopen(file, "r")) == NULL) {
149                 if (!complain) 
150                         return -1;
151                 radlog(L_CONS|L_ERR, "Couldn't open %s for reading: %s",
152                                 file, strerror(errno));
153                 return -1;
154         }
155
156         parsecode = T_EOL;
157         /*
158          *      Read the entire file into memory for speed.
159          */
160         while(fgets(buffer, sizeof(buffer), fp) != NULL) {
161                 lineno++;
162                 if (strchr(buffer, '\n') == NULL) {
163                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
164                         pairlist_free(&pl);
165                         return -1;
166                 }
167                 if (buffer[0] == '#' || buffer[0] == '\n') continue;
168 parse_again:
169                 if(mode == FIND_MODE_NAME) {
170                         /*
171                          *      Find the entry starting with the users name
172                          */
173                         if (isspace(buffer[0]))  {
174                                 if (parsecode != T_EOL) {
175                                         radlog(L_ERR|L_CONS,
176                                                         "%s[%d]: Unexpected trailing comma for entry %s",
177                                                         file, lineno, entry);
178                                         fclose(fp);
179                                         return -1;
180                                 }
181                                 continue;
182                         }
183
184                         ptr = buffer;
185                         getword(&ptr, entry, sizeof(entry));
186
187                         /*
188                          *      Include another file if we see
189                          *      $INCLUDE filename
190                          */
191                         if (strcasecmp(entry, "$include") == 0) {
192                                 while(isspace(*ptr))
193                                         ptr++;
194                                 s = ptr;
195                                 while (!isspace(*ptr))
196                                         ptr++;
197                                 *ptr = 0;
198
199                                 /*
200                                  *      If it's an absolute pathname,
201                                  *      then use it verbatim.
202                                  *
203                                  *      If not, then make the $include
204                                  *      files *relative* to the current
205                                  *      file.
206                                  */
207                                 if (*s != '/') {
208                                         strNcpy(newfile, file,
209                                                 sizeof(newfile));
210                                         ptr = strrchr(newfile, '/');
211                                         strcpy(ptr + 1, s);
212                                         s = newfile;
213                                 }
214
215                                 t = NULL;
216                                 if (pairlist_read(s, &t, 0) != 0) {
217                                         pairlist_free(&pl);
218                                         radlog(L_ERR|L_CONS,
219                                                         "%s[%d]: Could not open included file %s: %s",
220                                                         file, lineno, s, strerror(errno));
221                                         fclose(fp);
222                                 return -1;
223                                 }
224                                 if (last)
225                                         last->next = t;
226                                 else
227                                         pl = t;
228                                 last = t;
229                                 while (last && last->next)
230                                         last = last->next;
231                                 continue;
232                         }
233
234                         /*
235                          *      Parse the check values
236                          */
237                         check_tmp = NULL;
238                         reply_tmp = NULL;
239                         old_lineno = lineno;
240                         parsecode = userparse(ptr, &check_tmp);
241                         if (parsecode == T_INVALID) {
242                                 pairlist_free(&pl);
243                                 radlog(L_ERR|L_CONS,
244                                 "%s[%d]: Parse error (check) for entry %s: %s",
245                                         file, lineno, entry, librad_errstr);
246                                 fclose(fp);
247                                 return -1;
248                         } else if (parsecode == T_COMMA) {
249                                 radlog(L_ERR|L_CONS,
250                                                 "%s[%d]: Unexpected trailing comma in check item list for entry %s",
251                                                 file, lineno, entry);
252                                 fclose(fp);
253                                 return -1;
254                         }
255                         mode = FIND_MODE_REPLY;
256                         parsecode = T_COMMA;
257                 }
258                 else {
259                         if(*buffer == ' ' || *buffer == '\t') {
260                                 if (parsecode != T_COMMA) {
261                                         radlog(L_ERR|L_CONS,
262                                                         "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
263                                                         file, lineno, entry);
264                                         fclose(fp);
265                                         return -1;
266                                 }
267
268                                 /*
269                                  *      Parse the reply values
270                                  */
271                                 parsecode = userparse(buffer, &reply_tmp);
272                                 if (parsecode < 0) {
273                                         pairlist_free(&pl);
274                                         radlog(L_ERR|L_CONS,
275                                                         "%s[%d]: Parse error (reply) for entry %s: %s",
276                                                         file, lineno, entry, librad_errstr);
277                                         fclose(fp);
278                                         return -1;
279                                 }
280                         }
281                         else {
282                                 /*
283                                  *      Done with this entry...
284                                  */
285                                 t = rad_malloc(sizeof(PAIR_LIST));
286
287                                 auth_type_fixup(check_tmp);
288                                 memset(t, 0, sizeof(*t));
289                                 t->name = strdup(entry);
290                                 t->check = check_tmp;
291                                 t->reply = reply_tmp;
292                                 t->lineno = old_lineno;
293                                 check_tmp = NULL;
294                                 reply_tmp = NULL;
295                                 if (last)
296                                         last->next = t;
297                                 else
298                                         pl = t;
299                                 last = t;
300
301                                 mode = FIND_MODE_NAME;
302                                 if (buffer[0] != 0)
303                                         goto parse_again;
304                         }
305                 }
306         }
307         /*
308          *      Make sure that we also read the last line of the file!
309          */
310         if (mode == FIND_MODE_REPLY) {
311                 buffer[0] = 0;
312                 goto parse_again;
313         }
314         fclose(fp);
315
316         *list = pl;
317         return 0;
318 }
319
320
321 /*
322  *      Debug code.
323  */
324 #if 0
325 static void debug_pair_list(PAIR_LIST *pl)
326 {
327         VALUE_PAIR *vp;
328
329         while(pl) {
330                 printf("Pair list: %s\n", pl->name);
331                 printf("** Check:\n");
332                 for(vp = pl->check; vp; vp = vp->next) {
333                         printf("    ");
334                         fprint_attr_val(stdout, vp);
335                         printf("\n");
336                 }
337                 printf("** Reply:\n");
338                 for(vp = pl->reply; vp; vp = vp->next) {
339                         printf("    ");
340                         fprint_attr_val(stdout, vp);
341                         printf("\n");
342                 }
343                 pl = pl->next;
344         }
345 }
346 #endif
347
348 #ifndef BUILDDBM /* HACK HACK */
349
350 /*
351  *      Free a REALM list.
352  */
353 static void realm_free(REALM *cl)
354 {
355         REALM *next;
356
357         while(cl) {
358                 next = cl->next;
359                 free(cl);
360                 cl = next;
361         }
362 }
363
364 /*
365  *      Read the realms file.
366  */
367 int read_realms_file(const char *file)
368 {
369         FILE *fp;
370         char buffer[256];
371         char realm[256];
372         char hostnm[256];
373         char opts[256];
374         char *s, *p;
375         int lineno = 0;
376         REALM *c, **tail;
377
378         realm_free(realms);
379         realms = NULL;
380         tail = &realms;
381
382         if ((fp = fopen(file, "r")) == NULL) {
383                 /* The realms file is not mandatory.  If it exists it will
384                    be used, however, since the new style config files are
385                    more robust and flexible they are more likely to get used.
386                    So this is a non-fatal error.  */
387                 return 0;
388         }
389         while(fgets(buffer, 256, fp) != NULL) {
390                 lineno++;
391                 if (strchr(buffer, '\n') == NULL) {
392                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
393                         return -1;
394                 }
395                 if (buffer[0] == '#' || buffer[0] == '\n')
396                         continue;
397                 p = buffer;
398                 if (!getword(&p, realm, sizeof(realm)) ||
399                                 !getword(&p, hostnm, sizeof(hostnm))) {
400                         radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
401                         continue;
402                 }
403
404                 c = rad_malloc(sizeof(REALM));
405                 memset(c, 0, sizeof(REALM));
406
407                 if ((s = strchr(hostnm, ':')) != NULL) {
408                         *s++ = 0;
409                         c->auth_port = atoi(s);
410                         c->acct_port = c->auth_port + 1;
411                 } else {
412                         c->auth_port = auth_port;
413                         c->acct_port = acct_port;
414                 }
415
416                 if (strcmp(hostnm, "LOCAL") == 0) {
417                         /*
418                          *      Local realms don't have an IP address,
419                          *      secret, or port.
420                          */
421                         c->acct_ipaddr = c->ipaddr = htonl(INADDR_NONE);
422                         c->secret[0] = '\0';
423                         c->auth_port = auth_port;
424                         c->acct_port = acct_port;
425
426                 } else {
427                         RADCLIENT *client;
428                         c->ipaddr = ip_getaddr(hostnm);
429                         c->acct_ipaddr = c->ipaddr;
430
431                         if (c->ipaddr == htonl(INADDR_NONE)) {
432                                 radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
433                                        file, lineno, hostnm);
434                                 return -1;
435                         }
436
437                         /*
438                          *      Find the remote server in the "clients" list.
439                          *      If we can't find it, there's a big problem...
440                          */
441                         client = client_find(c->ipaddr);
442                         if (client == NULL) {
443                           radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
444                                  file, lineno, hostnm, realm);
445                           return -1;
446                         }
447                         memcpy(c->secret, client->secret, sizeof(c->secret));
448                 }
449
450                 /*
451                  *      Double-check lengths to be sure they're sane
452                  */
453                 if (strlen(hostnm) >= sizeof(c->server)) {
454                         radlog(L_ERR, "%s[%d]: server name of length %d is greater than the allowed maximum of %d.",
455                                         file, lineno,
456                                         strlen(hostnm), sizeof(c->server) - 1);
457                         return -1;
458                 }
459                 if (strlen(realm) > sizeof(c->realm)) {
460                         radlog(L_ERR, "%s[%d]: realm of length %d is greater than the allowed maximum of %d.",
461                                         file, lineno,
462                                         strlen(realm), sizeof(c->realm) - 1);
463                         return -1;
464                 }
465
466                 /*
467                  *      OK, they're sane, copy them over.
468                  */
469                 strcpy(c->realm, realm);
470                 strcpy(c->server, hostnm);
471                 c->striprealm = TRUE;
472                 c->active = TRUE;
473
474                 while (getword(&p, opts, sizeof(opts))) {
475                         if (strcmp(opts, "nostrip") == 0)
476                                 c->striprealm = FALSE;
477                         if (strstr(opts, "noacct") != NULL)
478                                 c->acct_port = 0;
479                         if (strstr(opts, "trusted") != NULL)
480                                 c->trusted = 1;
481                         if (strstr(opts, "notrealm") != NULL)
482                                 c->notrealm = 1;
483                         if (strstr(opts, "notsuffix") != NULL)
484                                 c->notrealm = 1;
485                 }
486
487                 c->next = NULL;
488                 *tail = c;
489                 tail = &c->next;
490         }
491         fclose(fp);
492
493         return 0;
494 }
495 #endif /* BUILDDBM */
496
497 /*
498  * Mark a host inactive
499  */
500 void realm_disable(uint32_t ipaddr)
501 {
502         REALM *cl;
503         time_t now;
504
505         now = time(NULL);
506         for(cl = realms; cl; cl = cl->next)
507                 if ((ipaddr == cl->ipaddr) ||
508                     (ipaddr == cl->acct_ipaddr)) {
509                         cl->active = FALSE;
510                         cl->wakeup = now + proxy_dead_time;
511                 }
512 }
513
514 /*
515  *      Find a realm in the REALM list.
516  */
517 REALM *realm_find(const char *realm)
518 {
519         REALM *cl;
520         REALM *default_realm = NULL;
521         time_t now;
522
523         now = time(NULL);
524
525         /*
526          *      If we're passed a NULL realm pointer,
527          *      then look for a "NULL" realm string.
528          */
529         if (realm == NULL) {
530                 realm = "NULL";
531         }
532
533         for (cl = realms; cl; cl = cl->next) {
534                 /*
535                  *      Wake up any sleeping realm.
536                  */
537                 if (cl->wakeup <= now) {
538                         cl->active = TRUE;
539                 }
540
541                 /*
542                  *      It's not alive, skip it.
543                  */
544                 if (!cl->active) {
545                         continue;
546                 }
547
548                 /*
549                  *      If it matches exactly, return it.
550                  */
551                 if (strcmp(cl->realm, realm) == 0) {
552                         return cl;
553                 }
554
555                 /*
556                  *      No default realm, try to set one.
557                  */
558                 if ((default_realm == NULL) &&
559                     (strcmp(cl->realm, "DEFAULT") == 0)) {
560                   default_realm = cl;
561                 }
562         } /* loop over all realms */
563
564         /*
565          *      Didn't find anything that matched exactly, return
566          *      the default.
567          */
568         return default_realm;
569 }
570
571
572 /*
573  *      Find a realm for a proxy reply by proxy's IP
574  */
575 REALM *realm_findbyaddr(uint32_t ipaddr)
576 {
577         REALM *cl;
578
579         /*
580          *      Note that we do NOT check for inactive realms!
581          *
582          *      If we get a packet from an end server, then we mark it
583          *      as active, and return the realm.
584          */
585         for(cl = realms; cl != NULL; cl = cl->next)
586                 if ((ipaddr == cl->ipaddr) ||
587                     (ipaddr == cl->acct_ipaddr)) {
588                         cl->active = TRUE;
589                         return cl;
590                 }
591
592         return NULL;
593 }