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