5aaa22a085f3002674ed1fb30134210a3dd103cf
[tinc] / src / conf.c
1 /*
2     conf.c -- configuration code
3     Copyright (C) 1998 Robert van der Meulen
4     Copyright (C) 1998,1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>
5                             2000 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.36 2001/01/05 23:53:49 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   { NULL, 0, 0 }
87 };
88
89 /*
90   Add given value to the list of configs cfg
91 */
92 config_t *
93 add_config_val(config_t **cfg, int argtype, char *val)
94 {
95   config_t *p;
96   char *q;
97 cp
98   p = (config_t*)xmalloc(sizeof(*p));
99   p->data.val = 0;
100
101   switch(argtype)
102     {
103     case TYPE_INT:
104       p->data.val = strtol(val, &q, 0);
105       if(q && *q)
106         p->data.val = 0;
107       break;
108     case TYPE_NAME:
109       p->data.ptr = xmalloc(strlen(val) + 1);
110       strcpy(p->data.ptr, val);
111       break;
112     case TYPE_IP:
113       p->data.ip = strtoip(val);
114       break;
115     case TYPE_BOOL:
116       if(!strcasecmp("yes", val))
117         p->data.val = stupid_true;
118       else if(!strcasecmp("no", val))
119         p->data.val = stupid_false;
120       else
121         p->data.val = 0;
122     }
123
124   p->argtype = argtype;
125
126   if(p->data.val)
127     {
128       p->next = *cfg;
129       *cfg = p;
130 cp
131       return p;
132     }
133   else
134     {
135       free(p);
136 cp
137       return NULL;
138     }
139 }
140
141 /*
142   Read exactly one line and strip the trailing newline if any.  If the
143   file was on EOF, return NULL. Otherwise, return all the data in a
144   dynamically allocated buffer.
145   
146   If line is non-NULL, it will be used as an initial buffer, to avoid
147   unnecessary mallocing each time this function is called.  If buf is
148   given, and buf needs to be expanded, the var pointed to by buflen
149   will be increased.
150 */
151 char *readline(FILE *fp, char **buf, size_t *buflen)
152 {
153   char *newline = NULL;
154   char *p;
155   char *line; /* The array that contains everything that has been read
156                  so far */
157   char *idx; /* Read into this pointer, which points to an offset
158                 within line */
159   size_t size, newsize; /* The size of the current array pointed to by
160                            line */
161   size_t maxlen; /* Maximum number of characters that may be read with
162                     fgets.  This is newsize - oldsize. */
163
164   if(feof(fp))
165     return NULL;
166
167   if((buf != NULL) && (buflen != NULL))
168     {
169       size = *buflen;
170       line = *buf;
171     }
172   else
173     {
174       size = 100;
175       line = xmalloc(size);
176     }
177
178   maxlen = size;
179   idx = line;
180   *idx = 0;
181   for(;;)
182     {
183       errno = 0;
184       p = fgets(idx, maxlen, fp);
185       if(p == NULL)  /* EOF or error */
186         {
187           if(feof(fp))
188             break;
189
190           /* otherwise: error; let the calling function print an error
191              message if applicable */
192           free(line);
193           return NULL;
194         }
195
196       newline = strchr(p, '\n');
197       if(newline == NULL)
198         /* We haven't yet read everything to the end of the line */
199         {
200           newsize = size << 1;
201           line = xrealloc(line, newsize);
202           idx = &line[size - 1];
203           maxlen = newsize - size + 1;
204           size = newsize;
205         }
206       else
207         {
208           *newline = '\0'; /* kill newline */
209           break;  /* yay */
210         }
211     }
212
213   if((buf != NULL) && (buflen != NULL))
214     {
215       *buflen = size;
216       *buf = line;
217     }
218   return line;
219 }
220
221 /*
222   Parse a configuration file and put the results in the configuration tree
223   starting at *base.
224 */
225 int read_config_file(config_t **base, const char *fname)
226 {
227   int err = -2; /* Parse error */
228   FILE *fp;
229   char *buffer, *line;
230   char *p, *q;
231   int i, lineno = 0;
232   config_t *cfg;
233   size_t bufsize;
234   
235 cp
236   if((fp = fopen (fname, "r")) == NULL)
237     {
238       syslog(LOG_ERR, _("Cannot open config file %s: %m"), fname);
239       return -1;
240     }
241
242   bufsize = 100;
243   buffer = xmalloc(bufsize);
244   
245   for(;;)
246     {
247       
248       if((line = readline(fp, &buffer, &bufsize)) == NULL)
249         {
250           err = -1;
251           break;
252         }
253
254       if(feof(fp))
255         {
256           err = 0;
257           break;
258         }
259
260       lineno++;
261
262       if((p = strtok(line, "\t =")) == NULL)
263         continue; /* no tokens on this line */
264
265       if(p[0] == '#')
266         continue; /* comment: ignore */
267
268       for(i = 0; hazahaza[i].name != NULL; i++)
269         if(!strcasecmp(hazahaza[i].name, p))
270           break;
271
272       if(!hazahaza[i].name)
273         {
274           syslog(LOG_ERR, _("Invalid variable name `%s' on line %d while reading config file %s"),
275                   p, lineno, fname);
276           break;
277         }
278
279       if(((q = strtok(NULL, "\t\n\r =")) == NULL) || q[0] == '#')
280         {
281           syslog(LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
282                   hazahaza[i].name, lineno, fname);
283           break;
284         }
285
286       cfg = add_config_val(base, hazahaza[i].argtype, q);
287       if(cfg == NULL)
288         {
289           syslog(LOG_ERR, _("Invalid value for variable `%s' on line %d while reading config file %s"),
290                   hazahaza[i].name, lineno, fname);
291           break;
292         }
293
294       cfg->which = hazahaza[i].which;
295       if(!config)
296         config = cfg;
297     }
298
299   free(buffer);
300   fclose (fp);
301 cp
302   return err;
303 }
304
305 int read_server_config()
306 {
307   char *fname;
308   int x;
309 cp
310   asprintf(&fname, "%s/tinc.conf", confbase);
311   x = read_config_file(&config, fname);
312   if(x == -1) /* System error */
313     {
314       syslog(LOG_ERR, _("Failed to read `%s': %m"),
315               fname);
316     }
317   free(fname);
318 cp
319   return x;  
320 }
321
322 /*
323   Look up the value of the config option type
324 */
325 const config_t *get_config_val(config_t *p, which_t type)
326 {
327 cp
328   for(; p != NULL; p = p->next)
329     if(p->which == type)
330       break;
331 cp
332   return p;
333 }
334
335 /*
336   Remove the complete configuration tree.
337 */
338 void clear_config(config_t **base)
339 {
340   config_t *p, *next;
341 cp
342   for(p = *base; p != NULL; p = next)
343     {
344       next = p->next;
345       if(p->data.ptr && (p->argtype == TYPE_NAME))
346         {
347           free(p->data.ptr);
348         }
349       free(p);
350     }
351   *base = NULL;
352 cp
353 }
354
355 int isadir(const char* f)
356 {
357   struct stat s;
358
359   if(stat(f, &s) < 0)
360     {
361       syslog(LOG_ERR, _("Couldn't stat `%s': %m"),
362               f);
363       return -1;
364     }
365
366   return S_ISDIR(s.st_mode);
367 }
368
369 int is_safe_path(const char *file)
370 {
371   char *p;
372   struct stat s;
373
374   if(*file != '/')
375     {
376       syslog(LOG_ERR, _("`%s' is not an absolute path"), file);
377       return 0;
378     }
379
380   p = strrchr(file, '/');
381   *p = '\0';
382   if(stat(file, &s) < 0)
383     {
384       syslog(LOG_ERR, _("Couldn't stat `%s': %m"),
385               file);
386       return 0;
387     }
388   if(s.st_uid != geteuid())
389     {
390       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
391               file, s.st_uid, geteuid());
392       return 0;
393     }
394   if(S_ISLNK(s.st_mode))
395     {
396       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
397               file);
398       /* fixme: read the symlink and start again */
399     }
400
401   *p = '/';
402   if(stat(file, &s) < 0 && errno != ENOENT)
403     {
404       syslog(LOG_ERR, _("Couldn't stat `%s': %m"),
405               file);
406       return 0;
407     }
408   if(errno == ENOENT)
409     return 1;
410   if(s.st_uid != geteuid())
411     {
412       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
413               file, s.st_uid, geteuid());
414       return 0;
415     }
416   if(S_ISLNK(s.st_mode))
417     {
418       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
419               file);
420       /* fixme: read the symlink and start again */
421     }
422   if(s.st_mode & 0007)
423     {
424       /* Accessible by others */
425       syslog(LOG_ERR, _("`%s' has unsecure permissions"),
426               file);
427       return 0;
428     }
429   
430   return 1;
431 }
432
433 FILE *ask_and_safe_open(const char* filename, const char* what)
434 {
435   FILE *r;
436   char *directory;
437   char *fn;
438   int len;
439
440   /* Check stdin and stdout */
441   if(!isatty(0) || !isatty(1))
442     {
443       /* Argh, they are running us from a script or something.  Write
444          the files to the current directory and let them burn in hell
445          for ever. */
446       fn = xstrdup(filename);
447     }
448   else
449     {
450       /* Ask for a file and/or directory name. */
451       fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
452               what, filename);
453       fflush(stdout);
454
455       if((fn = readline(stdin, NULL, NULL)) == NULL)
456         {
457           fprintf(stderr, _("Error while reading stdin: %m\n"));
458           return NULL;
459         }
460
461       if(strlen(fn) == 0)
462         /* User just pressed enter. */
463         fn = xstrdup(filename);
464     }
465
466   if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
467     {
468       /* The directory is a relative path or a filename. */
469       char *p;
470       
471       directory = get_current_dir_name();
472       len = strlen(fn) + strlen(directory) + 2; /* 1 for the / */
473       p = xmalloc(len);
474       snprintf(p, len, "%s/%s", directory, fn);
475       free(fn);
476       free(directory);
477       fn = p;
478     }
479
480   if(isadir(fn) > 0) /* -1 is error */
481     {
482       char *p;
483
484       len = strlen(fn) + strlen(filename) + 2; /* 1 for the / */
485       p = xmalloc(len);
486       snprintf(p, len, "%s/%s", fn, filename);
487       free(fn);
488       fn = p;
489     }
490
491   umask(0077); /* Disallow everything for group and other */
492   
493   /* Open it first to keep the inode busy */
494   if((r = fopen(fn, "w")) == NULL)
495     {
496       fprintf(stderr, _("Error opening file `%s': %m\n"),
497               fn);
498       free(fn);
499       return NULL;
500     }
501
502   /* Then check the file for nasty attacks */
503   if(!is_safe_path(fn))  /* Do not permit any directories that are
504                             readable or writeable by other users. */
505     {
506       fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
507                         "I will not create or overwrite this file.\n"),
508                         fn);
509       fclose(r);
510       free(fn);
511       return NULL;
512     }
513
514   free(fn);
515   
516   return r;
517 }