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