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