tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

conffile.c (8581B)


      1 /* Copyright (c) 2001 Matej Pfajfar.
      2 * Copyright (c) 2001-2004, Roger Dingledine.
      3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      4 * Copyright (c) 2007-2021, The Tor Project, Inc. */
      5 /* See LICENSE for licensing information */
      6 
      7 /**
      8 * \file conffile.h
      9 *
     10 * \brief Read configuration files from disk, with full `%include` support.
     11 **/
     12 
     13 #include "lib/fs/conffile.h"
     14 
     15 #include "lib/container/smartlist.h"
     16 #include "lib/encoding/confline.h"
     17 #include "lib/fs/dir.h"
     18 #include "lib/fs/files.h"
     19 #include "lib/fs/path.h"
     20 #include "lib/log/log.h"
     21 #include "lib/malloc/malloc.h"
     22 #include "lib/sandbox/sandbox.h"
     23 #include "lib/string/printf.h"
     24 
     25 #include <stdbool.h>
     26 #include <errno.h>
     27 
     28 static smartlist_t *config_get_file_list(const char *path,
     29                                         smartlist_t *opened_files);
     30 static int config_get_included_config(const char *path, int recursion_level,
     31                                      int extended, config_line_t **config,
     32                                      config_line_t **config_last,
     33                                      smartlist_t *opened_lst);
     34 static int config_process_include(const char *path, int recursion_level,
     35                                  int extended, config_line_t **list,
     36                                  config_line_t **list_last,
     37                                  smartlist_t *opened_lst);
     38 
     39 /** Helper: parse the config string and strdup into key/value
     40 * strings. Set *result to the list, or NULL if parsing the string
     41 * failed. Set *has_include to 1 if <b>result</b> has values from
     42 * %included files. <b>opened_lst</b> will have a list of opened files if
     43 * provided. Return 0 on success, -1 on failure. Warn and ignore any
     44 * misformatted lines.
     45 *
     46 * If <b>extended</b> is set, then treat keys beginning with / and with + as
     47 * indicating "clear" and "append" respectively. */
     48 int
     49 config_get_lines_include(const char *string, config_line_t **result,
     50                         int extended, int *has_include,
     51                         smartlist_t *opened_lst)
     52 {
     53  return config_get_lines_aux(string, result, extended, 1, has_include,
     54                              opened_lst, 1, NULL, config_process_include);
     55 }
     56 
     57 /** Return a list of paths obtained when expanding globs in <b>pattern</b>.
     58 * If <b>pattern</b> has no globs, return a list with <b>pattern</b> in it.
     59 * If <b>opened_files</b> is provided, add paths opened by glob to it.
     60 * Return NULL on failure. */
     61 static smartlist_t *
     62 expand_glob(const char *pattern, smartlist_t *opened_files)
     63 {
     64  if (! has_glob(pattern)) {
     65    smartlist_t *matches = smartlist_new();
     66    smartlist_add_strdup(matches, pattern);
     67    return matches;
     68  }
     69 
     70  smartlist_t *matches = tor_glob(pattern);
     71  if (!matches) {
     72    if (errno == EPERM) {
     73      log_err(LD_CONFIG, "Sandbox is active, but the configuration pattern "
     74              "\"%s\" listed with %%include would access files or folders not "
     75              "allowed by it. Cannot proceed.", pattern);
     76    }
     77    return NULL;
     78  }
     79 
     80  if (opened_files) {
     81    smartlist_t *glob_opened = get_glob_opened_files(pattern);
     82    if (!glob_opened) {
     83      SMARTLIST_FOREACH(matches, char *, f, tor_free(f));
     84      smartlist_free(matches);
     85      return NULL;
     86    }
     87    smartlist_add_all(opened_files, glob_opened);
     88    smartlist_free(glob_opened);
     89  }
     90  smartlist_sort_strings(matches);
     91  return matches;
     92 }
     93 
     94 /** Returns a list of configuration files present on paths that match
     95 * <b>pattern</b>. The pattern is expanded and then all the paths are
     96 * processed. A path can be a file or a directory. If it is a file, that file
     97 * will be added to the list to be returned. If it is a directory,
     98 * all paths for files on that directory root (no recursion) except for files
     99 * whose name starts with a dot will be added to the list to be returned.
    100 * <b>opened_files</b> will have a list of files opened by this function
    101 * if provided. Return NULL on failure. Ignores empty files.
    102 */
    103 static smartlist_t *
    104 config_get_file_list(const char *pattern, smartlist_t *opened_files)
    105 {
    106  smartlist_t *glob_matches = expand_glob(pattern, opened_files);
    107  if (!glob_matches) {
    108    return NULL;
    109  }
    110 
    111  bool error_found = false;
    112  smartlist_t *file_list = smartlist_new();
    113  SMARTLIST_FOREACH_BEGIN(glob_matches, char *, path) {
    114    if (opened_files) {
    115      smartlist_add_strdup(opened_files, path);
    116    }
    117    if (sandbox_interned_string_is_missing(path)) {
    118      log_err(LD_CONFIG, "Sandbox is active, but a new configuration "
    119              "file \"%s\" has been listed with %%include. Cannot proceed.",
    120              path);
    121      error_found = true;
    122      break;
    123    }
    124 
    125    file_status_t file_type = file_status(path);
    126    if (file_type == FN_FILE) {
    127      smartlist_add_strdup(file_list, path);
    128    } else if (file_type == FN_DIR) {
    129      smartlist_t *all_files = tor_listdir(path);
    130      if (!all_files) {
    131        error_found = true;
    132        break;
    133      }
    134      smartlist_sort_strings(all_files);
    135      SMARTLIST_FOREACH_BEGIN(all_files, char *, f) {
    136        if (f[0] == '.') {
    137          continue;
    138        }
    139 
    140        char *fullname;
    141        tor_asprintf(&fullname, "%s"PATH_SEPARATOR"%s", path, f);
    142 
    143        if (opened_files) {
    144          smartlist_add_strdup(opened_files, fullname);
    145        }
    146 
    147        if (file_status(fullname) != FN_FILE) {
    148          tor_free(fullname);
    149          continue;
    150        }
    151        smartlist_add(file_list, fullname);
    152      } SMARTLIST_FOREACH_END(f);
    153      SMARTLIST_FOREACH(all_files, char *, f, tor_free(f));
    154      smartlist_free(all_files);
    155    } else if (file_type == FN_EMPTY) {
    156        continue;
    157    } else {
    158      error_found = true;
    159      break;
    160    }
    161  } SMARTLIST_FOREACH_END(path);
    162  SMARTLIST_FOREACH(glob_matches, char *, f, tor_free(f));
    163  smartlist_free(glob_matches);
    164 
    165  if (error_found) {
    166    SMARTLIST_FOREACH(file_list, char *, f, tor_free(f));
    167    smartlist_free(file_list);
    168    file_list = NULL;
    169  }
    170 
    171  return file_list;
    172 }
    173 
    174 /** Creates a list of config lines present on included <b>path</b>.
    175 * Set <b>config</b> to the list and <b>config_last</b> to the last element of
    176 * <b>config</b>. <b>opened_lst</b> will have a list of opened files if
    177 * provided. Return 0 on success, -1 on failure. */
    178 static int
    179 config_get_included_config(const char *path, int recursion_level, int extended,
    180                           config_line_t **config, config_line_t **config_last,
    181                           smartlist_t *opened_lst)
    182 {
    183  char *included_conf = read_file_to_str(path, 0, NULL);
    184  if (!included_conf) {
    185    return -1;
    186  }
    187 
    188  if (config_get_lines_aux(included_conf, config, extended, 1, NULL,
    189                           opened_lst, recursion_level+1, config_last,
    190                           config_process_include) < 0) {
    191    tor_free(included_conf);
    192    return -1;
    193  }
    194 
    195  tor_free(included_conf);
    196  return 0;
    197 }
    198 
    199 /** Process an %include <b>pattern</b> in a config file. Set <b>list</b> to the
    200 * list of configuration settings obtained and <b>list_last</b> to the last
    201 * element of the same list. <b>opened_lst</b> will have a list of opened
    202 * files if provided. Return 0 on success, -1 on failure. */
    203 static int
    204 config_process_include(const char *pattern, int recursion_level, int extended,
    205                       config_line_t **list, config_line_t **list_last,
    206                       smartlist_t *opened_lst)
    207 {
    208  config_line_t *ret_list = NULL;
    209  config_line_t **next = &ret_list;
    210 
    211  smartlist_t *config_files = config_get_file_list(pattern, opened_lst);
    212  if (!config_files) {
    213    return -1;
    214  }
    215 
    216  int rv = -1;
    217  SMARTLIST_FOREACH_BEGIN(config_files, const char *, config_file) {
    218    if (sandbox_interned_string_is_missing(config_file)) {
    219      log_err(LD_CONFIG, "Sandbox is active, but a new configuration "
    220              "file \"%s\" has been listed with %%include. Cannot proceed.",
    221              config_file);
    222      goto done;
    223    }
    224 
    225    log_notice(LD_CONFIG, "Including configuration file \"%s\".", config_file);
    226    config_line_t *included_config = NULL;
    227    config_line_t *included_config_last = NULL;
    228    if (config_get_included_config(config_file, recursion_level, extended,
    229                                   &included_config, &included_config_last,
    230                                   opened_lst) < 0) {
    231      goto done;
    232    }
    233 
    234    *next = included_config;
    235    if (included_config_last) {
    236      next = &included_config_last->next;
    237      *list_last = included_config_last;
    238    }
    239  } SMARTLIST_FOREACH_END(config_file);
    240  *list = ret_list;
    241  rv = 0;
    242 
    243 done:
    244  SMARTLIST_FOREACH(config_files, char *, f, tor_free(f));
    245  smartlist_free(config_files);
    246  return rv;
    247 }