Update copyrights to 2021, using "make update-copyright"
[tor.git] / src / lib / fs / conffile.c
bloba0908ed42e469dfc3b99fd0f634c6e1225dccf38
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 */
7 /**
8 * \file conffile.h
10 * \brief Read configuration files from disk, with full `%include` support.
11 **/
13 #include "lib/fs/conffile.h"
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"
25 #include <stdbool.h>
26 #include <errno.h>
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);
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.
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)
53 return config_get_lines_aux(string, result, extended, 1, has_include,
54 opened_lst, 1, NULL, config_process_include);
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 expand_glob(const char *pattern, smartlist_t *opened_files)
64 if (! has_glob(pattern)) {
65 smartlist_t *matches = smartlist_new();
66 smartlist_add_strdup(matches, pattern);
67 return matches;
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);
77 return NULL;
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;
87 smartlist_add_all(opened_files, glob_opened);
88 smartlist_free(glob_opened);
90 smartlist_sort_strings(matches);
91 return matches;
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.
103 static smartlist_t *
104 config_get_file_list(const char *pattern, smartlist_t *opened_files)
106 smartlist_t *glob_matches = expand_glob(pattern, opened_files);
107 if (!glob_matches) {
108 return NULL;
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);
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;
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;
134 smartlist_sort_strings(all_files);
135 SMARTLIST_FOREACH_BEGIN(all_files, char *, f) {
136 if (f[0] == '.') {
137 continue;
140 char *fullname;
141 tor_asprintf(&fullname, "%s"PATH_SEPARATOR"%s", path, f);
143 if (opened_files) {
144 smartlist_add_strdup(opened_files, fullname);
147 if (file_status(fullname) != FN_FILE) {
148 tor_free(fullname);
149 continue;
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;
161 } SMARTLIST_FOREACH_END(path);
162 SMARTLIST_FOREACH(glob_matches, char *, f, tor_free(f));
163 smartlist_free(glob_matches);
165 if (error_found) {
166 SMARTLIST_FOREACH(file_list, char *, f, tor_free(f));
167 smartlist_free(file_list);
168 file_list = NULL;
171 return file_list;
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)
183 char *included_conf = read_file_to_str(path, 0, NULL);
184 if (!included_conf) {
185 return -1;
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;
195 tor_free(included_conf);
196 return 0;
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)
208 config_line_t *ret_list = NULL;
209 config_line_t **next = &ret_list;
211 smartlist_t *config_files = config_get_file_list(pattern, opened_lst);
212 if (!config_files) {
213 return -1;
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;
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;
234 *next = included_config;
235 if (included_config_last) {
236 next = &included_config_last->next;
237 *list_last = included_config_last;
239 } SMARTLIST_FOREACH_END(config_file);
240 *list = ret_list;
241 rv = 0;
243 done:
244 SMARTLIST_FOREACH(config_files, char *, f, tor_free(f));
245 smartlist_free(config_files);
246 return rv;