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