Merge branch 'master' into 1.1
[tinc] / src / conf.c
1 /*
2     conf.c -- configuration code
3     Copyright (C) 1998 Robert van der Meulen
4                   1998-2005 Ivo Timmermans
5                   2000-2009 Guus Sliepen <guus@tinc-vpn.org>
6                   2000 Cris van Pelt
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 along
19     with this program; if not, write to the Free Software Foundation, Inc.,
20     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #include "system.h"
24
25 #include "splay_tree.h"
26 #include "conf.h"
27 #include "logger.h"
28 #include "netutl.h"                             /* for str2address */
29 #include "protocol.h"
30 #include "utils.h"                              /* for cp */
31 #include "xalloc.h"
32
33 splay_tree_t *config_tree;
34
35 int pinginterval = 0;                   /* seconds between pings */
36 int pingtimeout = 0;                    /* seconds to wait for response */
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         int result;
42
43         result = strcasecmp(a->variable, b->variable);
44
45         if(result)
46                 return result;
47
48         result = a->line - b->line;
49
50         if(result)
51                 return result;
52         else
53                 return strcmp(a->file, b->file);
54 }
55
56 void init_configuration(splay_tree_t ** config_tree) {
57         *config_tree = splay_alloc_tree((splay_compare_t) config_compare, (splay_action_t) free_config);
58 }
59
60 void exit_configuration(splay_tree_t ** config_tree) {
61         splay_delete_tree(*config_tree);
62         *config_tree = NULL;
63 }
64
65 config_t *new_config(void) {
66         return xmalloc_and_zero(sizeof(config_t));
67 }
68
69 void free_config(config_t *cfg) {
70         if(cfg->variable)
71                 free(cfg->variable);
72
73         if(cfg->value)
74                 free(cfg->value);
75
76         if(cfg->file)
77                 free(cfg->file);
78
79         free(cfg);
80 }
81
82 void config_add(splay_tree_t *config_tree, config_t *cfg) {
83         splay_insert(config_tree, cfg);
84 }
85
86 config_t *lookup_config(splay_tree_t *config_tree, char *variable) {
87         config_t cfg, *found;
88
89         cfg.variable = variable;
90         cfg.file = "";
91         cfg.line = 0;
92
93         found = splay_search_closest_greater(config_tree, &cfg);
94
95         if(!found)
96                 return NULL;
97
98         if(strcasecmp(found->variable, variable))
99                 return NULL;
100
101         return found;
102 }
103
104 config_t *lookup_config_next(splay_tree_t *config_tree, const config_t *cfg) {
105         splay_node_t *node;
106         config_t *found;
107
108         node = splay_search_node(config_tree, cfg);
109
110         if(node) {
111                 if(node->next) {
112                         found = node->next->data;
113
114                         if(!strcasecmp(found->variable, cfg->variable))
115                                 return found;
116                 }
117         }
118
119         return NULL;
120 }
121
122 bool get_config_bool(const config_t *cfg, bool *result) {
123         if(!cfg)
124                 return false;
125
126         if(!strcasecmp(cfg->value, "yes")) {
127                 *result = true;
128                 return true;
129         } else if(!strcasecmp(cfg->value, "no")) {
130                 *result = false;
131                 return true;
132         }
133
134         logger(LOG_ERR, "\"yes\" or \"no\" expected for configuration variable %s in %s line %d",
135                    cfg->variable, cfg->file, cfg->line);
136
137         return false;
138 }
139
140 bool get_config_int(const config_t *cfg, int *result) {
141         if(!cfg)
142                 return false;
143
144         if(sscanf(cfg->value, "%d", result) == 1)
145                 return true;
146
147         logger(LOG_ERR, "Integer expected for configuration variable %s in %s line %d",
148                    cfg->variable, cfg->file, cfg->line);
149
150         return false;
151 }
152
153 bool get_config_string(const config_t *cfg, char **result) {
154         if(!cfg)
155                 return false;
156
157         *result = xstrdup(cfg->value);
158
159         return true;
160 }
161
162 bool get_config_address(const config_t *cfg, struct addrinfo **result) {
163         struct addrinfo *ai;
164
165         if(!cfg)
166                 return false;
167
168         ai = str2addrinfo(cfg->value, NULL, 0);
169
170         if(ai) {
171                 *result = ai;
172                 return true;
173         }
174
175         logger(LOG_ERR, "Hostname or IP address 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_subnet(const config_t *cfg, subnet_t ** result) {
182         subnet_t subnet = {0};
183
184         if(!cfg)
185                 return false;
186
187         if(!str2net(&subnet, cfg->value)) {
188                 logger(LOG_ERR, "Subnet expected for configuration variable %s in %s line %d",
189                            cfg->variable, cfg->file, cfg->line);
190                 return false;
191         }
192
193         /* Teach newbies what subnets are... */
194
195         if(((subnet.type == SUBNET_IPV4)
196                 && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof subnet.net.ipv4.address))
197                 || ((subnet.type == SUBNET_IPV6)
198                 && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof subnet.net.ipv6.address))) {
199                 logger(LOG_ERR, "Network address and prefix length do not match for configuration variable %s in %s line %d",
200                            cfg->variable, cfg->file, cfg->line);
201                 return false;
202         }
203
204         *(*result = new_subnet()) = subnet;
205
206         return true;
207 }
208
209 /*
210   Read exactly one line and strip the trailing newline if any.
211 */
212 static char *readline(FILE * fp, char *buf, size_t buflen) {
213         char *newline = NULL;
214         char *p;
215
216         if(feof(fp))
217                 return NULL;
218
219         p = fgets(buf, buflen, fp);
220
221         if(!p)
222                 return NULL;
223
224         newline = strchr(p, '\n');
225
226         if(!newline)
227                 return NULL;
228
229         *newline = '\0';        /* kill newline */
230         if(newline > p && newline[-1] == '\r')  /* and carriage return if necessary */
231                 newline[-1] = '\0';
232
233         return buf;
234 }
235
236 /*
237   Parse a configuration file and put the results in the configuration tree
238   starting at *base.
239 */
240 int read_config_file(splay_tree_t *config_tree, const char *fname) {
241         int err = -2;                           /* Parse error */
242         FILE *fp;
243         char buffer[MAX_STRING_SIZE];
244         char *line;
245         char *variable, *value, *eol;
246         int lineno = 0;
247         int len;
248         bool ignore = false;
249         config_t *cfg;
250         bool result = false;
251
252         fp = fopen(fname, "r");
253
254         if(!fp) {
255                 logger(LOG_ERR, "Cannot open config file %s: %s", fname, strerror(errno));
256                 return false;
257         }
258
259         for(;;) {
260                 line = readline(fp, buffer, sizeof buffer);
261
262                 if(!line) {
263                         if(feof(fp))
264                                 result = true;
265                         break;
266                 }
267
268                 lineno++;
269
270                 if(!*line || *line == '#')
271                         continue;
272
273                 if(ignore) {
274                         if(!strncmp(line, "-----END", 8))
275                                 ignore = false;
276                         continue;
277                 }
278                 
279                 if(!strncmp(line, "-----BEGIN", 10)) {
280                         ignore = true;
281                         continue;
282                 }
283
284                 variable = value = line;
285
286                 eol = line + strlen(line);
287                 while(strchr("\t ", *--eol))
288                         *eol = '\0';
289
290                 len = strcspn(value, "\t =");
291                 value += len;
292                 value += strspn(value, "\t ");
293                 if(*value == '=') {
294                         value++;
295                         value += strspn(value, "\t ");
296                 }
297                 variable[len] = '\0';
298
299         
300                 if(!*value) {
301                         logger(LOG_ERR, "No value for variable `%s' on line %d while reading config file %s",
302                                    variable, lineno, fname);
303                         break;
304                 }
305
306                 cfg = new_config();
307                 cfg->variable = xstrdup(variable);
308                 cfg->value = xstrdup(value);
309                 cfg->file = xstrdup(fname);
310                 cfg->line = lineno;
311
312                 config_add(config_tree, cfg);
313         }
314
315         fclose(fp);
316
317         return result;
318 }
319
320 bool read_server_config() {
321         char *fname;
322         bool x;
323
324         xasprintf(&fname, "%s/tinc.conf", confbase);
325         x = read_config_file(config_tree, fname);
326
327         if(!x) {                                /* System error: complain */
328                 logger(LOG_ERR, "Failed to read `%s': %s", fname, strerror(errno));
329         }
330
331         free(fname);
332
333         return x;
334 }
335
336 FILE *ask_and_open(const char *filename, const char *what) {
337         FILE *r;
338         char *directory;
339         char line[PATH_MAX];
340         const char *fn;
341
342         /* Check stdin and stdout */
343         if(!isatty(0) || !isatty(1)) {
344                 /* Argh, they are running us from a script or something.  Write
345                    the files to the current directory and let them burn in hell
346                    for ever. */
347                 fn = filename;
348         } else {
349                 /* Ask for a file and/or directory name. */
350                 fprintf(stdout, "Please enter a file to save %s to [%s]: ",
351                                 what, filename);
352                 fflush(stdout);
353
354                 fn = readline(stdin, line, sizeof line);
355
356                 if(!fn) {
357                         fprintf(stderr, "Error while reading stdin: %s\n",
358                                         strerror(errno));
359                         return NULL;
360                 }
361
362                 if(!strlen(fn))
363                         /* User just pressed enter. */
364                         fn = filename;
365         }
366
367 #ifdef HAVE_MINGW
368         if(fn[0] != '\\' && fn[0] != '/' && !strchr(fn, ':')) {
369 #else
370         if(fn[0] != '/') {
371 #endif
372                 /* The directory is a relative path or a filename. */
373                 char *p;
374
375                 directory = get_current_dir_name();
376                 xasprintf(&p, "%s/%s", directory, fn);
377                 free(directory);
378                 fn = p;
379         }
380
381         umask(0077);                            /* Disallow everything for group and other */
382
383         /* Open it first to keep the inode busy */
384
385         r = fopen(fn, "r+") ?: fopen(fn, "w+");
386
387         if(!r) {
388                 fprintf(stderr, "Error opening file `%s': %s\n",
389                                 fn, strerror(errno));
390                 return NULL;
391         }
392
393         return r;
394 }
395
396 bool disable_old_keys(FILE *f) {
397         char buf[100];
398         long pos;
399         bool disabled = false;
400
401         rewind(f);
402         pos = ftell(f);
403
404         while(fgets(buf, sizeof buf, f)) {
405                 if(!strncmp(buf, "-----BEGIN RSA", 14)) {       
406                         buf[11] = 'O';
407                         buf[12] = 'L';
408                         buf[13] = 'D';
409                         fseek(f, pos, SEEK_SET);
410                         fputs(buf, f);
411                         disabled = true;
412                 }
413                 else if(!strncmp(buf, "-----END RSA", 12)) {    
414                         buf[ 9] = 'O';
415                         buf[10] = 'L';
416                         buf[11] = 'D';
417                         fseek(f, pos, SEEK_SET);
418                         fputs(buf, f);
419                         disabled = true;
420                 }
421                 pos = ftell(f);
422         }
423
424         return disabled;
425 }