Changed the tokens from being define's to enums. This allows
[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 REALM *realms;
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 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
106 #if 0
107         vp->next = c->next;
108         c->next = vp;
109 #endif
110         vp->next = check;
111         check = vp;
112
113         for(vp = check; vp; vp = vp->next) {
114                 DEBUG2("  auth_type_fixup: %s [%d]", vp->name, vp->attribute);
115         }
116
117 }
118
119
120 #define FIND_MODE_NAME  0
121 #define FIND_MODE_REPLY 1
122
123 /*
124  *      Read the users, huntgroups or hints file.
125  *      Return a PAIR_LIST.
126  */
127 int pairlist_read(const char *file, PAIR_LIST **list, int complain)
128 {
129         FILE *fp;
130         int mode = FIND_MODE_NAME;
131         char entry[256];
132         char buffer[256];
133         char *ptr, *s;
134         VALUE_PAIR *check_tmp;
135         VALUE_PAIR *reply_tmp;
136         PAIR_LIST *pl = NULL, *last = NULL, *t;
137         int lineno = 0;
138         int old_lineno = 0;
139         LRAD_TOKEN parsecode;
140         char newfile[8192];
141
142         /*
143          *      Open the file.  The error message should be a little
144          *      more useful...
145          */
146         if ((fp = fopen(file, "r")) == NULL) {
147                 if (!complain) 
148                         return -1;
149                 radlog(L_CONS|L_ERR, "Couldn't open %s for reading: %s",
150                                 file, strerror(errno));
151                 return -1;
152         }
153
154         parsecode = T_EOL;
155         /*
156          *      Read the entire file into memory for speed.
157          */
158         while(fgets(buffer, sizeof(buffer), fp) != NULL) {
159                 lineno++;
160                 if (strchr(buffer, '\n') == NULL) {
161                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
162                         pairlist_free(&pl);
163                         return -1;
164                 }
165                 if (buffer[0] == '#' || buffer[0] == '\n') continue;
166 parse_again:
167                 if(mode == FIND_MODE_NAME) {
168                         /*
169                          *      Find the entry starting with the users name
170                          */
171                         if (isspace(buffer[0]))  {
172                                 if (parsecode != T_EOL) {
173                                         radlog(L_ERR|L_CONS,
174                                                         "%s[%d]: Unexpected trailing comma for entry %s",
175                                                         file, lineno, entry);
176                                         fclose(fp);
177                                         return -1;
178                                 }
179                                 continue;
180                         }
181
182                         ptr = buffer;
183                         getword(&ptr, entry, sizeof(entry));
184
185                         /*
186                          *      Include another file if we see
187                          *      $INCLUDE filename
188                          */
189                         if (strcasecmp(entry, "$include") == 0) {
190                                 while(isspace(*ptr))
191                                         ptr++;
192                                 s = ptr;
193                                 while (!isspace(*ptr))
194                                         ptr++;
195                                 *ptr = 0;
196
197                                 /*
198                                  *      If it's an absolute pathname,
199                                  *      then use it verbatim.
200                                  *
201                                  *      If not, then make the $include
202                                  *      files *relative* to the current
203                                  *      file.
204                                  */
205                                 if (*s != '/') {
206                                         strNcpy(newfile, file,
207                                                 sizeof(newfile));
208                                         ptr = strrchr(newfile, '/');
209                                         strcpy(ptr + 1, s);
210                                         s = newfile;
211                                 }
212
213                                 t = NULL;
214                                 if (pairlist_read(s, &t, 0) != 0) {
215                                         pairlist_free(&pl);
216                                         radlog(L_ERR|L_CONS,
217                                                         "%s[%d]: Could not open included file %s: %s",
218                                                         file, lineno, s, strerror(errno));
219                                         fclose(fp);
220                                 return -1;
221                                 }
222                                 if (last)
223                                         last->next = t;
224                                 else
225                                         pl = t;
226                                 last = t;
227                                 while (last && last->next)
228                                         last = last->next;
229                                 continue;
230                         }
231
232                         /*
233                          *      Parse the check values
234                          */
235                         check_tmp = NULL;
236                         reply_tmp = NULL;
237                         old_lineno = lineno;
238                         parsecode = userparse(ptr, &check_tmp);
239                         if (parsecode == T_INVALID) {
240                                 pairlist_free(&pl);
241                                 radlog(L_ERR|L_CONS,
242                                 "%s[%d]: Parse error (check) for entry %s: %s",
243                                         file, lineno, entry, librad_errstr);
244                                 fclose(fp);
245                                 return -1;
246                         } else if (parsecode == T_COMMA) {
247                                 radlog(L_ERR|L_CONS,
248                                                 "%s[%d]: Unexpected trailing comma in check item list for entry %s",
249                                                 file, lineno, entry);
250                                 fclose(fp);
251                                 return -1;
252                         }
253                         mode = FIND_MODE_REPLY;
254                         parsecode = T_COMMA;
255                 }
256                 else {
257                         if(*buffer == ' ' || *buffer == '\t') {
258                                 if (parsecode != T_COMMA) {
259                                         radlog(L_ERR|L_CONS,
260                                                         "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
261                                                         file, lineno, entry);
262                                         fclose(fp);
263                                         return -1;
264                                 }
265
266                                 /*
267                                  *      Parse the reply values
268                                  */
269                                 parsecode = userparse(buffer, &reply_tmp);
270                                 if (parsecode < 0) {
271                                         pairlist_free(&pl);
272                                         radlog(L_ERR|L_CONS,
273                                                         "%s[%d]: Parse error (reply) for entry %s: %s",
274                                                         file, lineno, entry, librad_errstr);
275                                         fclose(fp);
276                                         return -1;
277                                 }
278                         }
279                         else {
280                                 /*
281                                  *      Done with this entry...
282                                  */
283                                 t = rad_malloc(sizeof(PAIR_LIST));
284
285                                 auth_type_fixup(check_tmp);
286                                 memset(t, 0, sizeof(*t));
287                                 t->name = strdup(entry);
288                                 t->check = check_tmp;
289                                 t->reply = reply_tmp;
290                                 t->lineno = old_lineno;
291                                 check_tmp = NULL;
292                                 reply_tmp = NULL;
293                                 if (last)
294                                         last->next = t;
295                                 else
296                                         pl = t;
297                                 last = t;
298
299                                 mode = FIND_MODE_NAME;
300                                 if (buffer[0] != 0)
301                                         goto parse_again;
302                         }
303                 }
304         }
305         /*
306          *      Make sure that we also read the last line of the file!
307          */
308         if (mode == FIND_MODE_REPLY) {
309                 buffer[0] = 0;
310                 goto parse_again;
311         }
312         fclose(fp);
313
314         *list = pl;
315         return 0;
316 }
317
318
319 /*
320  *      Debug code.
321  */
322 #if 0
323 static void debug_pair_list(PAIR_LIST *pl)
324 {
325         VALUE_PAIR *vp;
326
327         while(pl) {
328                 printf("Pair list: %s\n", pl->name);
329                 printf("** Check:\n");
330                 for(vp = pl->check; vp; vp = vp->next) {
331                         printf("    ");
332                         fprint_attr_val(stdout, vp);
333                         printf("\n");
334                 }
335                 printf("** Reply:\n");
336                 for(vp = pl->reply; vp; vp = vp->next) {
337                         printf("    ");
338                         fprint_attr_val(stdout, vp);
339                         printf("\n");
340                 }
341                 pl = pl->next;
342         }
343 }
344 #endif
345
346 #ifndef BUILDDBM /* HACK HACK */
347
348 /*
349  *      Free a REALM list.
350  */
351 static void realm_free(REALM *cl)
352 {
353         REALM *next;
354
355         while(cl) {
356                 next = cl->next;
357                 free(cl);
358                 cl = next;
359         }
360 }
361
362 /*
363  *      Read the realms file.
364  */
365 int read_realms_file(const char *file)
366 {
367         FILE *fp;
368         char buffer[256];
369         char realm[256];
370         char hostnm[256];
371         char opts[256];
372         char *s, *p;
373         int lineno = 0;
374         REALM *c;
375         RADCLIENT *client;
376
377         realm_free(realms);
378         realms = NULL;
379
380         if ((fp = fopen(file, "r")) == NULL) {
381                 /* The realms file is not mandatory.  If it exists it will
382                    be used, however, since the new style config files are
383                    more robust and flexible they are more likely to get used.
384                    So this is a non-fatal error.  */
385                 return 0;
386         }
387         while(fgets(buffer, 256, fp) != NULL) {
388                 lineno++;
389                 if (strchr(buffer, '\n') == NULL) {
390                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
391                         return -1;
392                 }
393                 if (buffer[0] == '#' || buffer[0] == '\n')
394                         continue;
395                 p = buffer;
396                 if (!getword(&p, realm, sizeof(realm)) ||
397                                 !getword(&p, hostnm, sizeof(hostnm))) {
398                         radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
399                         continue;
400                 }
401
402                 c = rad_malloc(sizeof(REALM));
403                 memset(c, 0, sizeof(REALM));
404
405                 if ((s = strchr(hostnm, ':')) != NULL) {
406                         *s++ = 0;
407                         c->auth_port = atoi(s);
408                         c->acct_port = c->auth_port + 1;
409                 } else {
410                         c->auth_port = auth_port;
411                         c->acct_port = acct_port;
412                 }
413
414                 if (strcmp(hostnm, "LOCAL") == 0) {
415                         c->ipaddr = htonl(INADDR_LOOPBACK);
416                 } else {
417                         c->ipaddr = ip_getaddr(hostnm);
418                 }
419
420                 if (c->ipaddr == 0) {
421                         radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
422                                         file, lineno, hostnm);
423                         return -1;
424                 }
425
426                 /*
427                  *      Find the remote server in the "clients" list.
428                  *      If we can't find it, there's a big problem...
429                  */
430                 client = client_find(c->ipaddr);
431                 if (client == NULL) {
432                         radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
433                                         file, lineno, hostnm, realm);
434                         return -1;
435                 }
436                 memcpy(c->secret, client->secret, sizeof(c->secret));
437
438                 /*
439                  *      Double-check lengths to be sure they're sane
440                  */
441                 if (strlen(hostnm) >= sizeof(c->server)) {
442                         radlog(L_ERR, "%s[%d]: server name of length %d is greater than the allowed maximum of %d.",
443                                         file, lineno,
444                                         strlen(hostnm), sizeof(c->server) - 1);
445                         return -1;
446                 }
447                 if (strlen(realm) > sizeof(c->realm)) {
448                         radlog(L_ERR, "%s[%d]: realm of length %d is greater than the allowed maximum of %d.",
449                                         file, lineno,
450                                         strlen(realm), sizeof(c->realm) - 1);
451                         return -1;
452                 }
453
454                 /*
455                  *      OK, they're sane, copy them over.
456                  */
457                 strcpy(c->realm, realm);
458                 strcpy(c->server, hostnm);
459                 c->striprealm = TRUE;
460
461                 while (getword(&p, opts, sizeof(opts))) {
462                         if (strcmp(opts, "nostrip") == 0)
463                                 c->striprealm = FALSE;
464                         if (strstr(opts, "noacct") != NULL)
465                                 c->acct_port = 0;
466                         if (strstr(opts, "trusted") != NULL)
467                                 c->trusted = 1;
468                         if (strstr(opts, "notrealm") != NULL)
469                                 c->notrealm = 1;
470                         if (strstr(opts, "notsuffix") != NULL)
471                                 c->notrealm = 1;
472                 }
473
474                 c->next = realms;
475                 realms = c;
476         }
477         fclose(fp);
478
479         return 0;
480 }
481 #endif /* BUILDDBM */
482
483 /*
484  *      Find a realm in the REALM list.
485  */
486 REALM *realm_find(const char *realm)
487 {
488         REALM *cl;
489
490         /*
491          *      If we're passed a NULL realm pointer,
492          *      then look for a "NULL" realm string.
493          */
494         if (realm == NULL) {
495                 realm = "NULL";
496         }
497
498         for(cl = realms; cl; cl = cl->next)
499                 if (strcmp(cl->realm, realm) == 0)
500                         break;
501         if (cl != NULL) 
502                 return cl;
503         for(cl = realms; cl; cl = cl->next)
504                 if (strcmp(cl->realm, "DEFAULT") == 0)
505                         break;
506         return cl;
507 }
508
509
510 /*
511  *      Find a realm for a proxy reply by proxy's IP
512  */
513 REALM *realm_findbyaddr(uint32_t ipaddr)
514 {
515         REALM *cl;
516
517         for(cl = realms; cl != NULL; cl = cl->next)
518                 if (ipaddr == cl->ipaddr)
519                         break;
520
521         return cl;
522 }