11a9ca08633c09414dc4b46752d76ce03d1a0227
[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.27 2000/11/30 00:24:13 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)
360     {
361       fprintf(stderr, _("Couldn't stat `%s': %m\n"),
362               file);
363       return 0;
364     }
365   if(s.st_uid != geteuid())
366     {
367       fprintf(stderr, _("`%s' is owned by UID %d instead of %d.\n"),
368               file, s.st_uid, geteuid());
369       return 0;
370     }
371   if(S_ISLNK(s.st_mode))
372     {
373       fprintf(stderr, _("Warning: `%s' is a symlink\n"),
374               file);
375       /* fixme: read the symlink and start again */
376     }
377   if(s.st_mode & 0007)
378     {
379       /* Accessible by others */
380       fprintf(stderr, _("`%s' has unsecure permissions.\n"),
381               file);
382       return 0;
383     }
384   
385   return 1;
386 }
387
388 FILE *ask_and_safe_open(const char* filename, const char* what)
389 {
390   FILE *r;
391   char *directory;
392   char *fn;
393   int len;
394
395   /* Check stdin and stdout */
396   if(!isatty(0) || !isatty(1))
397     {
398       /* Argh, they are running us from a script or something.  Write
399          the files to the current directory and let them burn in hell
400          for ever. */
401       fn = xstrdup(filename);
402     }
403   else
404     {
405       /* Ask for a file and/or directory name. */
406       fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
407               what, filename);
408       fflush(stdout);  /* Don't wait for a newline */
409       if((fn = readline(stdin)) == NULL)
410         {
411           fprintf(stderr, _("Error while reading stdin: %m\n"));
412           return NULL;
413         }
414       if(strlen(fn) == 0)
415         /* User just pressed enter. */
416         fn = xstrdup(filename);
417     }
418
419   if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
420     {
421       /* The directory is a relative path or a filename. */
422       char *p;
423       
424       directory = get_current_dir_name();
425       len = strlen(fn) + strlen(directory) + 2; /* 1 for the / */
426       p = xmalloc(len);
427       snprintf(p, len, "%s/%s", directory, fn);
428       free(fn);
429       free(directory);
430       fn = p;
431     }
432
433   if(isadir(fn) > 0) /* -1 is error */
434     {
435       char *p;
436
437       len = strlen(fn) + strlen(filename) + 2; /* 1 for the / */
438       p = xmalloc(len);
439       snprintf(p, len, "%s/%s", fn, filename);
440       free(fn);
441       fn = p;
442     }
443
444   umask(0077); /* Disallow everything for group and other */
445   
446   /* Open it first to keep the inode busy */
447   if((r = fopen(fn, "w")) == NULL)
448     {
449       fprintf(stderr, _("Error opening file `%s': %m\n"),
450               fn);
451       free(fn);
452       return NULL;
453     }
454
455   /* Then check the file for nasty attacks */
456   if(!is_safe_path(fn))  /* Do not permit any directories that are
457                             readable or writeable by other users. */
458     {
459       fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
460                         "I will not create or overwrite this file.\n"),
461                         fn);
462       fclose(r);
463       free(fn);
464       return NULL;
465     }
466
467   free(fn);
468   
469   return r;
470 }