Revamp configuration handling:
[tinc] / src / conf.c
1 /*
2     conf.c -- configuration code
3     Copyright (C) 1998 Robert van der Meulen
4                   1998-2001 Ivo Timmermans <itimmermans@bigfoot.com>
5                   2000,2001 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.44 2001/10/10 20:34:27 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 strtoip */
46
47 #include "system.h"
48
49 avl_tree_t *config_tree;
50
51 int debug_lvl = 0;
52 int timeout = 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 /* Will be set if HUP signal is received. It will be processed when it is safe. */
57 int sighup = 0;
58
59 int config_compare(config_t *a, config_t *b)
60 {
61   int result;
62   
63   result = strcmp(a->variable, b->variable);
64   
65   if(result)
66     return result;
67
68   result = a->line - b->line;
69   
70   if(result)
71     return result;
72   else
73     return strcmp(a->file, b->file);
74 }
75
76 void init_configuration(avl_tree_t **config_tree)
77 {
78 cp
79   *config_tree = avl_alloc_tree((avl_compare_t)config_compare, (avl_action_t)free_config);
80 cp
81 }
82
83 void exit_configuration(avl_tree_t **config_tree)
84 {
85 cp
86   avl_delete_tree(*config_tree);
87   *config_tree = NULL;
88 cp
89 }
90
91 config_t *new_config(void)
92 {
93   config_t *cfg;
94 cp
95   cfg = (config_t *)xmalloc_and_zero(sizeof(*cfg));
96   
97   return cfg;
98 }
99
100 void free_config(config_t *cfg)
101 {
102 cp
103   if(cfg->variable)
104     free(cfg->variable);
105   if(cfg->value)
106     free(cfg->value);
107   if(cfg->file)
108     free(cfg->file);
109   free(cfg);
110 cp
111 }
112
113 void config_add(avl_tree_t *config_tree, config_t *cfg)
114 {
115 cp
116   avl_insert(config_tree, cfg);
117 cp
118 }
119
120 config_t *lookup_config(avl_tree_t *config_tree, char *variable)
121 {
122   config_t cfg, *found;
123 cp
124   cfg.variable = variable;
125   cfg.file = "";
126   cfg.line = 0;
127
128   found = avl_search_closest_greater(config_tree, &cfg);
129   
130   if(!strcmp(found->variable, variable))
131     return found;
132   else
133     return NULL;
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, "np"))
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->value, 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->value, 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_ip(config_t *cfg, ip_mask_t **result)
204 {
205   ip_mask_t *ip;
206 cp
207   if(!cfg)
208     return 0;
209
210   ip = strtoip(cfg->value);
211
212   if(ip)
213     {
214       *result = ip;
215       return 1;
216     }
217
218   syslog(LOG_ERR, _("IP address expected for configuration variable %s in %s line %d"),
219          cfg->value, cfg->file, cfg->line);
220   return 0;
221 }
222
223 /*
224   Read exactly one line and strip the trailing newline if any.  If the
225   file was on EOF, return NULL. Otherwise, return all the data in a
226   dynamically allocated buffer.
227   
228   If line is non-NULL, it will be used as an initial buffer, to avoid
229   unnecessary mallocing each time this function is called.  If buf is
230   given, and buf needs to be expanded, the var pointed to by buflen
231   will be increased.
232 */
233 char *readline(FILE *fp, char **buf, size_t *buflen)
234 {
235   char *newline = NULL;
236   char *p;
237   char *line; /* The array that contains everything that has been read
238                  so far */
239   char *idx; /* Read into this pointer, which points to an offset
240                 within line */
241   size_t size, newsize; /* The size of the current array pointed to by
242                            line */
243   size_t maxlen; /* Maximum number of characters that may be read with
244                     fgets.  This is newsize - oldsize. */
245
246   if(feof(fp))
247     return NULL;
248
249   if((buf != NULL) && (buflen != NULL))
250     {
251       size = *buflen;
252       line = *buf;
253     }
254   else
255     {
256       size = 100;
257       line = xmalloc(size);
258     }
259
260   maxlen = size;
261   idx = line;
262   *idx = 0;
263   for(;;)
264     {
265       errno = 0;
266       p = fgets(idx, maxlen, fp);
267       if(p == NULL)  /* EOF or error */
268         {
269           if(feof(fp))
270             break;
271
272           /* otherwise: error; let the calling function print an error
273              message if applicable */
274           free(line);
275           return NULL;
276         }
277
278       newline = strchr(p, '\n');
279       if(newline == NULL)
280         /* We haven't yet read everything to the end of the line */
281         {
282           newsize = size << 1;
283           line = xrealloc(line, newsize);
284           idx = &line[size - 1];
285           maxlen = newsize - size + 1;
286           size = newsize;
287         }
288       else
289         {
290           *newline = '\0'; /* kill newline */
291           break;  /* yay */
292         }
293     }
294
295   if((buf != NULL) && (buflen != NULL))
296     {
297       *buflen = size;
298       *buf = line;
299     }
300   return line;
301 }
302
303 /*
304   Parse a configuration file and put the results in the configuration tree
305   starting at *base.
306 */
307 int read_config_file(avl_tree_t *config_tree, const char *fname)
308 {
309   int err = -2; /* Parse error */
310   FILE *fp;
311   char *buffer, *line;
312   char *variable, *value;
313   int lineno = 0, ignore = 0;
314   config_t *cfg;
315   size_t bufsize;
316   
317 cp
318   if((fp = fopen (fname, "r")) == NULL)
319     {
320       syslog(LOG_ERR, _("Cannot open config file %s: %m"), fname);
321       return -3;
322     }
323
324   bufsize = 100;
325   buffer = xmalloc(bufsize);
326   
327   for(;;)
328     {
329       if((line = readline(fp, &buffer, &bufsize)) == NULL)
330         {
331           err = -1;
332           break;
333         }
334
335       if(feof(fp))
336         {
337           err = 0;
338           break;
339         }
340
341       lineno++;
342
343       if((variable = strtok(line, "\t =")) == NULL)
344         continue; /* no tokens on this line */
345
346       if(variable[0] == '#')
347         continue; /* comment: ignore */
348
349       if(!strcmp(variable, "-----BEGIN"))
350         ignore = 1;
351         
352       if(!ignore)
353         {
354           if(((value = strtok(NULL, "\t\n\r =")) == NULL) || value[0] == '#')
355             {
356               syslog(LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
357                       variable, lineno, fname);
358               break;
359             }
360
361           cfg = new_config();
362           cfg->variable = xstrdup(variable);
363           cfg->value = xstrdup(value);
364           cfg->file = xstrdup(fname);
365           cfg->line = lineno;
366
367           config_add(config_tree, cfg);
368        }
369
370       if(!strcmp(variable, "-----END"))
371         ignore = 0;
372     }
373
374   free(buffer);
375   fclose (fp);
376 cp
377   return err;
378 }
379
380 int read_server_config()
381 {
382   char *fname;
383   int x;
384 cp
385   asprintf(&fname, "%s/tinc.conf", confbase);
386   x = read_config_file(config_tree, fname);
387   if(x == -1) /* System error: complain */
388     {
389       syslog(LOG_ERR, _("Failed to read `%s': %m"),
390               fname);
391     }
392   free(fname);
393 cp
394   return x;  
395 }
396
397 int isadir(const char* f)
398 {
399   struct stat s;
400
401   if(stat(f, &s) < 0)
402     return 0;
403   else
404     return S_ISDIR(s.st_mode);
405 }
406
407 int is_safe_path(const char *file)
408 {
409   char *p;
410   const char *f;
411   char x;
412   struct stat s;
413   char l[MAXBUFSIZE];
414
415   if(*file != '/')
416     {
417       syslog(LOG_ERR, _("`%s' is not an absolute path"), file);
418       return 0;
419     }
420
421   p = strrchr(file, '/');
422   
423   if(p == file)         /* It's in the root */
424     p++;
425     
426   x = *p;
427   *p = '\0';
428
429   f = file;
430 check1:
431   if(lstat(f, &s) < 0)
432     {
433       syslog(LOG_ERR, _("Couldn't stat `%s': %m"),
434               f);
435       return 0;
436     }
437
438   if(s.st_uid != geteuid())
439     {
440       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
441               f, s.st_uid, geteuid());
442       return 0;
443     }
444
445   if(S_ISLNK(s.st_mode))
446     {
447       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
448               f);
449
450       if(readlink(f, l, MAXBUFSIZE) < 0)
451         {
452           syslog(LOG_ERR, _("Unable to read symbolic link `%s': %m"), f);
453           return 0;
454         }
455       
456       f = l;
457       goto check1;
458     }
459
460   *p = x;
461   f = file;
462   
463 check2:
464   if(lstat(f, &s) < 0 && errno != ENOENT)
465     {
466       syslog(LOG_ERR, _("Couldn't stat `%s': %m"),
467               f);
468       return 0;
469     }
470     
471   if(errno == ENOENT)
472     return 1;
473
474   if(s.st_uid != geteuid())
475     {
476       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
477               f, s.st_uid, geteuid());
478       return 0;
479     }
480
481   if(S_ISLNK(s.st_mode))
482     {
483       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
484               f);
485
486       if(readlink(f, l, MAXBUFSIZE) < 0)
487         {
488           syslog(LOG_ERR, _("Unable to read symbolic link `%s': %m"), f);
489           return 0;
490         }
491       
492       f = l;
493       goto check2;
494     }
495
496   if(s.st_mode & 0007)
497     {
498       /* Accessible by others */
499       syslog(LOG_ERR, _("`%s' has unsecure permissions"),
500               f);
501       return 0;
502     }
503   
504   return 1;
505 }
506
507 FILE *ask_and_safe_open(const char* filename, const char* what, const char* mode)
508 {
509   FILE *r;
510   char *directory;
511   char *fn;
512
513   /* Check stdin and stdout */
514   if(!isatty(0) || !isatty(1))
515     {
516       /* Argh, they are running us from a script or something.  Write
517          the files to the current directory and let them burn in hell
518          for ever. */
519       fn = xstrdup(filename);
520     }
521   else
522     {
523       /* Ask for a file and/or directory name. */
524       fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
525               what, filename);
526       fflush(stdout);
527
528       if((fn = readline(stdin, NULL, NULL)) == NULL)
529         {
530           fprintf(stderr, _("Error while reading stdin: %s\n"), strerror(errno));
531           return NULL;
532         }
533
534       if(strlen(fn) == 0)
535         /* User just pressed enter. */
536         fn = xstrdup(filename);
537     }
538
539   if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
540     {
541       /* The directory is a relative path or a filename. */
542       char *p;
543       
544       directory = get_current_dir_name();
545       asprintf(&p, "%s/%s", directory, fn);
546       free(fn);
547       free(directory);
548       fn = p;
549     }
550
551   umask(0077); /* Disallow everything for group and other */
552   
553   /* Open it first to keep the inode busy */
554   if((r = fopen(fn, mode)) == NULL)
555     {
556       fprintf(stderr, _("Error opening file `%s': %s\n"),
557               fn, strerror(errno));
558       free(fn);
559       return NULL;
560     }
561     
562   /* Then check the file for nasty attacks */
563   if(!is_safe_path(fn))  /* Do not permit any directories that are
564                             readable or writeable by other users. */
565     {
566       fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
567                         "I will not create or overwrite this file.\n"),
568                         fn);
569       fclose(r);
570       free(fn);
571       return NULL;
572     }
573
574   free(fn);
575
576   return r;
577 }