3edcb30238a1fd54dcbcd9bffd4b037a429f13d9
[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.35 2000/12/22 21:34:20 guus Exp $
23 */
24
25 #include "config.h"
26
27 #include <assert.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #include <netdb.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <syslog.h>
35 #include <sys/stat.h>
36 #include <sys/types.h>
37 #include <unistd.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       fprintf(stderr, _("Failed to read `%s': %m\n"),
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       fprintf(stderr, _("Couldn't stat `%s': %m\n"),
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   p = strrchr(file, '/');
375   assert(p); /* p has to contain a / */
376   *p = '\0';
377   if(stat(file, &s) < 0)
378     {
379       fprintf(stderr, _("Couldn't stat `%s': %m\n"),
380               file);
381       return 0;
382     }
383   if(s.st_uid != geteuid())
384     {
385       fprintf(stderr, _("`%s' is owned by UID %d instead of %d.\n"),
386               file, s.st_uid, geteuid());
387       return 0;
388     }
389   if(S_ISLNK(s.st_mode))
390     {
391       fprintf(stderr, _("Warning: `%s' is a symlink\n"),
392               file);
393       /* fixme: read the symlink and start again */
394     }
395
396   *p = '/';
397   if(stat(file, &s) < 0 && errno != ENOENT)
398     {
399       fprintf(stderr, _("Couldn't stat `%s': %m\n"),
400               file);
401       return 0;
402     }
403   if(errno == ENOENT)
404     return 1;
405   if(s.st_uid != geteuid())
406     {
407       fprintf(stderr, _("`%s' is owned by UID %d instead of %d.\n"),
408               file, s.st_uid, geteuid());
409       return 0;
410     }
411   if(S_ISLNK(s.st_mode))
412     {
413       fprintf(stderr, _("Warning: `%s' is a symlink\n"),
414               file);
415       /* fixme: read the symlink and start again */
416     }
417   if(s.st_mode & 0007)
418     {
419       /* Accessible by others */
420       fprintf(stderr, _("`%s' has unsecure permissions.\n"),
421               file);
422       return 0;
423     }
424   
425   return 1;
426 }
427
428 FILE *ask_and_safe_open(const char* filename, const char* what)
429 {
430   FILE *r;
431   char *directory;
432   char *fn;
433   int len;
434
435   /* Check stdin and stdout */
436   if(!isatty(0) || !isatty(1))
437     {
438       /* Argh, they are running us from a script or something.  Write
439          the files to the current directory and let them burn in hell
440          for ever. */
441       fn = xstrdup(filename);
442     }
443   else
444     {
445       /* Ask for a file and/or directory name. */
446       fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
447               what, filename);
448       fflush(stdout);  /* Don't wait for a newline */
449       if((fn = readline(stdin, NULL, NULL)) == NULL)
450         {
451           fprintf(stderr, _("Error while reading stdin: %m\n"));
452           return NULL;
453         }
454       if(strlen(fn) == 0)
455         /* User just pressed enter. */
456         fn = xstrdup(filename);
457     }
458
459   if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
460     {
461       /* The directory is a relative path or a filename. */
462       char *p;
463       
464       directory = get_current_dir_name();
465       len = strlen(fn) + strlen(directory) + 2; /* 1 for the / */
466       p = xmalloc(len);
467       snprintf(p, len, "%s/%s", directory, fn);
468       free(fn);
469       free(directory);
470       fn = p;
471     }
472
473   if(isadir(fn) > 0) /* -1 is error */
474     {
475       char *p;
476
477       len = strlen(fn) + strlen(filename) + 2; /* 1 for the / */
478       p = xmalloc(len);
479       snprintf(p, len, "%s/%s", fn, filename);
480       free(fn);
481       fn = p;
482     }
483
484   umask(0077); /* Disallow everything for group and other */
485   
486   /* Open it first to keep the inode busy */
487   if((r = fopen(fn, "w")) == NULL)
488     {
489       fprintf(stderr, _("Error opening file `%s': %m\n"),
490               fn);
491       free(fn);
492       return NULL;
493     }
494
495   /* Then check the file for nasty attacks */
496   if(!is_safe_path(fn))  /* Do not permit any directories that are
497                             readable or writeable by other users. */
498     {
499       fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
500                         "I will not create or overwrite this file.\n"),
501                         fn);
502       fclose(r);
503       free(fn);
504       return NULL;
505     }
506
507   free(fn);
508   
509   return r;
510 }