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