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