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