LCOV - code coverage report
Current view: top level - lib/fs - conffile.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 89 99 89.9 %
Date: 2021-11-24 03:28:48 Functions: 5 5 100.0 %

          Line data    Source code
       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         485 : config_get_lines_include(const char *string, config_line_t **result,
      50             :                          int extended, int *has_include,
      51             :                          smartlist_t *opened_lst)
      52             : {
      53         485 :   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 expading 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         125 : expand_glob(const char *pattern, smartlist_t *opened_files)
      63             : {
      64         125 :   if (! has_glob(pattern)) {
      65         116 :     smartlist_t *matches = smartlist_new();
      66         116 :     smartlist_add_strdup(matches, pattern);
      67         116 :     return matches;
      68             :   }
      69             : 
      70           9 :   smartlist_t *matches = tor_glob(pattern);
      71           9 :   if (!matches) {
      72           0 :     if (errno == EPERM) {
      73           0 :       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           0 :     return NULL;
      78             :   }
      79             : 
      80           9 :   if (opened_files) {
      81           2 :     smartlist_t *glob_opened = get_glob_opened_files(pattern);
      82           2 :     if (!glob_opened) {
      83           0 :       SMARTLIST_FOREACH(matches, char *, f, tor_free(f));
      84           0 :       smartlist_free(matches);
      85           0 :       return NULL;
      86             :     }
      87           2 :     smartlist_add_all(opened_files, glob_opened);
      88           2 :     smartlist_free(glob_opened);
      89             :   }
      90           9 :   smartlist_sort_strings(matches);
      91           9 :   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         125 : config_get_file_list(const char *pattern, smartlist_t *opened_files)
     105             : {
     106         125 :   smartlist_t *glob_matches = expand_glob(pattern, opened_files);
     107         125 :   if (!glob_matches) {
     108             :     return NULL;
     109             :   }
     110             : 
     111         125 :   bool error_found = false;
     112         125 :   smartlist_t *file_list = smartlist_new();
     113         256 :   SMARTLIST_FOREACH_BEGIN(glob_matches, char *, path) {
     114         134 :     if (opened_files) {
     115          15 :       smartlist_add_strdup(opened_files, path);
     116             :     }
     117         134 :     if (sandbox_interned_string_is_missing(path)) {
     118           0 :       log_err(LD_CONFIG, "Sandbox is active, but a new configuration "
     119             :               "file \"%s\" has been listed with %%include. Cannot proceed.",
     120             :               path);
     121           0 :       error_found = true;
     122           0 :       break;
     123             :     }
     124             : 
     125         134 :     file_status_t file_type = file_status(path);
     126         134 :     if (file_type == FN_FILE) {
     127         110 :       smartlist_add_strdup(file_list, path);
     128          24 :     } else if (file_type == FN_DIR) {
     129          20 :       smartlist_t *all_files = tor_listdir(path);
     130          20 :       if (!all_files) {
     131             :         error_found = true;
     132         125 :         break;
     133             :       }
     134          19 :       smartlist_sort_strings(all_files);
     135          65 :       SMARTLIST_FOREACH_BEGIN(all_files, char *, f) {
     136          46 :         if (f[0] == '.') {
     137          27 :           continue;
     138             :         }
     139             : 
     140          41 :         char *fullname;
     141          41 :         tor_asprintf(&fullname, "%s"PATH_SEPARATOR"%s", path, f);
     142             : 
     143          41 :         if (opened_files) {
     144          10 :           smartlist_add_strdup(opened_files, fullname);
     145             :         }
     146             : 
     147          41 :         if (file_status(fullname) != FN_FILE) {
     148          22 :           tor_free(fullname);
     149          22 :           continue;
     150             :         }
     151          19 :         smartlist_add(file_list, fullname);
     152          46 :       } SMARTLIST_FOREACH_END(f);
     153          65 :       SMARTLIST_FOREACH(all_files, char *, f, tor_free(f));
     154          19 :       smartlist_free(all_files);
     155           4 :     } else if (file_type == FN_EMPTY) {
     156           2 :         continue;
     157             :     } else {
     158             :       error_found = true;
     159             :       break;
     160             :     }
     161         131 :   } SMARTLIST_FOREACH_END(path);
     162         259 :   SMARTLIST_FOREACH(glob_matches, char *, f, tor_free(f));
     163         125 :   smartlist_free(glob_matches);
     164             : 
     165         125 :   if (error_found) {
     166           3 :     SMARTLIST_FOREACH(file_list, char *, f, tor_free(f));
     167           3 :     smartlist_free(file_list);
     168           3 :     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         129 : 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         129 :   char *included_conf = read_file_to_str(path, 0, NULL);
     184         129 :   if (!included_conf) {
     185             :     return -1;
     186             :   }
     187             : 
     188         129 :   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          32 :     tor_free(included_conf);
     192          32 :     return -1;
     193             :   }
     194             : 
     195          97 :   tor_free(included_conf);
     196          97 :   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         125 : 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         125 :   config_line_t *ret_list = NULL;
     209         125 :   config_line_t **next = &ret_list;
     210             : 
     211         125 :   smartlist_t *config_files = config_get_file_list(pattern, opened_lst);
     212         125 :   if (!config_files) {
     213             :     return -1;
     214             :   }
     215             : 
     216         122 :   int rv = -1;
     217         219 :   SMARTLIST_FOREACH_BEGIN(config_files, const char *, config_file) {
     218         129 :     if (sandbox_interned_string_is_missing(config_file)) {
     219           0 :       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          32 :       goto done;
     223             :     }
     224             : 
     225         129 :     log_notice(LD_CONFIG, "Including configuration file \"%s\".", config_file);
     226         129 :     config_line_t *included_config = NULL;
     227         129 :     config_line_t *included_config_last = NULL;
     228         129 :     if (config_get_included_config(config_file, recursion_level, extended,
     229             :                                    &included_config, &included_config_last,
     230             :                                    opened_lst) < 0) {
     231          32 :       goto done;
     232             :     }
     233             : 
     234          97 :     *next = included_config;
     235          97 :     if (included_config_last) {
     236          92 :       next = &included_config_last->next;
     237          92 :       *list_last = included_config_last;
     238             :     }
     239          97 :   } SMARTLIST_FOREACH_END(config_file);
     240          90 :   *list = ret_list;
     241          90 :   rv = 0;
     242             : 
     243         122 :  done:
     244         251 :   SMARTLIST_FOREACH(config_files, char *, f, tor_free(f));
     245         122 :   smartlist_free(config_files);
     246         122 :   return rv;
     247             : }

Generated by: LCOV version 1.14