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 : }
|