e0cc11e5137610ccd5220bd95116d1f969c721a7
[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.33 2000/12/05 08:56:44 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     {
210       *buflen = size;
211       *buf = line;
212     }
213   return line;
214 }
215
216 /*
217   Parse a configuration file and put the results in the configuration tree
218   starting at *base.
219 */
220 int read_config_file(config_t **base, const char *fname)
221 {
222   int err = -2; /* Parse error */
223   FILE *fp;
224   char *buffer, *line;
225   char *p, *q;
226   int i, lineno = 0;
227   config_t *cfg;
228   size_t bufsize;
229   
230 cp
231   if((fp = fopen (fname, "r")) == NULL)
232     return -1;
233
234   bufsize = 100;
235   buffer = xmalloc(bufsize);
236   
237   for(;;)
238     {
239       
240       if((line = readline(fp, &buffer, &bufsize)) == NULL)
241         {
242           err = -1;
243           break;
244         }
245
246       if(feof(fp))
247         {
248           err = 0;
249           break;
250         }
251
252       lineno++;
253
254       if((p = strtok(line, "\t =")) == NULL)
255         continue; /* no tokens on this line */
256
257       if(p[0] == '#')
258         continue; /* comment: ignore */
259
260       for(i = 0; hazahaza[i].name != NULL; i++)
261         if(!strcasecmp(hazahaza[i].name, p))
262           break;
263
264       if(!hazahaza[i].name)
265         {
266           syslog(LOG_ERR, _("Invalid variable name `%s' on line %d while reading config file %s"),
267                   p, lineno, fname);
268           break;
269         }
270
271       if(((q = strtok(NULL, "\t\n\r =")) == NULL) || q[0] == '#')
272         {
273           fprintf(stderr, _("No value for variable `%s' on line %d while reading config file %s"),
274                   hazahaza[i].name, lineno, fname);
275           break;
276         }
277
278       cfg = add_config_val(base, hazahaza[i].argtype, q);
279       if(cfg == NULL)
280         {
281           fprintf(stderr, _("Invalid value for variable `%s' on line %d while reading config file %s"),
282                   hazahaza[i].name, lineno, fname);
283           break;
284         }
285
286       cfg->which = hazahaza[i].which;
287       if(!config)
288         config = cfg;
289     }
290
291   free(buffer);
292   fclose (fp);
293 cp
294   return err;
295 }
296
297 int read_server_config()
298 {
299   char *fname;
300   int x;
301 cp
302   asprintf(&fname, "%s/tinc.conf", confbase);
303   x = read_config_file(&config, fname);
304   if(x == -1) /* System error */
305     {
306       fprintf(stderr, _("Failed to read `%s': %m\n"),
307               fname);
308     }
309   free(fname);
310 cp
311   return x;  
312 }
313
314 /*
315   Look up the value of the config option type
316 */
317 const config_t *get_config_val(config_t *p, which_t type)
318 {
319 cp
320   for(; p != NULL; p = p->next)
321     if(p->which == type)
322       break;
323 cp
324   return p;
325 }
326
327 /*
328   Remove the complete configuration tree.
329 */
330 void clear_config(config_t **base)
331 {
332   config_t *p, *next;
333 cp
334   for(p = *base; p != NULL; p = next)
335     {
336       next = p->next;
337       if(p->data.ptr && (p->argtype == TYPE_NAME))
338         {
339           free(p->data.ptr);
340         }
341       free(p);
342     }
343   *base = NULL;
344 cp
345 }
346
347 int isadir(const char* f)
348 {
349   struct stat s;
350
351   if(stat(f, &s) < 0)
352     {
353       fprintf(stderr, _("Couldn't stat `%s': %m\n"),
354               f);
355       return -1;
356     }
357
358   return S_ISDIR(s.st_mode);
359 }
360
361 int is_safe_path(const char *file)
362 {
363   char *p;
364   struct stat s;
365
366   p = strrchr(file, '/');
367   assert(p); /* p has to contain a / */
368   *p = '\0';
369   if(stat(file, &s) < 0)
370     {
371       fprintf(stderr, _("Couldn't stat `%s': %m\n"),
372               file);
373       return 0;
374     }
375   if(s.st_uid != geteuid())
376     {
377       fprintf(stderr, _("`%s' is owned by UID %d instead of %d.\n"),
378               file, s.st_uid, geteuid());
379       return 0;
380     }
381   if(S_ISLNK(s.st_mode))
382     {
383       fprintf(stderr, _("Warning: `%s' is a symlink\n"),
384               file);
385       /* fixme: read the symlink and start again */
386     }
387
388   *p = '/';
389   if(stat(file, &s) < 0 && errno != ENOENT)
390     {
391       fprintf(stderr, _("Couldn't stat `%s': %m\n"),
392               file);
393       return 0;
394     }
395   if(errno == ENOENT)
396     return 1;
397   if(s.st_uid != geteuid())
398     {
399       fprintf(stderr, _("`%s' is owned by UID %d instead of %d.\n"),
400               file, s.st_uid, geteuid());
401       return 0;
402     }
403   if(S_ISLNK(s.st_mode))
404     {
405       fprintf(stderr, _("Warning: `%s' is a symlink\n"),
406               file);
407       /* fixme: read the symlink and start again */
408     }
409   if(s.st_mode & 0007)
410     {
411       /* Accessible by others */
412       fprintf(stderr, _("`%s' has unsecure permissions.\n"),
413               file);
414       return 0;
415     }
416   
417   return 1;
418 }
419
420 FILE *ask_and_safe_open(const char* filename, const char* what)
421 {
422   FILE *r;
423   char *directory;
424   char *fn;
425   int len;
426
427   /* Check stdin and stdout */
428   if(!isatty(0) || !isatty(1))
429     {
430       /* Argh, they are running us from a script or something.  Write
431          the files to the current directory and let them burn in hell
432          for ever. */
433       fn = xstrdup(filename);
434     }
435   else
436     {
437       /* Ask for a file and/or directory name. */
438       fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
439               what, filename);
440       fflush(stdout);  /* Don't wait for a newline */
441       if((fn = readline(stdin, NULL, NULL)) == NULL)
442         {
443           fprintf(stderr, _("Error while reading stdin: %m\n"));
444           return NULL;
445         }
446       if(strlen(fn) == 0)
447         /* User just pressed enter. */
448         fn = xstrdup(filename);
449     }
450
451   if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
452     {
453       /* The directory is a relative path or a filename. */
454       char *p;
455       
456       directory = get_current_dir_name();
457       len = strlen(fn) + strlen(directory) + 2; /* 1 for the / */
458       p = xmalloc(len);
459       snprintf(p, len, "%s/%s", directory, fn);
460       free(fn);
461       free(directory);
462       fn = p;
463     }
464
465   if(isadir(fn) > 0) /* -1 is error */
466     {
467       char *p;
468
469       len = strlen(fn) + strlen(filename) + 2; /* 1 for the / */
470       p = xmalloc(len);
471       snprintf(p, len, "%s/%s", fn, filename);
472       free(fn);
473       fn = p;
474     }
475
476   umask(0077); /* Disallow everything for group and other */
477   
478   /* Open it first to keep the inode busy */
479   if((r = fopen(fn, "w")) == NULL)
480     {
481       fprintf(stderr, _("Error opening file `%s': %m\n"),
482               fn);
483       free(fn);
484       return NULL;
485     }
486
487   /* Then check the file for nasty attacks */
488   if(!is_safe_path(fn))  /* Do not permit any directories that are
489                             readable or writeable by other users. */
490     {
491       fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
492                         "I will not create or overwrite this file.\n"),
493                         fn);
494       fclose(r);
495       free(fn);
496       return NULL;
497     }
498
499   free(fn);
500   
501   return r;
502 }