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