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