More on edges.
[tinc] / src / conf.c
1 /*
2     conf.c -- configuration code
3     Copyright (C) 1998 Robert van der Meulen
4                   1998-2001 Ivo Timmermans <itimmermans@bigfoot.com>
5                   2000,2001 Guus Sliepen <guus@sliepen.warande.net>
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.41 2001/06/05 19:39:54 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
39 #include <xalloc.h>
40 #include <utils.h> /* for cp */
41
42 #include "conf.h"
43 #include "netutl.h" /* for strtoip */
44
45 #include "system.h"
46
47 config_t *config = NULL;
48 int debug_lvl = 0;
49 int timeout = 0; /* seconds before timeout */
50 char *confbase = NULL;           /* directory in which all config files are */
51 char *netname = NULL;            /* name of the vpn network */
52
53 /* Will be set if HUP signal is received. It will be processed when it is safe. */
54 int sighup = 0;
55
56 /*
57   These are all the possible configurable values
58 */
59 static internal_config_t hazahaza[] = {
60 /* Main configuration file keywords */
61   { "ConnectTo",    config_connectto,      TYPE_NAME },
62   { "Hostnames",    config_hostnames,    TYPE_BOOL },
63   { "Interface",    config_interface,      TYPE_NAME },
64   { "InterfaceIP",  config_interfaceip,    TYPE_IP },
65   { "KeyExpire",    config_keyexpire,      TYPE_INT },
66   { "MyVirtualIP",  config_dummy,          TYPE_IP },
67   { "MyOwnVPNIP",   config_dummy,          TYPE_IP },
68   { "Name",         config_name,       TYPE_NAME },
69   { "PingTimeout",  config_pingtimeout,    TYPE_INT },
70   { "PrivateKey",   config_privatekey,     TYPE_NAME },
71   { "PrivateKeyFile", config_privatekeyfile, TYPE_NAME },
72   { "TapDevice",    config_tapdevice,      TYPE_NAME },
73   { "VpnMask",      config_dummy,          TYPE_IP },
74 /* Host configuration file keywords */
75   { "Address",      config_address,        TYPE_NAME },
76   { "IndirectData", config_indirectdata,   TYPE_BOOL },
77   { "Port",         config_port,           TYPE_INT },
78   { "PublicKey",    config_publickey,      TYPE_NAME },
79   { "PublicKeyFile", config_publickeyfile, TYPE_NAME },
80   { "RestrictAddress", config_restrictaddress, TYPE_BOOL },
81   { "RestrictHosts", config_restricthosts, TYPE_BOOL },
82   { "RestrictPort", config_restrictport,   TYPE_BOOL },
83   { "RestrictSubnets", config_restrictsubnets, TYPE_BOOL },
84   { "Subnet",       config_subnet,         TYPE_IP },           /* Use IPv4 subnets only for now */
85   { "TCPonly",      config_tcponly,        TYPE_BOOL },
86   { "Mode",         config_mode,           TYPE_NAME },
87   { NULL, 0, 0 }
88 };
89
90 /*
91   Add given value to the list of configs cfg
92 */
93 config_t *
94 add_config_val(config_t **cfg, int argtype, char *val)
95 {
96   config_t *p;
97   char *q;
98 cp
99   p = (config_t*)xmalloc(sizeof(*p));
100   p->data.val = 0;
101
102   switch(argtype)
103     {
104     case TYPE_INT:
105       p->data.val = strtol(val, &q, 0);
106       if(q && *q)
107         p->data.val = 0;
108       break;
109     case TYPE_NAME:
110       p->data.ptr = xmalloc(strlen(val) + 1);
111       strcpy(p->data.ptr, val);
112       break;
113     case TYPE_IP:
114       p->data.ip = strtoip(val);
115       break;
116     case TYPE_BOOL:
117       if(!strcasecmp("yes", val))
118         p->data.val = stupid_true;
119       else if(!strcasecmp("no", val))
120         p->data.val = stupid_false;
121       else
122         p->data.val = 0;
123     }
124
125   p->argtype = argtype;
126
127   if(p->data.val)
128     {
129       p->next = *cfg;
130       *cfg = p;
131 cp
132       return p;
133     }
134   else
135     {
136       free(p);
137 cp
138       return NULL;
139     }
140 }
141
142 /*
143   Read exactly one line and strip the trailing newline if any.  If the
144   file was on EOF, return NULL. Otherwise, return all the data in a
145   dynamically allocated buffer.
146   
147   If line is non-NULL, it will be used as an initial buffer, to avoid
148   unnecessary mallocing each time this function is called.  If buf is
149   given, and buf needs to be expanded, the var pointed to by buflen
150   will be increased.
151 */
152 char *readline(FILE *fp, char **buf, size_t *buflen)
153 {
154   char *newline = NULL;
155   char *p;
156   char *line; /* The array that contains everything that has been read
157                  so far */
158   char *idx; /* Read into this pointer, which points to an offset
159                 within line */
160   size_t size, newsize; /* The size of the current array pointed to by
161                            line */
162   size_t maxlen; /* Maximum number of characters that may be read with
163                     fgets.  This is newsize - oldsize. */
164
165   if(feof(fp))
166     return NULL;
167
168   if((buf != NULL) && (buflen != NULL))
169     {
170       size = *buflen;
171       line = *buf;
172     }
173   else
174     {
175       size = 100;
176       line = xmalloc(size);
177     }
178
179   maxlen = size;
180   idx = line;
181   *idx = 0;
182   for(;;)
183     {
184       errno = 0;
185       p = fgets(idx, maxlen, fp);
186       if(p == NULL)  /* EOF or error */
187         {
188           if(feof(fp))
189             break;
190
191           /* otherwise: error; let the calling function print an error
192              message if applicable */
193           free(line);
194           return NULL;
195         }
196
197       newline = strchr(p, '\n');
198       if(newline == NULL)
199         /* We haven't yet read everything to the end of the line */
200         {
201           newsize = size << 1;
202           line = xrealloc(line, newsize);
203           idx = &line[size - 1];
204           maxlen = newsize - size + 1;
205           size = newsize;
206         }
207       else
208         {
209           *newline = '\0'; /* kill newline */
210           break;  /* yay */
211         }
212     }
213
214   if((buf != NULL) && (buflen != NULL))
215     {
216       *buflen = size;
217       *buf = line;
218     }
219   return line;
220 }
221
222 /*
223   Parse a configuration file and put the results in the configuration tree
224   starting at *base.
225 */
226 int read_config_file(config_t **base, const char *fname)
227 {
228   int err = -2; /* Parse error */
229   FILE *fp;
230   char *buffer, *line;
231   char *p, *q;
232   int i, lineno = 0, ignore = 0;
233   config_t *cfg;
234   size_t bufsize;
235   
236 cp
237   if((fp = fopen (fname, "r")) == NULL)
238     {
239       syslog(LOG_ERR, _("Cannot open config file %s: %m"), fname);
240       return -3;
241     }
242
243   bufsize = 100;
244   buffer = xmalloc(bufsize);
245   
246   for(;;)
247     {
248       
249       if((line = readline(fp, &buffer, &bufsize)) == NULL)
250         {
251           err = -1;
252           break;
253         }
254
255       if(feof(fp))
256         {
257           err = 0;
258           break;
259         }
260
261       lineno++;
262
263       if((p = strtok(line, "\t =")) == NULL)
264         continue; /* no tokens on this line */
265
266       if(p[0] == '#')
267         continue; /* comment: ignore */
268
269       if(!strcmp(p, "-----BEGIN"))
270         ignore = 1;
271         
272       if(ignore == 0)
273         {
274           for(i = 0; hazahaza[i].name != NULL; i++)
275             if(!strcasecmp(hazahaza[i].name, p))
276               break;
277
278           if(!hazahaza[i].name)
279             {
280               syslog(LOG_ERR, _("Invalid variable name `%s' on line %d while reading config file %s"),
281                       p, lineno, fname);
282               break;
283             }
284
285           if(((q = strtok(NULL, "\t\n\r =")) == NULL) || q[0] == '#')
286             {
287               syslog(LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
288                       hazahaza[i].name, lineno, fname);
289               break;
290             }
291
292           cfg = add_config_val(base, hazahaza[i].argtype, q);
293           if(cfg == NULL)
294             {
295               syslog(LOG_ERR, _("Invalid value for variable `%s' on line %d while reading config file %s"),
296                       hazahaza[i].name, lineno, fname);
297               break;
298             }
299
300           cfg->which = hazahaza[i].which;
301           if(!config)
302             config = cfg;
303        }
304
305       if(!strcmp(p, "-----END"))
306         ignore = 0;
307     }
308
309   free(buffer);
310   fclose (fp);
311 cp
312   return err;
313 }
314
315 int read_server_config()
316 {
317   char *fname;
318   int x;
319 cp
320   asprintf(&fname, "%s/tinc.conf", confbase);
321   x = read_config_file(&config, fname);
322   if(x == -1) /* System error: complain */
323     {
324       syslog(LOG_ERR, _("Failed to read `%s': %m"),
325               fname);
326     }
327   free(fname);
328 cp
329   return x;  
330 }
331
332 /*
333   Look up the value of the config option type
334 */
335 const config_t *get_config_val(config_t *p, which_t type)
336 {
337 cp
338   for(; p != NULL; p = p->next)
339     if(p->which == type)
340       break;
341 cp
342   return p;
343 }
344
345 /*
346   Remove the complete configuration tree.
347 */
348 void clear_config(config_t **base)
349 {
350   config_t *p, *next;
351 cp
352   for(p = *base; p != NULL; p = next)
353     {
354       next = p->next;
355       if(p->data.ptr && (p->argtype == TYPE_NAME))
356         {
357           free(p->data.ptr);
358         }
359       free(p);
360     }
361   *base = NULL;
362 cp
363 }
364
365 int isadir(const char* f)
366 {
367   struct stat s;
368
369   if(stat(f, &s) < 0)
370     return 0;
371   else
372     return S_ISDIR(s.st_mode);
373 }
374
375 int is_safe_path(const char *file)
376 {
377   char *p;
378   const char *f;
379   char x;
380   struct stat s;
381   char l[MAXBUFSIZE];
382
383   if(*file != '/')
384     {
385       syslog(LOG_ERR, _("`%s' is not an absolute path"), file);
386       return 0;
387     }
388
389   p = strrchr(file, '/');
390   
391   if(p == file)         /* It's in the root */
392     p++;
393     
394   x = *p;
395   *p = '\0';
396
397   f = file;
398 check1:
399   if(lstat(f, &s) < 0)
400     {
401       syslog(LOG_ERR, _("Couldn't stat `%s': %m"),
402               f);
403       return 0;
404     }
405
406   if(s.st_uid != geteuid())
407     {
408       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
409               f, s.st_uid, geteuid());
410       return 0;
411     }
412
413   if(S_ISLNK(s.st_mode))
414     {
415       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
416               f);
417
418       if(readlink(f, l, MAXBUFSIZE) < 0)
419         {
420           syslog(LOG_ERR, _("Unable to read symbolic link `%s': %m"), f);
421           return 0;
422         }
423       
424       f = l;
425       goto check1;
426     }
427
428   *p = x;
429   f = file;
430   
431 check2:
432   if(lstat(f, &s) < 0 && errno != ENOENT)
433     {
434       syslog(LOG_ERR, _("Couldn't stat `%s': %m"),
435               f);
436       return 0;
437     }
438     
439   if(errno == ENOENT)
440     return 1;
441
442   if(s.st_uid != geteuid())
443     {
444       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
445               f, s.st_uid, geteuid());
446       return 0;
447     }
448
449   if(S_ISLNK(s.st_mode))
450     {
451       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
452               f);
453
454       if(readlink(f, l, MAXBUFSIZE) < 0)
455         {
456           syslog(LOG_ERR, _("Unable to read symbolic link `%s': %m"), f);
457           return 0;
458         }
459       
460       f = l;
461       goto check2;
462     }
463
464   if(s.st_mode & 0007)
465     {
466       /* Accessible by others */
467       syslog(LOG_ERR, _("`%s' has unsecure permissions"),
468               f);
469       return 0;
470     }
471   
472   return 1;
473 }
474
475 FILE *ask_and_safe_open(const char* filename, const char* what, const char* mode)
476 {
477   FILE *r;
478   char *directory;
479   char *fn;
480
481   /* Check stdin and stdout */
482   if(!isatty(0) || !isatty(1))
483     {
484       /* Argh, they are running us from a script or something.  Write
485          the files to the current directory and let them burn in hell
486          for ever. */
487       fn = xstrdup(filename);
488     }
489   else
490     {
491       /* Ask for a file and/or directory name. */
492       fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
493               what, filename);
494       fflush(stdout);
495
496       if((fn = readline(stdin, NULL, NULL)) == NULL)
497         {
498           fprintf(stderr, _("Error while reading stdin: %m\n"));
499           return NULL;
500         }
501
502       if(strlen(fn) == 0)
503         /* User just pressed enter. */
504         fn = xstrdup(filename);
505     }
506
507   if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
508     {
509       /* The directory is a relative path or a filename. */
510       char *p;
511       
512       directory = get_current_dir_name();
513       asprintf(&p, "%s/%s", directory, fn);
514       free(fn);
515       free(directory);
516       fn = p;
517     }
518
519   umask(0077); /* Disallow everything for group and other */
520   
521   /* Open it first to keep the inode busy */
522   if((r = fopen(fn, mode)) == NULL)
523     {
524       fprintf(stderr, _("Error opening file `%s': %m\n"),
525               fn);
526       free(fn);
527       return NULL;
528     }
529     
530   /* Then check the file for nasty attacks */
531   if(!is_safe_path(fn))  /* Do not permit any directories that are
532                             readable or writeable by other users. */
533     {
534       fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
535                         "I will not create or overwrite this file.\n"),
536                         fn);
537       fclose(r);
538       free(fn);
539       return NULL;
540     }
541
542   free(fn);
543
544   return r;
545 }