Use logging.h instead of syslog.h
[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.2.1 2002/04/14 09:30:16 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 <sys/stat.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36 #include <string.h>
37
38 #include <xalloc.h>
39 #include <utils.h> /* for cp */
40 #include <avl_tree.h>
41
42 #include "conf.h"
43 #include "netutl.h" /* for str2address */
44 #include "logging.h"
45
46 #include "system.h"
47
48 avl_tree_t *config_tree;
49
50 int pingtimeout = 0;             /* seconds before timeout */
51 char *confbase = NULL;           /* directory in which all config files are */
52 char *netname = NULL;            /* name of the vpn network */
53
54 int config_compare(config_t *a, config_t *b)
55 {
56   int result;
57
58   result = strcasecmp(a->variable, b->variable);
59
60   if(result)
61     return result;
62
63   result = a->line - b->line;
64
65   if(result)
66     return result;
67   else
68     return strcmp(a->file, b->file);
69 }
70
71 void init_configuration(avl_tree_t **config_tree)
72 {
73 cp
74   *config_tree = avl_alloc_tree((avl_compare_t)config_compare, (avl_action_t)free_config);
75 cp
76 }
77
78 void exit_configuration(avl_tree_t **config_tree)
79 {
80 cp
81   avl_delete_tree(*config_tree);
82   *config_tree = NULL;
83 cp
84 }
85
86 config_t *new_config(void)
87 {
88   config_t *cfg;
89 cp
90   cfg = (config_t *)xmalloc_and_zero(sizeof(*cfg));
91
92   return cfg;
93 }
94
95 void free_config(config_t *cfg)
96 {
97 cp
98   if(cfg->variable)
99     free(cfg->variable);
100   if(cfg->value)
101     free(cfg->value);
102   if(cfg->file)
103     free(cfg->file);
104   free(cfg);
105 cp
106 }
107
108 void config_add(avl_tree_t *config_tree, config_t *cfg)
109 {
110 cp
111   avl_insert(config_tree, cfg);
112 cp
113 }
114
115 config_t *lookup_config(avl_tree_t *config_tree, char *variable)
116 {
117   config_t cfg, *found;
118 cp
119   cfg.variable = variable;
120   cfg.file = "";
121   cfg.line = 0;
122
123   found = avl_search_closest_greater(config_tree, &cfg);
124
125   if(!found)
126     return NULL;
127
128   if(strcasecmp(found->variable, variable))
129     return NULL;
130
131   return found;
132 }
133
134 config_t *lookup_config_next(avl_tree_t *config_tree, config_t *cfg)
135 {
136   avl_node_t *node;
137   config_t *found;
138 cp
139   node = avl_search_node(config_tree, cfg);
140
141   if(node)
142     {
143       if(node->next)
144         {
145           found = (config_t *)node->next->data;
146           if(!strcasecmp(found->variable, cfg->variable))
147             return found;
148         }
149     }
150
151   return NULL;
152 }
153
154 int get_config_bool(config_t *cfg, int *result)
155 {
156 cp
157   if(!cfg)
158     return 0;
159
160   if(!strcasecmp(cfg->value, "yes"))
161     {
162       *result = 1;
163       return 1;
164     }
165   else if(!strcasecmp(cfg->value, "no"))
166     {
167       *result = 0;
168       return 1;
169     }
170
171   syslog(LOG_ERR, _("\"yes\" or \"no\" expected for configuration variable %s in %s line %d"),
172          cfg->variable, cfg->file, cfg->line);
173
174   return 0;
175 }
176
177 int get_config_int(config_t *cfg, int *result)
178 {
179 cp
180   if(!cfg)
181     return 0;
182
183   if(sscanf(cfg->value, "%d", result) == 1)
184     return 1;
185
186   syslog(LOG_ERR, _("Integer expected for configuration variable %s in %s line %d"),
187          cfg->variable, cfg->file, cfg->line);
188   return 0;
189 }
190
191 int get_config_string(config_t *cfg, char **result)
192 {
193 cp
194   if(!cfg)
195     return 0;
196
197   *result = xstrdup(cfg->value);
198   return 1;
199 }
200
201 int get_config_address(config_t *cfg, struct addrinfo **result)
202 {
203   struct addrinfo *ai;
204 cp
205   if(!cfg)
206     return 0;
207
208   ai = str2addrinfo(cfg->value, NULL, 0);
209
210   if(ai)
211     {
212       *result = ai;
213       return 1;
214     }
215
216   syslog(LOG_ERR, _("Hostname or IP address expected for configuration variable %s in %s line %d"),
217          cfg->variable, cfg->file, cfg->line);
218   return 0;
219 }
220
221 int get_config_port(config_t *cfg, port_t *result)
222 {
223 cp
224   if(!cfg)
225     return 0;
226
227   if(sscanf(cfg->value, "%hu", result) == 1)
228     {
229       *result = htons(*result);
230       return 1;
231     }
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) && maskcheck((char *)&subnet->net.ipv4.address, subnet->net.ipv4.prefixlength, sizeof(ipv4_t)))
257      || ((subnet->type == SUBNET_IPV6) && maskcheck((char *)&subnet->net.ipv6.address, subnet->net.ipv6.prefixlength, sizeof(ipv6_t))))
258     {
259       syslog(LOG_ERR, _("Network address and prefix 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: %s"), fname, strerror(errno));
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': %s"), fname, strerror(errno));
437     }
438   free(fname);
439 cp
440   return x;
441 }
442
443 int isadir(const char* f)
444 {
445   struct stat s;
446
447   if(stat(f, &s) < 0)
448     return 0;
449   else
450     return S_ISDIR(s.st_mode);
451 }
452
453 int is_safe_path(const char *file)
454 {
455   char *p;
456   const char *f;
457   char x;
458   struct stat s;
459   char l[MAXBUFSIZE];
460
461   if(*file != '/')
462     {
463       syslog(LOG_ERR, _("`%s' is not an absolute path"), file);
464       return 0;
465     }
466
467   p = strrchr(file, '/');
468
469   if(p == file)         /* It's in the root */
470     p++;
471
472   x = *p;
473   *p = '\0';
474
475   f = file;
476 check1:
477   if(lstat(f, &s) < 0)
478     {
479       syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
480       return 0;
481     }
482
483   if(s.st_uid != geteuid())
484     {
485       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
486               f, s.st_uid, geteuid());
487       return 0;
488     }
489
490   if(S_ISLNK(s.st_mode))
491     {
492       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
493               f);
494
495       if(readlink(f, l, MAXBUFSIZE) < 0)
496         {
497           syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f, strerror(errno));
498           return 0;
499         }
500
501       f = l;
502       goto check1;
503     }
504
505   *p = x;
506   f = file;
507
508 check2:
509   if(lstat(f, &s) < 0 && errno != ENOENT)
510     {
511       syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
512       return 0;
513     }
514
515   if(errno == ENOENT)
516     return 1;
517
518   if(s.st_uid != geteuid())
519     {
520       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
521               f, s.st_uid, geteuid());
522       return 0;
523     }
524
525   if(S_ISLNK(s.st_mode))
526     {
527       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
528               f);
529
530       if(readlink(f, l, MAXBUFSIZE) < 0)
531         {
532           syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f, strerror(errno));
533           return 0;
534         }
535
536       f = l;
537       goto check2;
538     }
539
540   if(s.st_mode & 0007)
541     {
542       /* Accessible by others */
543       syslog(LOG_ERR, _("`%s' has unsecure permissions"),
544               f);
545       return 0;
546     }
547
548   return 1;
549 }
550
551 FILE *ask_and_safe_open(const char* filename, const char* what, const char* mode)
552 {
553   FILE *r;
554   char *directory;
555   char *fn;
556
557   /* Check stdin and stdout */
558   if(!isatty(0) || !isatty(1))
559     {
560       /* Argh, they are running us from a script or something.  Write
561          the files to the current directory and let them burn in hell
562          for ever. */
563       fn = xstrdup(filename);
564     }
565   else
566     {
567       /* Ask for a file and/or directory name. */
568       fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
569               what, filename);
570       fflush(stdout);
571
572       if((fn = readline(stdin, NULL, NULL)) == NULL)
573         {
574           fprintf(stderr, _("Error while reading stdin: %s\n"), strerror(errno));
575           return NULL;
576         }
577
578       if(strlen(fn) == 0)
579         /* User just pressed enter. */
580         fn = xstrdup(filename);
581     }
582
583   if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
584     {
585       /* The directory is a relative path or a filename. */
586       char *p;
587
588       directory = get_current_dir_name();
589       asprintf(&p, "%s/%s", directory, fn);
590       free(fn);
591       free(directory);
592       fn = p;
593     }
594
595   umask(0077); /* Disallow everything for group and other */
596
597   /* Open it first to keep the inode busy */
598   if((r = fopen(fn, mode)) == NULL)
599     {
600       fprintf(stderr, _("Error opening file `%s': %s\n"),
601               fn, strerror(errno));
602       free(fn);
603       return NULL;
604     }
605
606   /* Then check the file for nasty attacks */
607   if(!is_safe_path(fn))  /* Do not permit any directories that are
608                             readable or writeable by other users. */
609     {
610       fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
611                         "I will not create or overwrite this file.\n"),
612                         fn);
613       fclose(r);
614       free(fn);
615       return NULL;
616     }
617
618   free(fn);
619
620   return r;
621 }