Jeff Carneal <jeff@apex.net>
[freeradius.git] / src / main / files.c
1 /*
2  * files.c      Read config files into memory.
3  *
4  * Version:     $Id$
5  *
6  */
7
8 static const char rcsid[] = "$Id$";
9
10 #include        "autoconf.h"
11
12 #include        <sys/stat.h>
13
14 #if HAVE_NETINET_IN_H
15 #include        <netinet/in.h>
16 #endif
17
18 #include        <stdlib.h>
19 #include        <string.h>
20 #include        <netdb.h>
21 #include        <ctype.h>
22 #include        <fcntl.h>
23
24 #if HAVE_MALLOC_H
25 #  include      <malloc.h>
26 #endif
27
28 #include        "radiusd.h"
29 #include        "modules.h"
30 #include        "conffile.h"
31
32 REALM                   *realms;
33
34 /*
35  *      Free a PAIR_LIST
36  */
37 void pairlist_free(PAIR_LIST **pl)
38 {
39         PAIR_LIST *p, *next;
40
41         for (p = *pl; p; p = next) {
42                 if (p->name) free(p->name);
43                 if (p->check) pairfree(p->check);
44                 if (p->reply) pairfree(p->reply);
45                 next = p->next;
46                 free(p);
47         }
48         *pl = NULL;
49 }
50
51
52 /*
53  *      Fixup a check line.
54  *      If Password or Crypt-Password is set, but there is no
55  *      Auth-Type, add one (kludge!).
56  */
57 static void auth_type_fixup(VALUE_PAIR *check)
58 {
59         VALUE_PAIR      *vp;
60         VALUE_PAIR      *c = NULL;
61         int             n = 0;
62
63         /*
64          *      See if a password is present. Return right away
65          *      if we see Auth-Type.
66          */
67         for (vp = check; vp; vp = vp->next) {
68                 if (vp->attribute == PW_AUTHTYPE)
69                         return;
70                 if (vp->attribute == PW_PASSWORD) {
71                         c = vp;
72                         n = PW_AUTHTYPE_LOCAL;
73                 }
74                 if (vp->attribute == PW_CRYPT_PASSWORD) {
75                         c = vp;
76                         n = PW_AUTHTYPE_CRYPT;
77                 }
78         }
79
80         if (c == NULL)
81                 return;
82
83         /*
84          *      Add an Auth-Type attribute.
85          *      FIXME: put Auth-Type _first_ (doesn't matter now,
86          *      might matter some day).
87          *      
88          */
89         if ((vp = paircreate(PW_AUTHTYPE, PW_TYPE_INTEGER)) == NULL) {
90                 radlog(L_CONS|L_ERR, "no memory");
91                 exit(1);
92         }
93         vp->lvalue = n;
94         vp->operator = T_OP_ADD;
95
96         vp->next = c->next;
97         c->next = vp;
98
99 }
100
101
102 #define FIND_MODE_NAME  0
103 #define FIND_MODE_REPLY 1
104
105 /*
106  *      Read the users, huntgroups or hints file.
107  *      Return a PAIR_LIST.
108  */
109 int pairlist_read(const char *file, PAIR_LIST **list, int complain)
110 {
111         FILE            *fp;
112         int             mode = FIND_MODE_NAME;
113         char            entry[256];
114         char            buffer[256];
115         char            *ptr, *s;
116         VALUE_PAIR      *check_tmp;
117         VALUE_PAIR      *reply_tmp;
118         PAIR_LIST       *pl = NULL, *last = NULL, *t;
119         int             lineno = 0;
120         int             old_lineno = 0;
121         int             parsecode;
122         char            newfile[8192];
123
124         /*
125          *      Open the file.  The error message should be a little
126          *      more useful...
127          */
128         if ((fp = fopen(file, "r")) == NULL) {
129                 if (!complain) return -1;
130                 radlog(L_CONS|L_ERR, "Couldn't open %s for reading: %s",
131                     file, strerror(errno));
132                 return -1;
133         }
134
135         parsecode = T_EOL;
136         /*
137          *      Read the entire file into memory for speed.
138          */
139         while(fgets(buffer, sizeof(buffer), fp) != NULL) {
140                 lineno++;
141                 if (strchr(buffer, '\n') == NULL) {
142                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
143                         pairlist_free(&pl);
144                         return -1;
145                 }
146                 if (buffer[0] == '#' || buffer[0] == '\n') continue;
147 parse_again:
148                 if(mode == FIND_MODE_NAME) {
149                         /*
150                          *      Find the entry starting with the users name
151                          */
152                         if (isspace(buffer[0]))  {
153                                 if (parsecode != T_EOL) {
154                                         radlog(L_ERR|L_CONS,
155                                             "%s[%d]: Unexpected trailing comma for entry %s",
156                                             file, lineno, entry);
157                                         fclose(fp);
158                                         return -1;
159                                 }
160                                 continue;
161                         }
162
163                         ptr = buffer;
164                         getword(&ptr, entry, sizeof(entry));
165
166                         /*
167                          *      Include another file if we see
168                          *      $INCLUDE filename
169                          */
170                         if (strcasecmp(entry, "$include") == 0) {
171                                 while(isspace(*ptr))
172                                         ptr++;
173                                 s = ptr;
174                                 while (!isspace(*ptr))
175                                         ptr++;
176                                 *ptr = 0;
177
178                                 /*
179                                  *      If it's an absolute pathname,
180                                  *      then use it verbatim.
181                                  *
182                                  *      If not, then make the $include
183                                  *      files *relative* to the current
184                                  *      file.
185                                  */
186                                 if (*s != '/') {
187                                         strNcpy(newfile, file,
188                                                 sizeof(newfile));
189                                         ptr = strrchr(newfile, '/');
190                                         strcpy(ptr + 1, s);
191                                         s = newfile;
192                                 }
193
194                                 t = NULL;
195                                 if (pairlist_read(s, &t, 0) != 0) {
196                                         pairlist_free(&pl);
197                                         radlog(L_ERR|L_CONS,
198                                             "%s[%d]: Could not open included file %s: %s",
199                                             file, lineno, s, strerror(errno));
200                                         fclose(fp);
201                                 return -1;
202                                 }
203                                 if (last)
204                                         last->next = t;
205                                 else
206                                         pl = t;
207                                 last = t;
208                                 while (last && last->next)
209                                         last = last->next;
210                                 continue;
211                         }
212
213                         /*
214                          *      Parse the check values
215                          */
216                         check_tmp = NULL;
217                         reply_tmp = NULL;
218                         old_lineno = lineno;
219                         parsecode = userparse(ptr, &check_tmp);
220                         if (parsecode < 0) {
221                                 pairlist_free(&pl);
222                                 radlog(L_ERR|L_CONS,
223                                 "%s[%d]: Parse error (check) for entry %s: %s",
224                                         file, lineno, entry, librad_errstr);
225                                 fclose(fp);
226                                 return -1;
227                         } else if (parsecode == T_COMMA) {
228                                 radlog(L_ERR|L_CONS,
229                                     "%s[%d]: Unexpected trailing comma in check item list for entry %s",
230                                     file, lineno, entry);
231                                 fclose(fp);
232                                 return -1;
233                         }
234                         mode = FIND_MODE_REPLY;
235                         parsecode = T_COMMA;
236                 }
237                 else {
238                         if(*buffer == ' ' || *buffer == '\t') {
239                                 if (parsecode != T_COMMA) {
240                                         radlog(L_ERR|L_CONS,
241                                 "%s[%d]: Syntax error: Previous line is missing a trailing comma for entry %s",
242                                                 file, lineno, entry);
243                                         fclose(fp);
244                                         return -1;
245                                 }
246
247                                 /*
248                                  *      Parse the reply values
249                                  */
250                                 parsecode = userparse(buffer, &reply_tmp);
251                                 if (parsecode < 0) {
252                                         pairlist_free(&pl);
253                                         radlog(L_ERR|L_CONS,
254                                 "%s[%d]: Parse error (reply) for entry %s: %s",
255                                             file, lineno, entry, librad_errstr);
256                                         fclose(fp);
257                                         return -1;
258                                 }
259                         }
260                         else {
261                                 /*
262                                  *      Done with this entry...
263                                  */
264                                 if ((t = malloc(sizeof(PAIR_LIST))) == NULL) {
265                                         perror(progname);
266                                         exit(1);
267                                 }
268                                 auth_type_fixup(check_tmp);
269                                 memset(t, 0, sizeof(*t));
270                                 t->name = strdup(entry);
271                                 t->check = check_tmp;
272                                 t->reply = reply_tmp;
273                                 t->lineno = old_lineno;
274                                 check_tmp = NULL;
275                                 reply_tmp = NULL;
276                                 if (last)
277                                         last->next = t;
278                                 else
279                                         pl = t;
280                                 last = t;
281
282                                 mode = FIND_MODE_NAME;
283                                 if (buffer[0] != 0)
284                                         goto parse_again;
285                         }
286                 }
287         }
288         /*
289          *      Make sure that we also read the last line of the file!
290          */
291         if (mode == FIND_MODE_REPLY) {
292                 buffer[0] = 0;
293                 goto parse_again;
294         }
295         fclose(fp);
296
297         *list = pl;
298         return 0;
299 }
300
301
302 /*
303  *      Debug code.
304  */
305 #if 0
306 static void debug_pair_list(PAIR_LIST *pl)
307 {
308         VALUE_PAIR *vp;
309
310         while(pl) {
311                 printf("Pair list: %s\n", pl->name);
312                 printf("** Check:\n");
313                 for(vp = pl->check; vp; vp = vp->next) {
314                         printf("    ");
315                         fprint_attr_val(stdout, vp);
316                         printf("\n");
317                 }
318                 printf("** Reply:\n");
319                 for(vp = pl->reply; vp; vp = vp->next) {
320                         printf("    ");
321                         fprint_attr_val(stdout, vp);
322                         printf("\n");
323                 }
324                 pl = pl->next;
325         }
326 }
327 #endif
328
329 #ifndef BUILDDBM /* HACK HACK */
330
331 /*
332  *      Free a REALM list.
333  */
334 static void realm_free(REALM *cl)
335 {
336         REALM *next;
337
338         while(cl) {
339                 next = cl->next;
340                 free(cl);
341                 cl = next;
342         }
343 }
344
345 /*
346  *      Read the realms file.
347  */
348 static int read_realms_file(const char *file)
349 {
350         FILE    *fp;
351         char    buffer[256];
352         char    realm[256];
353         char    hostnm[256];
354         char    opts[256];
355         char    *s, *p;
356         int     lineno = 0;
357         REALM   *c;
358         RADCLIENT *client;
359
360         realm_free(realms);
361         realms = NULL;
362
363         if ((fp = fopen(file, "r")) == NULL) {
364 #if 1 /* For now - realms file is not obligatory */
365                 return 0;
366 #else
367                 radlog(L_CONS|L_ERR, "cannot open %s", file);
368                 return -1;
369 #endif
370         }
371         while(fgets(buffer, 256, fp) != NULL) {
372                 lineno++;
373                 if (strchr(buffer, '\n') == NULL) {
374                         radlog(L_ERR, "%s[%d]: line too long", file, lineno);
375                         return -1;
376                 }
377                 if (buffer[0] == '#' || buffer[0] == '\n')
378                         continue;
379                 p = buffer;
380                 if (!getword(&p, realm, sizeof(realm)) ||
381                     !getword(&p, hostnm, sizeof(hostnm))) {
382                         radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
383                         continue;
384                 }
385
386                 if ((c = malloc(sizeof(REALM))) == NULL) {
387                         radlog(L_CONS|L_ERR, "%s[%d]: out of memory",
388                                 file, lineno);
389                         return -1;
390                 }
391                 memset(c, 0, sizeof(REALM));
392
393                 if ((s = strchr(hostnm, ':')) != NULL) {
394                         *s++ = 0;
395                         c->auth_port = atoi(s);
396                         c->acct_port = c->auth_port + 1;
397                 } else {
398                         c->auth_port = auth_port;
399                         c->acct_port = acct_port;
400                 }
401
402                 if (strcmp(hostnm, "LOCAL") == 0) {
403                         c->ipaddr = htonl(INADDR_LOOPBACK);
404                 } else {
405                         c->ipaddr = ip_getaddr(hostnm);
406                 }
407
408                 if (c->ipaddr == 0) {
409                         radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
410                             file, lineno, hostnm);
411                         return -1;
412                 }
413
414                 /*
415                  *      Find the remote server in the "clients" list.
416                  *      If we can't find it, there's a big problem...
417                  */
418                 client = client_find(c->ipaddr);
419                 if (client == NULL) {
420                         radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
421                             file, lineno, hostnm, realm);
422                         return -1;
423                 }
424                 memcpy(c->secret, client->secret, sizeof(c->secret));
425
426                 /*
427                  *      Double-check lengths to be sure they're sane
428                  */
429                 if (strlen(hostnm) >= sizeof(c->server)) {
430                         radlog(L_ERR, "%s[%d]: server name of length %d is greater than the allowed maximum of %d.",
431                             file, lineno,
432                             strlen(hostnm), sizeof(c->server) - 1);
433                         return -1;
434                 }
435                 if (strlen(realm) > sizeof(c->realm)) {
436                         radlog(L_ERR, "%s[%d]: realm of length %d is greater than the allowed maximum of %d.",
437                             file, lineno,
438                             strlen(realm), sizeof(c->realm) - 1);
439                         return -1;
440                 }
441
442                 /*
443                  *      OK, they're sane, copy them over.
444                  */
445                 strcpy(c->realm, realm);
446                 strcpy(c->server, hostnm);
447                 c->striprealm = TRUE;
448
449                 while (getword(&p, opts, sizeof(opts))) {
450                         if (strcmp(opts, "nostrip") == 0)
451                                 c->striprealm = FALSE;
452                         if (strstr(opts, "noacct") != NULL)
453                                 c->acct_port = 0;
454                         if (strstr(opts, "trusted") != NULL)
455                                 c->trusted = 1;
456                         if (strstr(opts, "notrealm") != NULL)
457                                 c->notrealm = 1;
458                         if (strstr(opts, "notsuffix") != NULL)
459                                 c->notrealm = 1;
460                 }
461
462                 c->next = realms;
463                 realms = c;
464         }
465         fclose(fp);
466
467         return 0;
468 }
469 #endif /* BUILDDBM */
470
471 /*
472  *      Find a realm in the REALM list.
473  */
474 REALM *realm_find(const char *realm)
475 {
476         REALM *cl;
477
478         /*
479          *      If we're passed a NULL realm pointer,
480          *      then look for a "NULL" realm string.
481          */
482         if (realm == NULL) {
483                 realm = "NULL";
484         }
485
486         for(cl = realms; cl; cl = cl->next)
487                 if (strcmp(cl->realm, realm) == 0)
488                         break;
489         if (cl) return cl;
490         for(cl = realms; cl; cl = cl->next)
491                 if (strcmp(cl->realm, "DEFAULT") == 0)
492                         break;
493         return cl;
494 }
495
496
497 /*
498  *      Find a realm for a proxy reply by proxy's IP
499  */
500 REALM *realm_findbyaddr(uint32_t ipaddr)
501 {
502         REALM *cl;
503
504         for(cl = realms; cl; cl = cl->next)
505                 if (ipaddr == cl->ipaddr)
506                         break;
507
508         return cl;
509 }
510
511
512 #ifndef BUILDDBM /* HACK HACK */
513
514 /*
515  *      (Re-) read the configuration files.
516  */
517 int read_config_files()
518 {
519         char buffer[256];
520
521         /* Initialize the dictionary */
522         DEBUG2("read_config_files:  reading dictionary");
523         if (dict_init(radius_dir, RADIUS_DICTIONARY) != 0) {
524                 radlog(L_ERR|L_CONS, "Errors reading dictionary: %s",
525                     librad_errstr);
526                 return -1;
527         }
528
529         sprintf(buffer, "%.200s/%.50s", radius_dir, RADIUS_CLIENTS);
530         DEBUG2("read_config_files:  reading clients");
531         if (read_clients_file(buffer) < 0) {
532                 radlog(L_ERR|L_CONS, "Errors reading clients");
533                 return -1;
534         }
535
536         sprintf(buffer, "%.200s/%.50s", radius_dir, RADIUS_REALMS);
537         DEBUG2("read_config_files:  reading realms");
538         if (read_realms_file(buffer) < 0) {
539                 radlog(L_ERR|L_CONS, "Errors reading realms");
540                 return -1;
541         }
542
543         sprintf(buffer, "%.200s/%.50s", radius_dir, RADIUS_NASLIST);
544         DEBUG2("read_config_files:  reading naslist");
545         if (read_naslist_file(buffer) < 0) {
546                 radlog(L_ERR|L_CONS, "Errors reading naslist");
547                 return -1;
548         }
549
550         DEBUG2("read_config_files:  entering modules setup");
551         if (setup_modules() < 0) {
552                 radlog(L_ERR|L_CONS, "Errors setting up modules");
553                 return -1;
554         }
555
556         return 0;
557 }
558
559 #endif