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