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