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