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 }