fc8237201cba67075f3aed67836fecd29818d079
[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 <itimmermans@bigfoot.com>
5                   2000-2002 Guus Sliepen <guus@sliepen.warande.net>
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.52 2002/02/10 21:57:53 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 = strcmp(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(strcmp(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(!strcmp(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 = cfg->value;
200   return 1;
201 }
202
203 int get_config_address(config_t *cfg, ipv4_t **result)
204 {
205   ipv4_t *ip;
206 cp
207   if(!cfg)
208     return 0;
209
210   ip = xmalloc(sizeof(*ip));
211   *ip = str2address(cfg->value);
212
213   if(ip)
214     {
215       *result = ip;
216       return 1;
217     }
218
219   syslog(LOG_ERR, _("IP address expected for configuration variable %s in %s line %d"),
220          cfg->variable, cfg->file, cfg->line);
221   return 0;
222 }
223
224 int get_config_port(config_t *cfg, port_t *result)
225 {
226 cp
227   if(!cfg)
228     return 0;
229
230   if(sscanf(cfg->value, "%hu", result) == 1)
231     return 1;
232     
233   syslog(LOG_ERR, _("Port number expected for configuration variable %s in %s line %d"),
234          cfg->variable, cfg->file, cfg->line);
235   return 0;
236 }
237
238 int get_config_subnet(config_t *cfg, subnet_t **result)
239 {
240   subnet_t *subnet;
241 cp
242   if(!cfg)
243     return 0;
244
245   subnet = str2net(cfg->value);
246
247   if(!subnet)
248     {
249       syslog(LOG_ERR, _("Subnet expected for configuration variable %s in %s line %d"),
250              cfg->variable, cfg->file, cfg->line);
251       return 0;
252     }
253   
254   /* Teach newbies what subnets are... */
255
256   if(subnet->type == SUBNET_IPV4)
257     if((subnet->net.ipv4.address & subnet->net.ipv4.mask) != subnet->net.ipv4.address)
258       {
259         syslog(LOG_ERR, _("Network address and mask length do not match for configuration variable %s in %s line %d"),
260                cfg->variable, cfg->file, cfg->line);
261         free(subnet);
262         return 0;
263       }
264
265   *result = subnet;
266   
267   return 1;
268 }
269
270 /*
271   Read exactly one line and strip the trailing newline if any.  If the
272   file was on EOF, return NULL. Otherwise, return all the data in a
273   dynamically allocated buffer.
274   
275   If line is non-NULL, it will be used as an initial buffer, to avoid
276   unnecessary mallocing each time this function is called.  If buf is
277   given, and buf needs to be expanded, the var pointed to by buflen
278   will be increased.
279 */
280 char *readline(FILE *fp, char **buf, size_t *buflen)
281 {
282   char *newline = NULL;
283   char *p;
284   char *line; /* The array that contains everything that has been read
285                  so far */
286   char *idx; /* Read into this pointer, which points to an offset
287                 within line */
288   size_t size, newsize; /* The size of the current array pointed to by
289                            line */
290   size_t maxlen; /* Maximum number of characters that may be read with
291                     fgets.  This is newsize - oldsize. */
292
293   if(feof(fp))
294     return NULL;
295
296   if((buf != NULL) && (buflen != NULL))
297     {
298       size = *buflen;
299       line = *buf;
300     }
301   else
302     {
303       size = 100;
304       line = xmalloc(size);
305     }
306
307   maxlen = size;
308   idx = line;
309   *idx = 0;
310   for(;;)
311     {
312       errno = 0;
313       p = fgets(idx, maxlen, fp);
314       if(p == NULL)  /* EOF or error */
315         {
316           if(feof(fp))
317             break;
318
319           /* otherwise: error; let the calling function print an error
320              message if applicable */
321           free(line);
322           return NULL;
323         }
324
325       newline = strchr(p, '\n');
326       if(newline == NULL)
327         /* We haven't yet read everything to the end of the line */
328         {
329           newsize = size << 1;
330           line = xrealloc(line, newsize);
331           idx = &line[size - 1];
332           maxlen = newsize - size + 1;
333           size = newsize;
334         }
335       else
336         {
337           *newline = '\0'; /* kill newline */
338           break;  /* yay */
339         }
340     }
341
342   if((buf != NULL) && (buflen != NULL))
343     {
344       *buflen = size;
345       *buf = line;
346     }
347   return line;
348 }
349
350 /*
351   Parse a configuration file and put the results in the configuration tree
352   starting at *base.
353 */
354 int read_config_file(avl_tree_t *config_tree, const char *fname)
355 {
356   int err = -2; /* Parse error */
357   FILE *fp;
358   char *buffer, *line;
359   char *variable, *value;
360   int lineno = 0, ignore = 0;
361   config_t *cfg;
362   size_t bufsize;
363   
364 cp
365   if((fp = fopen (fname, "r")) == NULL)
366     {
367       syslog(LOG_ERR, _("Cannot open config file %s: %m"), fname);
368       return -3;
369     }
370
371   bufsize = 100;
372   buffer = xmalloc(bufsize);
373   
374   for(;;)
375     {
376       if((line = readline(fp, &buffer, &bufsize)) == NULL)
377         {
378           err = -1;
379           break;
380         }
381
382       if(feof(fp))
383         {
384           err = 0;
385           break;
386         }
387
388       lineno++;
389
390       if((variable = strtok(line, "\t =")) == NULL)
391         continue; /* no tokens on this line */
392
393       if(variable[0] == '#')
394         continue; /* comment: ignore */
395
396       if(!strcmp(variable, "-----BEGIN"))
397         ignore = 1;
398         
399       if(!ignore)
400         {
401           if(((value = strtok(NULL, "\t\n\r =")) == NULL) || value[0] == '#')
402             {
403               syslog(LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
404                       variable, lineno, fname);
405               break;
406             }
407
408           cfg = new_config();
409           cfg->variable = xstrdup(variable);
410           cfg->value = xstrdup(value);
411           cfg->file = xstrdup(fname);
412           cfg->line = lineno;
413
414           config_add(config_tree, cfg);
415        }
416
417       if(!strcmp(variable, "-----END"))
418         ignore = 0;
419     }
420
421   free(buffer);
422   fclose (fp);
423 cp
424   return err;
425 }
426
427 int read_server_config()
428 {
429   char *fname;
430   int x;
431 cp
432   asprintf(&fname, "%s/tinc.conf", confbase);
433   x = read_config_file(config_tree, fname);
434   if(x == -1) /* System error: complain */
435     {
436       syslog(LOG_ERR, _("Failed to read `%s': %m"),
437               fname);
438     }
439   free(fname);
440 cp
441   return x;  
442 }
443
444 int isadir(const char* f)
445 {
446   struct stat s;
447
448   if(stat(f, &s) < 0)
449     return 0;
450   else
451     return S_ISDIR(s.st_mode);
452 }
453
454 int is_safe_path(const char *file)
455 {
456   char *p;
457   const char *f;
458   char x;
459   struct stat s;
460   char l[MAXBUFSIZE];
461
462   if(*file != '/')
463     {
464       syslog(LOG_ERR, _("`%s' is not an absolute path"), file);
465       return 0;
466     }
467
468   p = strrchr(file, '/');
469   
470   if(p == file)         /* It's in the root */
471     p++;
472     
473   x = *p;
474   *p = '\0';
475
476   f = file;
477 check1:
478   if(lstat(f, &s) < 0)
479     {
480       syslog(LOG_ERR, _("Couldn't stat `%s': %m"),
481               f);
482       return 0;
483     }
484
485   if(s.st_uid != geteuid())
486     {
487       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
488               f, s.st_uid, geteuid());
489       return 0;
490     }
491
492   if(S_ISLNK(s.st_mode))
493     {
494       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
495               f);
496
497       if(readlink(f, l, MAXBUFSIZE) < 0)
498         {
499           syslog(LOG_ERR, _("Unable to read symbolic link `%s': %m"), f);
500           return 0;
501         }
502       
503       f = l;
504       goto check1;
505     }
506
507   *p = x;
508   f = file;
509   
510 check2:
511   if(lstat(f, &s) < 0 && errno != ENOENT)
512     {
513       syslog(LOG_ERR, _("Couldn't stat `%s': %m"),
514               f);
515       return 0;
516     }
517     
518   if(errno == ENOENT)
519     return 1;
520
521   if(s.st_uid != geteuid())
522     {
523       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
524               f, s.st_uid, geteuid());
525       return 0;
526     }
527
528   if(S_ISLNK(s.st_mode))
529     {
530       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
531               f);
532
533       if(readlink(f, l, MAXBUFSIZE) < 0)
534         {
535           syslog(LOG_ERR, _("Unable to read symbolic link `%s': %m"), f);
536           return 0;
537         }
538       
539       f = l;
540       goto check2;
541     }
542
543   if(s.st_mode & 0007)
544     {
545       /* Accessible by others */
546       syslog(LOG_ERR, _("`%s' has unsecure permissions"),
547               f);
548       return 0;
549     }
550   
551   return 1;
552 }
553
554 FILE *ask_and_safe_open(const char* filename, const char* what, const char* mode)
555 {
556   FILE *r;
557   char *directory;
558   char *fn;
559
560   /* Check stdin and stdout */
561   if(!isatty(0) || !isatty(1))
562     {
563       /* Argh, they are running us from a script or something.  Write
564          the files to the current directory and let them burn in hell
565          for ever. */
566       fn = xstrdup(filename);
567     }
568   else
569     {
570       /* Ask for a file and/or directory name. */
571       fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
572               what, filename);
573       fflush(stdout);
574
575       if((fn = readline(stdin, NULL, NULL)) == NULL)
576         {
577           fprintf(stderr, _("Error while reading stdin: %s\n"), strerror(errno));
578           return NULL;
579         }
580
581       if(strlen(fn) == 0)
582         /* User just pressed enter. */
583         fn = xstrdup(filename);
584     }
585
586   if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
587     {
588       /* The directory is a relative path or a filename. */
589       char *p;
590       
591       directory = get_current_dir_name();
592       asprintf(&p, "%s/%s", directory, fn);
593       free(fn);
594       free(directory);
595       fn = p;
596     }
597
598   umask(0077); /* Disallow everything for group and other */
599   
600   /* Open it first to keep the inode busy */
601   if((r = fopen(fn, mode)) == NULL)
602     {
603       fprintf(stderr, _("Error opening file `%s': %s\n"),
604               fn, strerror(errno));
605       free(fn);
606       return NULL;
607     }
608     
609   /* Then check the file for nasty attacks */
610   if(!is_safe_path(fn))  /* Do not permit any directories that are
611                             readable or writeable by other users. */
612     {
613       fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
614                         "I will not create or overwrite this file.\n"),
615                         fn);
616       fclose(r);
617       free(fn);
618       return NULL;
619     }
620
621   free(fn);
622
623   return r;
624 }