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