2 conf.c -- configuration code
3 Copyright (C) 1998 Robert van der Meulen
4 1998-2002 Ivo Timmermans <ivo@o2w.nl>
5 2000-2002 Guus Sliepen <guus@sliepen.eu.org>
6 2000 Cris van Pelt <tribbel@arise.dhs.org>
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.
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.
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.
22 $Id: conf.c,v 1.9.4.64 2003/07/11 16:12:59 guus Exp $
34 #include <sys/types.h>
39 #include <utils.h> /* for cp */
43 #include "netutl.h" /* for str2address */
48 avl_tree_t *config_tree;
50 int pingtimeout = 0; /* seconds before timeout */
51 char *confbase = NULL; /* directory in which all config files are */
52 char *netname = NULL; /* name of the vpn network */
54 static int config_compare(config_t *a, config_t *b)
58 result = strcasecmp(a->variable, b->variable);
63 result = a->line - b->line;
68 return strcmp(a->file, b->file);
71 void init_configuration(avl_tree_t ** config_tree)
75 *config_tree = avl_alloc_tree((avl_compare_t) config_compare, (avl_action_t) free_config);
78 void exit_configuration(avl_tree_t ** config_tree)
82 avl_delete_tree(*config_tree);
86 config_t *new_config(void)
90 return (config_t *) xmalloc_and_zero(sizeof(config_t));
93 void free_config(config_t *cfg)
109 void config_add(avl_tree_t *config_tree, config_t *cfg)
113 avl_insert(config_tree, cfg);
116 config_t *lookup_config(avl_tree_t *config_tree, char *variable)
118 config_t cfg, *found;
122 cfg.variable = variable;
126 found = avl_search_closest_greater(config_tree, &cfg);
131 if(strcasecmp(found->variable, variable))
137 config_t *lookup_config_next(avl_tree_t *config_tree, config_t *cfg)
144 node = avl_search_node(config_tree, cfg);
148 found = (config_t *) node->next->data;
150 if(!strcasecmp(found->variable, cfg->variable))
158 int get_config_bool(config_t *cfg, int *result)
165 if(!strcasecmp(cfg->value, "yes")) {
168 } else if(!strcasecmp(cfg->value, "no")) {
173 logger(DEBUG_ALWAYS, LOG_ERR, _("\"yes\" or \"no\" expected for configuration variable %s in %s line %d"),
174 cfg->variable, cfg->file, cfg->line);
179 int get_config_int(config_t *cfg, int *result)
186 if(sscanf(cfg->value, "%d", result) == 1)
189 logger(DEBUG_ALWAYS, LOG_ERR, _("Integer expected for configuration variable %s in %s line %d"),
190 cfg->variable, cfg->file, cfg->line);
195 int get_config_string(config_t *cfg, char **result)
202 *result = xstrdup(cfg->value);
207 int get_config_address(config_t *cfg, struct addrinfo **result)
216 ai = str2addrinfo(cfg->value, NULL, 0);
223 logger(DEBUG_ALWAYS, LOG_ERR, _("Hostname or IP address expected for configuration variable %s in %s line %d"),
224 cfg->variable, cfg->file, cfg->line);
229 int get_config_subnet(config_t *cfg, subnet_t ** result)
238 subnet = str2net(cfg->value);
241 logger(DEBUG_ALWAYS, LOG_ERR, _("Subnet expected for configuration variable %s in %s line %d"),
242 cfg->variable, cfg->file, cfg->line);
246 /* Teach newbies what subnets are... */
248 if(((subnet->type == SUBNET_IPV4)
249 && maskcheck(&subnet->net.ipv4.address, subnet->net.ipv4.prefixlength, sizeof(ipv4_t)))
250 || ((subnet->type == SUBNET_IPV6)
251 && maskcheck(&subnet->net.ipv6.address, subnet->net.ipv6.prefixlength, sizeof(ipv6_t)))) {
252 logger(DEBUG_ALWAYS, LOG_ERR, _ ("Network address and prefix length do not match for configuration variable %s in %s line %d"),
253 cfg->variable, cfg->file, cfg->line);
264 Read exactly one line and strip the trailing newline if any. If the
265 file was on EOF, return NULL. Otherwise, return all the data in a
266 dynamically allocated buffer.
268 If line is non-NULL, it will be used as an initial buffer, to avoid
269 unnecessary mallocing each time this function is called. If buf is
270 given, and buf needs to be expanded, the var pointed to by buflen
273 static char *readline(FILE * fp, char **buf, size_t *buflen)
275 char *newline = NULL;
277 char *line; /* The array that contains everything that has been read so far */
278 char *idx; /* Read into this pointer, which points to an offset within line */
279 size_t size, newsize; /* The size of the current array pointed to by line */
280 size_t maxlen; /* Maximum number of characters that may be read with fgets. This is newsize - oldsize. */
290 line = xmalloc(size);
299 p = fgets(idx, maxlen, fp);
301 if(!p) { /* EOF or error */
305 /* otherwise: error; let the calling function print an error message if applicable */
310 newline = strchr(p, '\n');
312 if(!newline) { /* We haven't yet read everything to the end of the line */
314 line = xrealloc(line, newsize);
315 idx = &line[size - 1];
316 maxlen = newsize - size + 1;
319 *newline = '\0'; /* kill newline */
333 Parse a configuration file and put the results in the configuration tree
336 int read_config_file(avl_tree_t *config_tree, const char *fname)
338 int err = -2; /* Parse error */
341 char *variable, *value;
342 int lineno = 0, ignore = 0;
348 fp = fopen(fname, "r");
351 logger(DEBUG_ALWAYS, LOG_ERR, _("Cannot open config file %s: %s"), fname,
357 buffer = xmalloc(bufsize);
360 line = readline(fp, &buffer, &bufsize);
374 variable = strtok(line, "\t =");
377 continue; /* no tokens on this line */
379 if(variable[0] == '#')
380 continue; /* comment: ignore */
382 if(!strcmp(variable, "-----BEGIN"))
386 value = strtok(NULL, "\t\n\r =");
388 if(!value || value[0] == '#') {
389 logger(DEBUG_ALWAYS, LOG_ERR, _("No value for variable `%s' on line %d while reading config file %s"),
390 variable, lineno, fname);
395 cfg->variable = xstrdup(variable);
396 cfg->value = xstrdup(value);
397 cfg->file = xstrdup(fname);
400 config_add(config_tree, cfg);
403 if(!strcmp(variable, "-----END"))
413 int read_server_config()
420 asprintf(&fname, "%s/tinc.conf", confbase);
421 x = read_config_file(config_tree, fname);
423 if(x == -1) { /* System error: complain */
424 logger(DEBUG_ALWAYS, LOG_ERR, _("Failed to read `%s': %s"), fname, strerror(errno));
432 int is_safe_path(const char *file)
441 logger(DEBUG_ALWAYS, LOG_ERR, _("`%s' is not an absolute path"), file);
445 p = strrchr(file, '/');
447 if(p == file) /* It's in the root */
456 if(lstat(f, &s) < 0) {
457 logger(DEBUG_ALWAYS, LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
461 if(s.st_uid != geteuid()) {
462 logger(DEBUG_ALWAYS, LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
463 f, s.st_uid, geteuid());
467 if(S_ISLNK(s.st_mode)) {
468 logger(DEBUG_ALWAYS, LOG_WARNING, _("Warning: `%s' is a symlink"), f);
470 if(readlink(f, l, MAXBUFSIZE) < 0) {
471 logger(DEBUG_ALWAYS, LOG_ERR, _("Unable to read symbolic link `%s': %s"), f,
484 if(lstat(f, &s) < 0 && errno != ENOENT) {
485 logger(DEBUG_ALWAYS, LOG_ERR, _("Couldn't stat `%s': %s"), f, strerror(errno));
492 if(s.st_uid != geteuid()) {
493 logger(DEBUG_ALWAYS, LOG_ERR, _("`%s' is owned by UID %d instead of %d"),
494 f, s.st_uid, geteuid());
498 if(S_ISLNK(s.st_mode)) {
499 logger(DEBUG_ALWAYS, LOG_WARNING, _("Warning: `%s' is a symlink"), f);
501 if(readlink(f, l, MAXBUFSIZE) < 0) {
502 logger(DEBUG_ALWAYS, LOG_ERR, _("Unable to read symbolic link `%s': %s"), f,
511 if(s.st_mode & 0007) {
512 /* Accessible by others */
513 logger(DEBUG_ALWAYS, LOG_ERR, _("`%s' has unsecure permissions"), f);
520 FILE *ask_and_safe_open(const char *filename, const char *what,
527 /* Check stdin and stdout */
528 if(!isatty(0) || !isatty(1)) {
529 /* Argh, they are running us from a script or something. Write
530 the files to the current directory and let them burn in hell
532 fn = xstrdup(filename);
534 /* Ask for a file and/or directory name. */
535 fprintf(stdout, _("Please enter a file to save %s to [%s]: "),
539 fn = readline(stdin, NULL, NULL);
542 fprintf(stderr, _("Error while reading stdin: %s\n"),
548 /* User just pressed enter. */
549 fn = xstrdup(filename);
552 if(!strchr(fn, '/') || fn[0] != '/') {
553 /* The directory is a relative path or a filename. */
556 directory = get_current_dir_name();
557 asprintf(&p, "%s/%s", directory, fn);
563 umask(0077); /* Disallow everything for group and other */
565 /* Open it first to keep the inode busy */
570 fprintf(stderr, _("Error opening file `%s': %s\n"),
571 fn, strerror(errno));
576 /* Then check the file for nasty attacks */
577 if(!is_safe_path(fn)) { /* Do not permit any directories that are readable or writeable by other users. */
578 fprintf(stderr, _("The file `%s' (or any of the leading directories) has unsafe permissions.\n"
579 "I will not create or overwrite this file.\n"), fn);