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