Enable OpenSSL ENGINE, so crypto hardware gets used. Thanks to Andreas van Cranenburgh.
[tinc] / src / conf.c
1 /*
2     conf.c -- configuration code
3     Copyright (C) 1998 Robert van der Meulen
4                   1998-2005 Ivo Timmermans <ivo@tinc-vpn.org>
5                   2000-2005 Guus Sliepen <guus@tinc-vpn.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$
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(const config_t *a, const 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 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, const 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 = node->next->data;
135
136                         if(!strcasecmp(found->variable, cfg->variable))
137                                 return found;
138                 }
139         }
140
141         return NULL;
142 }
143
144 bool get_config_bool(const config_t *cfg, bool *result)
145 {
146         cp();
147
148         if(!cfg)
149                 return false;
150
151         if(!strcasecmp(cfg->value, "yes")) {
152                 *result = true;
153                 return true;
154         } else if(!strcasecmp(cfg->value, "no")) {
155                 *result = false;
156                 return true;
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 false;
163 }
164
165 bool get_config_int(const config_t *cfg, int *result)
166 {
167         cp();
168
169         if(!cfg)
170                 return false;
171
172         if(sscanf(cfg->value, "%d", result) == 1)
173                 return true;
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 false;
179 }
180
181 bool get_config_string(const config_t *cfg, char **result)
182 {
183         cp();
184
185         if(!cfg)
186                 return false;
187
188         *result = xstrdup(cfg->value);
189
190         return true;
191 }
192
193 bool get_config_address(const config_t *cfg, struct addrinfo **result)
194 {
195         struct addrinfo *ai;
196
197         cp();
198
199         if(!cfg)
200                 return false;
201
202         ai = str2addrinfo(cfg->value, NULL, 0);
203
204         if(ai) {
205                 *result = ai;
206                 return true;
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 false;
213 }
214
215 bool get_config_subnet(const config_t *cfg, subnet_t ** result)
216 {
217         subnet_t subnet = {0};
218
219         cp();
220
221         if(!cfg)
222                 return false;
223
224         if(!str2net(&subnet, cfg->value)) {
225                 logger(LOG_ERR, _("Subnet expected for configuration variable %s in %s line %d"),
226                            cfg->variable, cfg->file, cfg->line);
227                 return false;
228         }
229
230         /* Teach newbies what subnets are... */
231
232         if(((subnet.type == SUBNET_IPV4)
233                 && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t)))
234                 || ((subnet.type == SUBNET_IPV6)
235                 && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t)))) {
236                 logger(LOG_ERR, _ ("Network address and prefix length do not match for configuration variable %s in %s line %d"),
237                            cfg->variable, cfg->file, cfg->line);
238                 return false;
239         }
240
241         *(*result = new_subnet()) = subnet;
242
243         return true;
244 }
245
246 /*
247   Read exactly one line and strip the trailing newline if any.  If the
248   file was on EOF, return NULL. Otherwise, return all the data in a
249   dynamically allocated buffer.
250
251   If line is non-NULL, it will be used as an initial buffer, to avoid
252   unnecessary mallocing each time this function is called.  If buf is
253   given, and buf needs to be expanded, the var pointed to by buflen
254   will be increased.
255 */
256 static char *readline(FILE * fp, char **buf, size_t *buflen)
257 {
258         char *newline = NULL;
259         char *p;
260         char *line;                                     /* The array that contains everything that has been read so far */
261         char *idx;                                      /* Read into this pointer, which points to an offset within line */
262         size_t size, newsize;           /* The size of the current array pointed to by line */
263         size_t maxlen;                          /* Maximum number of characters that may be read with fgets.  This is newsize - oldsize. */
264
265         if(feof(fp))
266                 return NULL;
267
268         if(buf && buflen) {
269                 size = *buflen;
270                 line = *buf;
271         } else {
272                 size = 100;
273                 line = xmalloc(size);
274         }
275
276         maxlen = size;
277         idx = line;
278         *idx = 0;
279
280         for(;;) {
281                 errno = 0;
282                 p = fgets(idx, maxlen, fp);
283
284                 if(!p) {                                /* EOF or error */
285                         if(feof(fp))
286                                 break;
287
288                         /* otherwise: error; let the calling function print an error message if applicable */
289                         free(line);
290                         return NULL;
291                 }
292
293                 newline = strchr(p, '\n');
294
295                 if(!newline) {                  /* We haven't yet read everything to the end of the line */
296                         newsize = size << 1;
297                         line = xrealloc(line, newsize);
298                         idx = &line[size - 1];
299                         maxlen = newsize - size + 1;
300                         size = newsize;
301                 } else {
302                         *newline = '\0';        /* kill newline */
303                         break;                          /* yay */
304                 }
305         }
306
307         if(buf && buflen) {
308                 *buflen = size;
309                 *buf = line;
310         }
311
312         return line;
313 }
314
315 /*
316   Parse a configuration file and put the results in the configuration tree
317   starting at *base.
318 */
319 int read_config_file(avl_tree_t *config_tree, const char *fname)
320 {
321         int err = -2;                           /* Parse error */
322         FILE *fp;
323         char *buffer, *line;
324         char *variable, *value, *eol;
325         int lineno = 0;
326         int len;
327         bool ignore = false;
328         config_t *cfg;
329         size_t bufsize;
330
331         cp();
332
333         fp = fopen(fname, "r");
334
335         if(!fp) {
336                 logger(LOG_ERR, _("Cannot open config file %s: %s"), fname,
337                            strerror(errno));
338                 return -3;
339         }
340
341         bufsize = 100;
342         buffer = xmalloc(bufsize);
343
344         for(;;) {
345                 line = readline(fp, &buffer, &bufsize);
346
347                 if(!line) {
348                         err = -1;
349                         break;
350                 }
351
352                 if(feof(fp)) {
353                         err = 0;
354                         break;
355                 }
356
357                 lineno++;
358
359                 if(!*line || *line == '#')
360                         continue;
361
362                 if(ignore) {
363                         if(!strncmp(line, "-----END", 8))
364                                 ignore = false;
365                         continue;
366                 }
367                 
368                 if(!strncmp(line, "-----BEGIN", 10)) {
369                         ignore = true;
370                         continue;
371                 }
372
373                 variable = value = line;
374
375                 eol = line + strlen(line);
376                 while(strchr("\t ", *--eol))
377                         *eol = '\0';
378
379                 len = strcspn(value, "\t =");
380                 value += len;
381                 value += strspn(value, "\t ");
382                 if(*value == '=') {
383                         value++;
384                         value += strspn(value, "\t ");
385                 }
386                 variable[len] = '\0';
387
388         
389                 if(!*value) {
390                         logger(LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
391                                    variable, lineno, fname);
392                         break;
393                 }
394
395                 cfg = new_config();
396                 cfg->variable = xstrdup(variable);
397                 cfg->value = xstrdup(value);
398                 cfg->file = xstrdup(fname);
399                 cfg->line = lineno;
400
401                 config_add(config_tree, cfg);
402         }
403
404         free(buffer);
405         fclose(fp);
406
407         return err;
408 }
409
410 bool read_server_config()
411 {
412         char *fname;
413         int x;
414
415         cp();
416
417         asprintf(&fname, "%s/tinc.conf", confbase);
418         x = read_config_file(config_tree, fname);
419
420         if(x == -1) {                           /* System error: complain */
421                 logger(LOG_ERR, _("Failed to read `%s': %s"), fname, strerror(errno));
422         }
423
424         free(fname);
425
426         return x == 0;
427 }
428
429 FILE *ask_and_open(const char *filename, const char *what, const char *mode)
430 {
431         FILE *r;
432         char *directory;
433         char *fn;
434
435         /* Check stdin and stdout */
436         if(!isatty(0) || !isatty(1)) {
437                 /* Argh, they are running us from a script or something.  Write
438                    the files to the current directory and let them burn in hell
439                    for ever. */
440                 fn = xstrdup(filename);
441         } else {
442                 /* Ask for a file and/or directory name. */
443                 fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
444                                 what, filename);
445                 fflush(stdout);
446
447                 fn = readline(stdin, NULL, NULL);
448
449                 if(!fn) {
450                         fprintf(stderr, _("Error while reading stdin: %s\n"),
451                                         strerror(errno));
452                         return NULL;
453                 }
454
455                 if(!strlen(fn))
456                         /* User just pressed enter. */
457                         fn = xstrdup(filename);
458         }
459
460 #ifdef HAVE_MINGW
461         if(fn[0] != '\\' && fn[0] != '/' && !strchr(fn, ':')) {
462 #else
463         if(fn[0] != '/') {
464 #endif
465                 /* The directory is a relative path or a filename. */
466                 char *p;
467
468                 directory = get_current_dir_name();
469                 asprintf(&p, "%s/%s", directory, fn);
470                 free(fn);
471                 free(directory);
472                 fn = p;
473         }
474
475         umask(0077);                            /* Disallow everything for group and other */
476
477         /* Open it first to keep the inode busy */
478
479         r = fopen(fn, mode);
480
481         if(!r) {
482                 fprintf(stderr, _("Error opening file `%s': %s\n"),
483                                 fn, strerror(errno));
484                 free(fn);
485                 return NULL;
486         }
487
488         free(fn);
489
490         return r;
491 }