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