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