]> git.meshlink.io Git - meshlink/blob - src/conf.c
61f0cf0e377c89022266f3891db2958fb1a99ed1
[meshlink] / 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.37 2001/01/06 18:03:39 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
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;
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 -1;
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       for(i = 0; hazahaza[i].name != NULL; i++)
269         if(!strcasecmp(hazahaza[i].name, p))
270           break;
271
272       if(!hazahaza[i].name)
273         {
274           syslog(LOG_ERR, _("Invalid variable name `%s' on line %d while reading config file %s"),
275                   p, lineno, fname);
276           break;
277         }
278
279       if(((q = strtok(NULL, "\t\n\r =")) == NULL) || q[0] == '#')
280         {
281           syslog(LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
282                   hazahaza[i].name, lineno, fname);
283           break;
284         }
285
286       cfg = add_config_val(base, hazahaza[i].argtype, q);
287       if(cfg == NULL)
288         {
289           syslog(LOG_ERR, _("Invalid value for variable `%s' on line %d while reading config file %s"),
290                   hazahaza[i].name, lineno, fname);
291           break;
292         }
293
294       cfg->which = hazahaza[i].which;
295       if(!config)
296         config = cfg;
297     }
298
299   free(buffer);
300   fclose (fp);
301 cp
302   return err;
303 }
304
305 int read_server_config()
306 {
307   char *fname;
308   int x;
309 cp
310   asprintf(&fname, "%s/tinc.conf", confbase);
311   x = read_config_file(&config, fname);
312   if(x == -1) /* System error */
313     {
314       syslog(LOG_ERR, _("Failed to read `%s': %m"),
315               fname);
316     }
317   free(fname);
318 cp
319   return x;  
320 }
321
322 /*
323   Look up the value of the config option type
324 */
325 const config_t *get_config_val(config_t *p, which_t type)
326 {
327 cp
328   for(; p != NULL; p = p->next)
329     if(p->which == type)
330       break;
331 cp
332   return p;
333 }
334
335 /*
336   Remove the complete configuration tree.
337 */
338 void clear_config(config_t **base)
339 {
340   config_t *p, *next;
341 cp
342   for(p = *base; p != NULL; p = next)
343     {
344       next = p->next;
345       if(p->data.ptr && (p->argtype == TYPE_NAME))
346         {
347           free(p->data.ptr);
348         }
349       free(p);
350     }
351   *base = NULL;
352 cp
353 }
354
355 int isadir(const char* f)
356 {
357   struct stat s;
358
359   if(stat(f, &s) < 0)
360     return 0;
361   else
362     return S_ISDIR(s.st_mode);
363 }
364
365 int is_safe_path(const char *file)
366 {
367   char *p;
368   const char *f;
369   char x;
370   struct stat s;
371   char l[MAXBUFSIZE];
372
373   if(*file != '/')
374     {
375       syslog(LOG_ERR, _("`%s' is not an absolute path"), file);
376       return 0;
377     }
378
379   p = strrchr(file, '/');
380   
381   if(p == file)         /* It's in the root */
382     p++;
383     
384   x = *p;
385   *p = '\0';
386
387   f = file;
388 check1:
389   if(lstat(f, &s) < 0)
390     {
391       syslog(LOG_ERR, _("Couldn't stat `%s': %m"),
392               f);
393       return 0;
394     }
395
396   if(s.st_uid != geteuid())
397     {
398       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
399               f, s.st_uid, geteuid());
400       return 0;
401     }
402
403   if(S_ISLNK(s.st_mode))
404     {
405       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
406               f);
407
408       if(readlink(f, l, MAXBUFSIZE) < 0)
409         {
410           syslog(LOG_ERR, _("Unable to read symbolic link `%s': %m"), f);
411           return 0;
412         }
413       
414       f = l;
415       goto check1;
416     }
417
418   *p = x;
419   f = file;
420   
421 check2:
422   if(lstat(f, &s) < 0 && errno != ENOENT)
423     {
424       syslog(LOG_ERR, _("Couldn't stat `%s': %m"),
425               f);
426       return 0;
427     }
428     
429   if(errno == ENOENT)
430     return 1;
431
432   if(s.st_uid != geteuid())
433     {
434       syslog(LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
435               f, s.st_uid, geteuid());
436       return 0;
437     }
438
439   if(S_ISLNK(s.st_mode))
440     {
441       syslog(LOG_WARNING, _("Warning: `%s' is a symlink"),
442               f);
443
444       if(readlink(f, l, MAXBUFSIZE) < 0)
445         {
446           syslog(LOG_ERR, _("Unable to read symbolic link `%s': %m"), f);
447           return 0;
448         }
449       
450       f = l;
451       goto check2;
452     }
453
454   if(s.st_mode & 0007)
455     {
456       /* Accessible by others */
457       syslog(LOG_ERR, _("`%s' has unsecure permissions"),
458               f);
459       return 0;
460     }
461   
462   return 1;
463 }
464
465 FILE *ask_and_safe_open(const char* filename, const char* what)
466 {
467   FILE *r;
468   char *directory;
469   char *fn;
470
471   /* Check stdin and stdout */
472   if(!isatty(0) || !isatty(1))
473     {
474       /* Argh, they are running us from a script or something.  Write
475          the files to the current directory and let them burn in hell
476          for ever. */
477       fn = xstrdup(filename);
478     }
479   else
480     {
481       /* Ask for a file and/or directory name. */
482       fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
483               what, filename);
484       fflush(stdout);
485
486       if((fn = readline(stdin, NULL, NULL)) == NULL)
487         {
488           fprintf(stderr, _("Error while reading stdin: %m\n"));
489           return NULL;
490         }
491
492       if(strlen(fn) == 0)
493         /* User just pressed enter. */
494         fn = xstrdup(filename);
495     }
496
497   if((strchr(fn, '/') == NULL) || (fn[0] != '/'))
498     {
499       /* The directory is a relative path or a filename. */
500       char *p;
501       
502       directory = get_current_dir_name();
503       asprintf(&p, "%s/%s", directory, fn);
504       free(fn);
505       free(directory);
506       fn = p;
507     }
508
509   umask(0077); /* Disallow everything for group and other */
510   
511   /* Open it first to keep the inode busy */
512   if((r = fopen(fn, "w")) == NULL)
513     {
514       fprintf(stderr, _("Error opening file `%s': %m\n"),
515               fn);
516       free(fn);
517       return NULL;
518     }
519
520   /* Then check the file for nasty attacks */
521   if(!is_safe_path(fn))  /* Do not permit any directories that are
522                             readable or writeable by other users. */
523     {
524       fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
525                         "I will not create or overwrite this file.\n"),
526                         fn);
527       fclose(r);
528       free(fn);
529       return NULL;
530     }
531
532   free(fn);
533   
534   return r;
535 }