Cleanups:
[oweals/tinc.git] / src / conf.c
1 /*
2     conf.c -- configuration code
3     Copyright (C) 1998 Robert van der Meulen
4                   1998-2002 Ivo Timmermans <ivo@o2w.nl>
5                   2000-2002 Guus Sliepen <guus@sliepen.eu.org>
6                   2000 Cris van Pelt <tribbel@arise.dhs.org>
7
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22     $Id: conf.c,v 1.9.4.58 2002/09/09 19:39:55 guus Exp $
23 */
24
25 #include "config.h"
26
27 #include <ctype.h>
28 #include <errno.h>
29 #include <netdb.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <syslog.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37 #include <syslog.h>
38 #include <string.h>
39
40 #include <xalloc.h>
41 #include <utils.h> /* for cp */
42 #include <avl_tree.h>
43
44 #include "conf.h"
45 #include "netutl.h" /* for str2address */
46
47 #include "system.h"
48
49 avl_tree_t *config_tree;
50
51 int debug_lvl = 0;
52 int pingtimeout = 0;             /* seconds before timeout */
53 char *confbase = NULL;           /* directory in which all config files are */
54 char *netname = NULL;            /* name of the vpn network */
55
56 int config_compare(config_t *a, config_t *b)
57 {
58   int result;
59
60   result = strcasecmp(a->variable, b->variable);
61
62   if(result)
63     return result;
64
65   result = a->line - b->line;
66
67   if(result)
68     return result;
69   else
70     return strcmp(a->file, b->file);
71 }
72
73 void init_configuration(avl_tree_t **config_tree)
74 {
75   cp();
76   *config_tree = avl_alloc_tree((avl_compare_t)config_compare, (avl_action_t)free_config);
77   cp();
78 }
79
80 void exit_configuration(avl_tree_t **config_tree)
81 {
82   cp();
83   avl_delete_tree(*config_tree);
84   *config_tree = NULL;
85   cp();
86 }
87
88 config_t *new_config(void)
89 {
90   config_t *cfg;
91   cp();
92   cfg = (config_t *)xmalloc_and_zero(sizeof(*cfg));
93
94   return cfg;
95 }
96
97 void free_config(config_t *cfg)
98 {
99   cp();
100   if(cfg->variable)
101     free(cfg->variable);
102   if(cfg->value)
103     free(cfg->value);
104   if(cfg->file)
105     free(cfg->file);
106   free(cfg);
107   cp();
108 }
109
110 void config_add(avl_tree_t *config_tree, config_t *cfg)
111 {
112   cp();
113   avl_insert(config_tree, cfg);
114   cp();
115 }
116
117 config_t *lookup_config(avl_tree_t *config_tree, char *variable)
118 {
119   config_t cfg, *found;
120   cp();
121   cfg.variable = variable;
122   cfg.file = "";
123   cfg.line = 0;
124
125   found = avl_search_closest_greater(config_tree, &cfg);
126
127   if(!found)
128     return NULL;
129
130   if(strcasecmp(found->variable, variable))
131     return NULL;
132
133   return found;
134 }
135
136 config_t *lookup_config_next(avl_tree_t *config_tree, config_t *cfg)
137 {
138   avl_node_t *node;
139   config_t *found;
140   cp();
141   node = avl_search_node(config_tree, cfg);
142
143   if(node)
144     {
145       if(node->next)
146         {
147           found = (config_t *)node->next->data;
148           if(!strcasecmp(found->variable, cfg->variable))
149             return found;
150         }
151     }
152
153   return NULL;
154 }
155
156 int get_config_bool(config_t *cfg, int *result)
157 {
158   cp();
159   if(!cfg)
160     return 0;
161
162   if(!strcasecmp(cfg->value, "yes"))
163     {
164       *result = 1;
165       return 1;
166     }
167   else if(!strcasecmp(cfg->value, "no"))
168     {
169       *result = 0;
170       return 1;
171     }
172
173   syslog(LOG_ERR, _("\"yes\" or \"no\" expected for configuration variable %s in %s line %d"),
174          cfg->variable, cfg->file, cfg->line);
175
176   return 0;
177 }
178
179 int get_config_int(config_t *cfg, int *result)
180 {
181   cp();
182   if(!cfg)
183     return 0;
184
185   if(sscanf(cfg->value, "%d", result) == 1)
186     return 1;
187
188   syslog(LOG_ERR, _("Integer expected for configuration variable %s in %s line %d"),
189          cfg->variable, cfg->file, cfg->line);
190   return 0;
191 }
192
193 int get_config_string(config_t *cfg, char **result)
194 {
195   cp();
196   if(!cfg)
197     return 0;
198
199   *result = xstrdup(cfg->value);
200   return 1;
201 }
202
203 int get_config_address(config_t *cfg, struct addrinfo **result)
204 {
205   struct addrinfo *ai;
206   cp();
207   if(!cfg)
208     return 0;
209
210   ai = str2addrinfo(cfg->value, NULL, 0);
211
212   if(ai)
213     {
214       *result = ai;
215       return 1;
216     }
217
218   syslog(LOG_ERR, _("Hostname or IP address expected for configuration variable %s in %s line %d"),
219          cfg->variable, cfg->file, cfg->line);
220   return 0;
221 }
222
223 int get_config_port(config_t *cfg, port_t *result)
224 {
225   cp();
226   if(!cfg)
227     return 0;
228
229   if(sscanf(cfg->value, "%hu", result) == 1)
230     {
231       *result = htons(*result);
232       return 1;
233     }
234
235   syslog(LOG_ERR, _("Port number expected for configuration variable %s in %s line %d"),
236          cfg->variable, cfg->file, cfg->line);
237   return 0;
238 }
239
240 int get_config_subnet(config_t *cfg, subnet_t **result)
241 {
242   subnet_t *subnet;
243   cp();
244   if(!cfg)
245     return 0;
246
247   subnet = str2net(cfg->value);
248
249   if(!subnet)
250     {
251       syslog(LOG_ERR, _("Subnet expected for configuration variable %s in %s line %d"),
252              cfg->variable, cfg->file, cfg->line);
253       return 0;
254     }
255
256   /* Teach newbies what subnets are... */
257
258   if(((subnet->type == SUBNET_IPV4) && maskcheck(&subnet->net.ipv4.address, subnet->net.ipv4.prefixlength, sizeof(ipv4_t)))
259      || ((subnet->type == SUBNET_IPV6) && maskcheck(&subnet->net.ipv6.address, subnet->net.ipv6.prefixlength, sizeof(ipv6_t))))
260     {
261       syslog(LOG_ERR, _("Network address and prefix length do not match for configuration variable %s in %s line %d"),
262              cfg->variable, cfg->file, cfg->line);
263       free(subnet);
264       return 0;
265     }
266
267   *result = subnet;
268
269   return 1;
270 }
271
272 /*
273   Read exactly one line and strip the trailing newline if any.  If the
274   file was on EOF, return NULL. Otherwise, return all the data in a
275   dynamically allocated buffer.
276
277   If line is non-NULL, it will be used as an initial buffer, to avoid
278   unnecessary mallocing each time this function is called.  If buf is
279   given, and buf needs to be expanded, the var pointed to by buflen
280   will be increased.
281 */
282 char *readline(FILE *fp, char **buf, size_t *buflen)
283 {
284   char *newline = NULL;
285   char *p;
286   char *line; /* The array that contains everything that has been read
287                  so far */
288   char *idx; /* Read into this pointer, which points to an offset
289                 within line */
290   size_t size, newsize; /* The size of the current array pointed to by
291                            line */
292   size_t maxlen; /* Maximum number of characters that may be read with
293                     fgets.  This is newsize - oldsize. */
294
295   if(feof(fp))
296     return NULL;
297
298   if(buf && buflen)
299     {
300       size = *buflen;
301       line = *buf;
302     }
303   else
304     {
305       size = 100;
306       line = xmalloc(size);
307     }
308
309   maxlen = size;
310   idx = line;
311   *idx = 0;
312   for(;;)
313     {
314       errno = 0;
315       p = fgets(idx, maxlen, fp);
316       if(!p)  /* EOF or error */
317         {
318           if(feof(fp))
319             break;
320
321           /* otherwise: error; let the calling function print an error
322              message if applicable */
323           free(line);
324           return NULL;
325         }
326
327       newline = strchr(p, '\n');
328       if(!newline)
329         /* We haven't yet read everything to the end of the line */
330         {
331           newsize = size << 1;
332           line = xrealloc(line, newsize);
333           idx = &line[size - 1];
334           maxlen = newsize - size + 1;
335           size = newsize;
336         }
337       else
338         {
339           *newline = '\0'; /* kill newline */
340           break;  /* yay */
341         }
342     }
343
344   if(buf && buflen)
345     {
346       *buflen = size;
347       *buf = line;
348     }
349   return line;
350 }
351
352 /*
353   Parse a configuration file and put the results in the configuration tree
354   starting at *base.
355 */
356 int read_config_file(avl_tree_t *config_tree, const char *fname)
357 {
358   int err = -2; /* Parse error */
359   FILE *fp;
360   char *buffer, *line;
361   char *variable, *value;
362   int lineno = 0, ignore = 0;
363   config_t *cfg;
364   size_t bufsize;
365
366   cp();
367   fp = fopen (fname, "r");
368
369   if(!fp)
370     {
371       syslog(LOG_ERR, _("Cannot open config file %s: %s"), fname, strerror(errno));
372       return -3;
373     }
374
375   bufsize = 100;
376   buffer = xmalloc(bufsize);
377
378   for(;;)
379     {
380       line = readline(fp, &buffer, &bufsize);
381
382       if(!line)
383         {
384           err = -1;
385           break;
386         }
387
388       if(feof(fp))
389         {
390           err = 0;
391           break;
392         }
393
394       lineno++;
395
396       variable = strtok(line, "\t =");
397       
398       if(!variable)
399         continue; /* no tokens on this line */
400
401       if(variable[0] == '#')
402         continue; /* comment: ignore */
403
404       if(!strcmp(variable, "-----BEGIN"))
405         ignore = 1;
406
407       if(!ignore)
408         {
409           value = strtok(NULL, "\t\n\r =");
410
411           if(!value || value[0] == '#')
412             {
413               syslog(LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
414                       variable, lineno, fname);
415               break;
416             }
417
418           cfg = new_config();
419           cfg->variable = xstrdup(variable);
420           cfg->value = xstrdup(value);
421           cfg->file = xstrdup(fname);
422           cfg->line = lineno;
423
424           config_add(config_tree, cfg);
425        }
426
427       if(!strcmp(variable, "-----END"))
428         ignore = 0;
429     }
430
431   free(buffer);
432   fclose (fp);
433   cp();
434   return err;
435 }
436
437 int read_server_config()
438 {
439   char *fname;
440   int x;
441   cp();
442   asprintf(&fname, "%s/tinc.conf", confbase);
443   x = read_config_file(config_tree, fname);
444   if(x == -1) /* System error: complain */
445     {
446       syslog(LOG_ERR, _("Failed to read `%s': %s"), fname, strerror(errno));
447     }
448   free(fname);
449   cp();
450   return x;
451 }
452
453 int isadir(const char* f)
454 {
455   struct stat s;
456
457   if(stat(f, &s) < 0)
458     return 0;
459   else
460     return S_ISDIR(s.st_mode);
461 }
462
463 int is_safe_path(const char *file)
464 {
465   char *p;
466   const char *f;
467   char x;
468   struct stat s;
469   char l[MAXBUFSIZE];
470
471   if(*file != '/')
472     {
473       syslog(LOG_ERR, _("`%s' is not an absolute path"), file);
474       return 0;
475     }
476
477   p = strrchr(file, '/');
478
479   if(p == file)         /* It's in the root */
480     p++;
481
482   x = *p;
483   *p = '\0';
484
485   f = file;
486 check1:
487   if(lstat(f, &s) < 0)
488     {
489       syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
490       return 0;
491     }
492
493   if(s.st_uid != geteuid())
494     {
495       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
496               f, s.st_uid, geteuid());
497       return 0;
498     }
499
500   if(S_ISLNK(s.st_mode))
501     {
502       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
503               f);
504
505       if(readlink(f, l, MAXBUFSIZE) < 0)
506         {
507           syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f, strerror(errno));
508           return 0;
509         }
510
511       f = l;
512       goto check1;
513     }
514
515   *p = x;
516   f = file;
517
518 check2:
519   if(lstat(f, &s) < 0 && errno != ENOENT)
520     {
521       syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
522       return 0;
523     }
524
525   if(errno == ENOENT)
526     return 1;
527
528   if(s.st_uid != geteuid())
529     {
530       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
531               f, s.st_uid, geteuid());
532       return 0;
533     }
534
535   if(S_ISLNK(s.st_mode))
536     {
537       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
538               f);
539
540       if(readlink(f, l, MAXBUFSIZE) < 0)
541         {
542           syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f, strerror(errno));
543           return 0;
544         }
545
546       f = l;
547       goto check2;
548     }
549
550   if(s.st_mode & 0007)
551     {
552       /* Accessible by others */
553       syslog(LOG_ERR, _("`%s' has unsecure permissions"),
554               f);
555       return 0;
556     }
557
558   return 1;
559 }
560
561 FILE *ask_and_safe_open(const char* filename, const char* what, const char* mode)
562 {
563   FILE *r;
564   char *directory;
565   char *fn;
566
567   /* Check stdin and stdout */
568   if(!isatty(0) || !isatty(1))
569     {
570       /* Argh, they are running us from a script or something.  Write
571          the files to the current directory and let them burn in hell
572          for ever. */
573       fn = xstrdup(filename);
574     }
575   else
576     {
577       /* Ask for a file and/or directory name. */
578       fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
579               what, filename);
580       fflush(stdout);
581
582       fn = readline(stdin, NULL, NULL);
583
584       if(!fn)
585         {
586           fprintf(stderr, _("Error while reading stdin: %s\n"), strerror(errno));
587           return NULL;
588         }
589
590       if(!strlen(fn))
591         /* User just pressed enter. */
592         fn = xstrdup(filename);
593     }
594
595   if(!strchr(fn, '/') || fn[0] != '/')
596     {
597       /* The directory is a relative path or a filename. */
598       char *p;
599
600       directory = get_current_dir_name();
601       asprintf(&p, "%s/%s", directory, fn);
602       free(fn);
603       free(directory);
604       fn = p;
605     }
606
607   umask(0077); /* Disallow everything for group and other */
608
609   /* Open it first to keep the inode busy */
610
611   r = fopen(fn, mode);
612
613   if(!r)
614     {
615       fprintf(stderr, _("Error opening file `%s': %s\n"),
616               fn, strerror(errno));
617       free(fn);
618       return NULL;
619     }
620
621   /* Then check the file for nasty attacks */
622   if(!is_safe_path(fn))  /* Do not permit any directories that are
623                             readable or writeable by other users. */
624     {
625       fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
626                         "I will not create or overwrite this file.\n"),
627                         fn);
628       fclose(r);
629       free(fn);
630       return NULL;
631     }
632
633   free(fn);
634
635   return r;
636 }