port_t isn't used anymore and conflicts with MacOS/X headers.
[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.61 2002/09/15 12:26:24 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_subnet(config_t *cfg, subnet_t ** result)
232 {
233         subnet_t *subnet;
234
235         cp();
236
237         if(!cfg)
238                 return 0;
239
240         subnet = str2net(cfg->value);
241
242         if(!subnet) {
243                 syslog(LOG_ERR, _("Subnet expected for configuration variable %s in %s line %d"),
244                            cfg->variable, cfg->file, cfg->line);
245                 return 0;
246         }
247
248         /* Teach newbies what subnets are... */
249
250         if(((subnet->type == SUBNET_IPV4)
251                 && maskcheck(&subnet->net.ipv4.address, subnet->net.ipv4.prefixlength, sizeof(ipv4_t)))
252                 || ((subnet->type == SUBNET_IPV6)
253                 && maskcheck(&subnet->net.ipv6.address, subnet->net.ipv6.prefixlength, sizeof(ipv6_t)))) {
254                 syslog(LOG_ERR, _ ("Network address and prefix length do not match for configuration variable %s in %s line %d"),
255                            cfg->variable, cfg->file, cfg->line);
256                 free(subnet);
257                 return 0;
258         }
259
260         *result = subnet;
261
262         return 1;
263 }
264
265 /*
266   Read exactly one line and strip the trailing newline if any.  If the
267   file was on EOF, return NULL. Otherwise, return all the data in a
268   dynamically allocated buffer.
269
270   If line is non-NULL, it will be used as an initial buffer, to avoid
271   unnecessary mallocing each time this function is called.  If buf is
272   given, and buf needs to be expanded, the var pointed to by buflen
273   will be increased.
274 */
275 char *readline(FILE * fp, char **buf, size_t *buflen)
276 {
277         char *newline = NULL;
278         char *p;
279         char *line;                                     /* The array that contains everything that has been read so far */
280         char *idx;                                      /* Read into this pointer, which points to an offset within line */
281         size_t size, newsize;           /* The size of the current array pointed to by line */
282         size_t maxlen;                          /* Maximum number of characters that may be read with fgets.  This is newsize - oldsize. */
283
284         if(feof(fp))
285                 return NULL;
286
287         if(buf && buflen) {
288                 size = *buflen;
289                 line = *buf;
290         } else {
291                 size = 100;
292                 line = xmalloc(size);
293         }
294
295         maxlen = size;
296         idx = line;
297         *idx = 0;
298
299         for(;;) {
300                 errno = 0;
301                 p = fgets(idx, maxlen, fp);
302
303                 if(!p) {                                /* EOF or error */
304                         if(feof(fp))
305                                 break;
306
307                         /* otherwise: error; let the calling function print an error message if applicable */
308                         free(line);
309                         return NULL;
310                 }
311
312                 newline = strchr(p, '\n');
313
314                 if(!newline) {                  /* We haven't yet read everything to the end of the line */
315                         newsize = size << 1;
316                         line = xrealloc(line, newsize);
317                         idx = &line[size - 1];
318                         maxlen = newsize - size + 1;
319                         size = newsize;
320                 } else {
321                         *newline = '\0';        /* kill newline */
322                         break;                          /* yay */
323                 }
324         }
325
326         if(buf && buflen) {
327                 *buflen = size;
328                 *buf = line;
329         }
330
331         return line;
332 }
333
334 /*
335   Parse a configuration file and put the results in the configuration tree
336   starting at *base.
337 */
338 int read_config_file(avl_tree_t *config_tree, const char *fname)
339 {
340         int err = -2;                           /* Parse error */
341         FILE *fp;
342         char *buffer, *line;
343         char *variable, *value;
344         int lineno = 0, ignore = 0;
345         config_t *cfg;
346         size_t bufsize;
347
348         cp();
349
350         fp = fopen(fname, "r");
351
352         if(!fp) {
353                 syslog(LOG_ERR, _("Cannot open config file %s: %s"), fname,
354                            strerror(errno));
355                 return -3;
356         }
357
358         bufsize = 100;
359         buffer = xmalloc(bufsize);
360
361         for(;;) {
362                 line = readline(fp, &buffer, &bufsize);
363
364                 if(!line) {
365                         err = -1;
366                         break;
367                 }
368
369                 if(feof(fp)) {
370                         err = 0;
371                         break;
372                 }
373
374                 lineno++;
375
376                 variable = strtok(line, "\t =");
377
378                 if(!variable)
379                         continue;                       /* no tokens on this line */
380
381                 if(variable[0] == '#')
382                         continue;                       /* comment: ignore */
383
384                 if(!strcmp(variable, "-----BEGIN"))
385                         ignore = 1;
386
387                 if(!ignore) {
388                         value = strtok(NULL, "\t\n\r =");
389
390                         if(!value || value[0] == '#') {
391                                 syslog(LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
392                                            variable, lineno, fname);
393                                 break;
394                         }
395
396                         cfg = new_config();
397                         cfg->variable = xstrdup(variable);
398                         cfg->value = xstrdup(value);
399                         cfg->file = xstrdup(fname);
400                         cfg->line = lineno;
401
402                         config_add(config_tree, cfg);
403                 }
404
405                 if(!strcmp(variable, "-----END"))
406                         ignore = 0;
407         }
408
409         free(buffer);
410         fclose(fp);
411
412         return err;
413 }
414
415 int read_server_config()
416 {
417         char *fname;
418         int x;
419
420         cp();
421
422         asprintf(&fname, "%s/tinc.conf", confbase);
423         x = read_config_file(config_tree, fname);
424
425         if(x == -1) {                           /* System error: complain */
426                 syslog(LOG_ERR, _("Failed to read `%s': %s"), fname, strerror(errno));
427         }
428
429         free(fname);
430
431         return x;
432 }
433
434 int isadir(const char *f)
435 {
436         struct stat s;
437
438         if(stat(f, &s) < 0)
439                 return 0;
440         else
441                 return S_ISDIR(s.st_mode);
442 }
443
444 int is_safe_path(const char *file)
445 {
446         char *p;
447         const char *f;
448         char x;
449         struct stat s;
450         char l[MAXBUFSIZE];
451
452         if(*file != '/') {
453                 syslog(LOG_ERR, _("`%s' is not an absolute path"), file);
454                 return 0;
455         }
456
457         p = strrchr(file, '/');
458
459         if(p == file)                           /* It's in the root */
460                 p++;
461
462         x = *p;
463         *p = '\0';
464
465         f = file;
466
467 check1:
468         if(lstat(f, &s) < 0) {
469                 syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
470                 return 0;
471         }
472
473         if(s.st_uid != geteuid()) {
474                 syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
475                            f, s.st_uid, geteuid());
476                 return 0;
477         }
478
479         if(S_ISLNK(s.st_mode)) {
480                 syslog(LOG_WARNING, _("Warning: `%s' is a symlink"), f);
481
482                 if(readlink(f, l, MAXBUFSIZE) < 0) {
483                         syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f,
484                                    strerror(errno));
485                         return 0;
486                 }
487
488                 f = l;
489                 goto check1;
490         }
491
492         *p = x;
493         f = file;
494
495 check2:
496         if(lstat(f, &s) < 0 && errno != ENOENT) {
497                 syslog(LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
498                 return 0;
499         }
500
501         if(errno == ENOENT)
502                 return 1;
503
504         if(s.st_uid != geteuid()) {
505                 syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
506                            f, s.st_uid, geteuid());
507                 return 0;
508         }
509
510         if(S_ISLNK(s.st_mode)) {
511                 syslog(LOG_WARNING, _("Warning: `%s' is a symlink"), f);
512
513                 if(readlink(f, l, MAXBUFSIZE) < 0) {
514                         syslog(LOG_ERR, _("Unable to read symbolic link `%s': %s"), f,
515                                    strerror(errno));
516                         return 0;
517                 }
518
519                 f = l;
520                 goto check2;
521         }
522
523         if(s.st_mode & 0007) {
524                 /* Accessible by others */
525                 syslog(LOG_ERR, _("`%s' has unsecure permissions"), f);
526                 return 0;
527         }
528
529         return 1;
530 }
531
532 FILE *ask_and_safe_open(const char *filename, const char *what,
533                                                 const char *mode)
534 {
535         FILE *r;
536         char *directory;
537         char *fn;
538
539         /* Check stdin and stdout */
540         if(!isatty(0) || !isatty(1)) {
541                 /* Argh, they are running us from a script or something.  Write
542                    the files to the current directory and let them burn in hell
543                    for ever. */
544                 fn = xstrdup(filename);
545         } else {
546                 /* Ask for a file and/or directory name. */
547                 fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
548                                 what, filename);
549                 fflush(stdout);
550
551                 fn = readline(stdin, NULL, NULL);
552
553                 if(!fn) {
554                         fprintf(stderr, _("Error while reading stdin: %s\n"),
555                                         strerror(errno));
556                         return NULL;
557                 }
558
559                 if(!strlen(fn))
560                         /* User just pressed enter. */
561                         fn = xstrdup(filename);
562         }
563
564         if(!strchr(fn, '/') || fn[0] != '/') {
565                 /* The directory is a relative path or a filename. */
566                 char *p;
567
568                 directory = get_current_dir_name();
569                 asprintf(&p, "%s/%s", directory, fn);
570                 free(fn);
571                 free(directory);
572                 fn = p;
573         }
574
575         umask(0077);                            /* Disallow everything for group and other */
576
577         /* Open it first to keep the inode busy */
578
579         r = fopen(fn, mode);
580
581         if(!r) {
582                 fprintf(stderr, _("Error opening file `%s': %s\n"),
583                                 fn, strerror(errno));
584                 free(fn);
585                 return NULL;
586         }
587
588         /* Then check the file for nasty attacks */
589         if(!is_safe_path(fn)) {         /* Do not permit any directories that are readable or writeable by other users. */
590                 fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
591                                  "I will not create or overwrite this file.\n"), fn);
592                 fclose(r);
593                 free(fn);
594                 return NULL;
595         }
596
597         free(fn);
598
599         return r;
600 }