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