Line data Source code
1 : /* Copyright (c) 2003, Roger Dingledine
2 : * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 : * Copyright (c) 2007-2021, The Tor Project, Inc. */
4 : /* See LICENSE for licensing information */
5 :
6 : /**
7 : * \file path.c
8 : *
9 : * \brief Manipulate strings that contain filesystem paths.
10 : **/
11 :
12 : #include "lib/fs/path.h"
13 : #include "lib/malloc/malloc.h"
14 : #include "lib/log/log.h"
15 : #include "lib/log/util_bug.h"
16 : #include "lib/container/smartlist.h"
17 : #include "lib/sandbox/sandbox.h"
18 : #include "lib/string/printf.h"
19 : #include "lib/string/util_string.h"
20 : #include "lib/string/compat_ctype.h"
21 : #include "lib/string/compat_string.h"
22 : #include "lib/fs/files.h"
23 : #include "lib/fs/dir.h"
24 : #include "lib/fs/userdb.h"
25 :
26 : #ifdef HAVE_SYS_TYPES_H
27 : #include <sys/types.h>
28 : #endif
29 : #ifdef HAVE_SYS_STAT_H
30 : #include <sys/stat.h>
31 : #endif
32 : #ifdef HAVE_UNISTD_H
33 : #include <unistd.h>
34 : #endif
35 :
36 : #ifdef _WIN32
37 : #include <windows.h>
38 : #include <shlwapi.h>
39 : #else /* !(defined(_WIN32)) */
40 : #include <dirent.h>
41 : #include <glob.h>
42 : #endif /* defined(_WIN32) */
43 :
44 : #include <errno.h>
45 : #include <string.h>
46 :
47 : /** Removes enclosing quotes from <b>path</b> and unescapes quotes between the
48 : * enclosing quotes. Backslashes are not unescaped. Return the unquoted
49 : * <b>path</b> on success or 0 if <b>path</b> is not quoted correctly. */
50 : char *
51 17 : get_unquoted_path(const char *path)
52 : {
53 17 : size_t len = strlen(path);
54 :
55 17 : if (len == 0) {
56 1 : return tor_strdup("");
57 : }
58 :
59 16 : int has_start_quote = (path[0] == '\"');
60 16 : int has_end_quote = (len > 0 && path[len-1] == '\"');
61 16 : if (has_start_quote != has_end_quote || (len == 1 && has_start_quote)) {
62 : return NULL;
63 : }
64 :
65 11 : char *unquoted_path = tor_malloc(len - has_start_quote - has_end_quote + 1);
66 11 : char *s = unquoted_path;
67 11 : size_t i;
68 60 : for (i = has_start_quote; i < len - has_end_quote; i++) {
69 41 : if (path[i] == '\"' && (i > 0 && path[i-1] == '\\')) {
70 4 : *(s-1) = path[i];
71 37 : } else if (path[i] != '\"') {
72 34 : *s++ = path[i];
73 : } else { /* unescaped quote */
74 3 : tor_free(unquoted_path);
75 3 : return NULL;
76 : }
77 : }
78 8 : *s = '\0';
79 8 : return unquoted_path;
80 : }
81 :
82 : /** Expand any homedir prefix on <b>filename</b>; return a newly allocated
83 : * string. */
84 : char *
85 991 : expand_filename(const char *filename)
86 : {
87 991 : tor_assert(filename);
88 : #ifdef _WIN32
89 : /* Might consider using GetFullPathName() as described here:
90 : * http://etutorials.org/Programming/secure+programming/
91 : * Chapter+3.+Input+Validation/3.7+Validating+Filenames+and+Paths/
92 : */
93 : return tor_strdup(filename);
94 : #else /* !defined(_WIN32) */
95 991 : if (*filename == '~') {
96 530 : char *home, *result=NULL;
97 530 : const char *rest;
98 :
99 530 : if (filename[1] == '/' || filename[1] == '\0') {
100 530 : home = getenv("HOME");
101 530 : if (!home) {
102 3 : log_warn(LD_CONFIG, "Couldn't find $HOME environment variable while "
103 : "expanding \"%s\"; defaulting to \"\".", filename);
104 3 : home = tor_strdup("");
105 : } else {
106 527 : home = tor_strdup(home);
107 : }
108 530 : rest = strlen(filename)>=2?(filename+2):"";
109 : } else {
110 : #ifdef HAVE_PWD_H
111 0 : char *username, *slash;
112 0 : slash = strchr(filename, '/');
113 0 : if (slash)
114 0 : username = tor_strndup(filename+1,slash-filename-1);
115 : else
116 0 : username = tor_strdup(filename+1);
117 0 : if (!(home = get_user_homedir(username))) {
118 0 : log_warn(LD_CONFIG,"Couldn't get homedir for \"%s\"",username);
119 0 : tor_free(username);
120 0 : return NULL;
121 : }
122 0 : tor_free(username);
123 0 : rest = slash ? (slash+1) : "";
124 : #else /* !defined(HAVE_PWD_H) */
125 : log_warn(LD_CONFIG, "Couldn't expand homedir on system without pwd.h");
126 : return tor_strdup(filename);
127 : #endif /* defined(HAVE_PWD_H) */
128 : }
129 530 : tor_assert(home);
130 : /* Remove trailing slash. */
131 530 : if (strlen(home)>1 && !strcmpend(home,PATH_SEPARATOR)) {
132 3 : home[strlen(home)-1] = '\0';
133 : }
134 530 : tor_asprintf(&result,"%s"PATH_SEPARATOR"%s",home,rest);
135 530 : tor_free(home);
136 530 : return result;
137 : } else {
138 461 : return tor_strdup(filename);
139 : }
140 : #endif /* defined(_WIN32) */
141 : }
142 :
143 : /** Return true iff <b>filename</b> is a relative path. */
144 : int
145 1222 : path_is_relative(const char *filename)
146 : {
147 1222 : if (filename && filename[0] == '/')
148 : return 0;
149 : #ifdef _WIN32
150 : else if (filename && filename[0] == '\\')
151 : return 0;
152 : else if (filename && strlen(filename)>3 && TOR_ISALPHA(filename[0]) &&
153 : filename[1] == ':' && filename[2] == '\\')
154 : return 0;
155 : #endif /* defined(_WIN32) */
156 : else
157 9 : return 1;
158 : }
159 :
160 : /** Clean up <b>name</b> so that we can use it in a call to "stat". On Unix,
161 : * we do nothing. On Windows, we remove a trailing slash, unless the path is
162 : * the root of a disk. */
163 : void
164 1121 : clean_fname_for_stat(char *name)
165 : {
166 : #ifdef _WIN32
167 : size_t len = strlen(name);
168 : if (!len)
169 : return;
170 : if (name[len-1]=='\\' || name[len-1]=='/') {
171 : if (len == 1 || (len==3 && name[1]==':'))
172 : return;
173 : name[len-1]='\0';
174 : }
175 : #else /* !defined(_WIN32) */
176 1121 : (void)name;
177 : #endif /* defined(_WIN32) */
178 1121 : }
179 :
180 : /** Modify <b>fname</b> to contain the name of its parent directory. Doesn't
181 : * actually examine the filesystem; does a purely syntactic modification.
182 : *
183 : * The parent of the root director is considered to be itself.
184 : *
185 : * Path separators are the forward slash (/) everywhere and additionally
186 : * the backslash (\) on Win32.
187 : *
188 : * Cuts off any number of trailing path separators but otherwise ignores
189 : * them for purposes of finding the parent directory.
190 : *
191 : * Returns 0 if a parent directory was successfully found, -1 otherwise (fname
192 : * did not have any path separators or only had them at the end).
193 : * */
194 : int
195 23 : get_parent_directory(char *fname)
196 : {
197 23 : char *cp;
198 23 : int at_end = 1;
199 23 : tor_assert(fname);
200 : #ifdef _WIN32
201 : /* If we start with, say, c:, then don't consider that the start of the path
202 : */
203 : if (fname[0] && fname[1] == ':') {
204 : fname += 2;
205 : }
206 : #endif /* defined(_WIN32) */
207 : /* Now we want to remove all path-separators at the end of the string,
208 : * and to remove the end of the string starting with the path separator
209 : * before the last non-path-separator. In perl, this would be
210 : * s#[/]*$##; s#/[^/]*$##;
211 : * on a unixy platform.
212 : */
213 23 : cp = fname + strlen(fname);
214 23 : at_end = 1;
215 119 : while (--cp >= fname) {
216 117 : int is_sep = (*cp == '/'
217 : #ifdef _WIN32
218 : || *cp == '\\'
219 : #endif
220 : );
221 117 : if (is_sep) {
222 38 : if (cp == fname) {
223 : /* This is the first separator in the file name; don't remove it! */
224 4 : cp[1] = '\0';
225 4 : return 0;
226 : }
227 34 : *cp = '\0';
228 34 : if (! at_end)
229 : return 0;
230 : } else {
231 : at_end = 0;
232 : }
233 : }
234 : return -1;
235 : }
236 :
237 : #ifndef _WIN32
238 : /** Return a newly allocated string containing the output of getcwd(). Return
239 : * NULL on failure. (We can't just use getcwd() into a PATH_MAX buffer, since
240 : * Hurd hasn't got a PATH_MAX.)
241 : */
242 : static char *
243 149 : alloc_getcwd(void)
244 : {
245 : #ifdef HAVE_GET_CURRENT_DIR_NAME
246 : /* Glibc makes this nice and simple for us. */
247 149 : char *cwd = get_current_dir_name();
248 149 : char *result = NULL;
249 149 : if (cwd) {
250 : /* We make a copy here, in case tor_malloc() is not malloc(). */
251 149 : result = tor_strdup(cwd);
252 149 : raw_free(cwd); // alias for free to avoid tripping check-spaces.
253 : }
254 149 : return result;
255 : #else /* !defined(HAVE_GET_CURRENT_DIR_NAME) */
256 : size_t size = 1024;
257 : char *buf = NULL;
258 : char *ptr = NULL;
259 :
260 : while (ptr == NULL) {
261 : buf = tor_realloc(buf, size);
262 : ptr = getcwd(buf, size);
263 :
264 : if (ptr == NULL && errno != ERANGE) {
265 : tor_free(buf);
266 : return NULL;
267 : }
268 :
269 : size *= 2;
270 : }
271 : return buf;
272 : #endif /* defined(HAVE_GET_CURRENT_DIR_NAME) */
273 : }
274 : #endif /* !defined(_WIN32) */
275 :
276 : /** Expand possibly relative path <b>fname</b> to an absolute path.
277 : * Return a newly allocated string, which may be a duplicate of <b>fname</b>.
278 : */
279 : char *
280 460 : make_path_absolute(const char *fname)
281 : {
282 : #ifdef _WIN32
283 : char *absfname_malloced = _fullpath(NULL, fname, 1);
284 :
285 : /* We don't want to assume that tor_free can free a string allocated
286 : * with malloc. On failure, return fname (it's better than nothing). */
287 : char *absfname = tor_strdup(absfname_malloced ? absfname_malloced : fname);
288 : if (absfname_malloced) raw_free(absfname_malloced);
289 :
290 : return absfname;
291 : #else /* !defined(_WIN32) */
292 460 : char *absfname = NULL, *path = NULL;
293 :
294 460 : tor_assert(fname);
295 :
296 460 : if (fname[0] == '/') {
297 311 : absfname = tor_strdup(fname);
298 : } else {
299 149 : path = alloc_getcwd();
300 149 : if (path) {
301 149 : tor_asprintf(&absfname, "%s/%s", path, fname);
302 149 : tor_free(path);
303 : } else {
304 : /* LCOV_EXCL_START Can't make getcwd fail. */
305 : /* If getcwd failed, the best we can do here is keep using the
306 : * relative path. (Perhaps / isn't readable by this UID/GID.) */
307 : log_warn(LD_GENERAL, "Unable to find current working directory: %s",
308 : strerror(errno));
309 : absfname = tor_strdup(fname);
310 : /* LCOV_EXCL_STOP */
311 : }
312 : }
313 460 : return absfname;
314 : #endif /* defined(_WIN32) */
315 : }
316 :
317 : /* The code below implements tor_glob and get_glob_opened_files. Because it is
318 : * not easy to understand it by looking at individual functions, the big
319 : * picture explanation here should be read first.
320 : *
321 : * Purpose of the functions:
322 : * - tor_glob - receives a pattern and returns all the paths that result from
323 : * its glob expansion, globs can be present on all path components.
324 : * - get_glob_opened_files - receives a pattern and returns all the paths that
325 : * are opened during its expansion (the paths before any path fragment that
326 : * contains a glob as they have to be opened to check for glob matches). This
327 : * is used to get the paths that have to be added to the seccomp sandbox
328 : * allowed list.
329 : *
330 : * Due to OS API differences explained below, the implementation of tor_glob is
331 : * completely different for Windows and POSIX systems, so we ended up with
332 : * three different implementations:
333 : * - tor_glob for POSIX - as POSIX glob does everything we need, we simply call
334 : * it and process the results. This is completely implemented in tor_glob.
335 : * - tor_glob for WIN32 - because the WIN32 API only supports expanding globs
336 : * in the last path fragment, we need to expand the globs in each path
337 : * fragment manually and call recursively to get the same behaviour as POSIX
338 : * glob. When there are no globs in pattern, we know we are on the last path
339 : * fragment and collect the full path.
340 : * - get_glob_opened_files - because the paths before any path fragment with a
341 : * glob will be opened to check for matches, we need to collect them and we
342 : * need to expand the globs in each path fragments and call recursively until
343 : * we find no more globs.
344 : *
345 : * As seen from the description above, both tor_glob for WIN32 and
346 : * get_glob_opened_files receive a pattern and return a list of paths and have
347 : * to expand all path fragments that contain globs and call themselves
348 : * recursively. The differences are:
349 : * - get_glob_opened_files collects paths before path fragments with globs
350 : * while tor_glob for WIN32 collects full paths resulting from the expansion
351 : * of all globs.
352 : * - get_glob_opened_files can call tor_glob to expand path fragments with
353 : * globs while tor_glob for WIN32 cannot because it IS tor_glob. For tor_glob
354 : * for WIN32, an auxiliary function has to be used for this purpose.
355 : *
356 : * To avoid code duplication, the logic of tor_glob for WIN32 and
357 : * get_glob_opened_files is implemented in get_glob_paths. The differences are
358 : * configured by the extra function parameters:
359 : * - final - if true, returns a list of paths obtained from expanding pattern
360 : * (implements tor_glob). Otherwise, returns the paths before path fragments
361 : * with globs (implements get_glob_opened_files).
362 : * - unglob - function used to expand a path fragment. The function signature
363 : * is defined by the unglob_fn typedef. Two implementations are available:
364 : * - unglob_win32 - uses tor_listdir and PathMatchSpec (for tor_glob WIN32)
365 : * - unglob_opened_files - uses tor_glob (for get_glob_opened_files)
366 : */
367 :
368 : /** Returns true if the character at position <b>pos</b> in <b>pattern</b> is
369 : * considered a glob. Returns false otherwise. Takes escaping into account on
370 : * systems where escaping globs is supported. */
371 : static inline bool
372 9642 : is_glob_char(const char *pattern, int pos)
373 : {
374 9642 : bool is_glob = pattern[pos] == '*' || pattern[pos] == '?';
375 : #ifdef _WIN32
376 : return is_glob;
377 : #else /* !defined(_WIN32) */
378 9642 : bool is_escaped = pos > 0 && pattern[pos-1] == '\\';
379 9642 : return is_glob && !is_escaped;
380 : #endif /* defined(_WIN32) */
381 : }
382 :
383 : /** Expands the first path fragment of <b>pattern</b> that contains globs. The
384 : * path fragment is between <b>prev_sep</b> and <b>next_sep</b>. If the path
385 : * fragment is the last fragment of <b>pattern</b>, <b>next_sep</b> will be the
386 : * index of the last char. Returns a list of paths resulting from the glob
387 : * expansion of the path fragment. Anything after <b>next_sep</b> is not
388 : * included in the returned list. Returns NULL on failure. */
389 : typedef struct smartlist_t * unglob_fn(const char *pattern, int prev_sep,
390 : int next_sep);
391 :
392 : /** Adds <b>path</b> to <b>result</b> if it exists and is a file type we can
393 : * handle. Returns false if <b>path</b> is a file type we cannot handle,
394 : * returns true otherwise. Used on tor_glob for WIN32. */
395 : static bool
396 0 : add_non_glob_path(const char *path, struct smartlist_t *result)
397 : {
398 0 : file_status_t file_type = file_status(path);
399 0 : if (file_type == FN_ERROR) {
400 : return false;
401 0 : } else if (file_type != FN_NOENT) {
402 0 : char *to_add = tor_strdup(path);
403 0 : clean_fname_for_stat(to_add);
404 0 : smartlist_add(result, to_add);
405 : }
406 : /* If WIN32 tor_glob is called with a non-existing path, we want it to
407 : * return an empty list instead of error to match the regular version */
408 : return true;
409 : }
410 :
411 : /** Auxiliary function used by get_glob_opened_files and WIN32 tor_glob.
412 : * Returns a list of paths obtained from <b>pattern</b> using <b>unglob</b> to
413 : * expand each path fragment. If <b>final</b> is true, the paths are the result
414 : * of the glob expansion of <b>pattern</b> (implements tor_glob). Otherwise,
415 : * the paths are the paths opened by glob while expanding <b>pattern</b>
416 : * (implements get_glob_opened_files). Returns NULL on failure. */
417 : static struct smartlist_t *
418 31 : get_glob_paths(const char *pattern, unglob_fn unglob, bool final)
419 : {
420 31 : smartlist_t *result = smartlist_new();
421 31 : int i, prev_sep = -1, next_sep = -1;
422 31 : bool is_glob = false, error_found = false, is_sep = false, is_last = false;
423 :
424 : // find first path fragment with globs
425 2047 : for (i = 0; pattern[i]; i++) {
426 2007 : is_glob = is_glob || is_glob_char(pattern, i);
427 2007 : is_last = !pattern[i+1];
428 2007 : is_sep = pattern[i] == *PATH_SEPARATOR || pattern[i] == '/';
429 2007 : if (is_sep || is_last) {
430 177 : prev_sep = next_sep;
431 177 : next_sep = i; // next_sep+1 is start of next fragment or end of string
432 177 : if (is_glob) {
433 : break;
434 : }
435 : }
436 : }
437 :
438 31 : if (!is_glob) { // pattern fully expanded or no glob in pattern
439 9 : if (final && !add_non_glob_path(pattern, result)) {
440 0 : error_found = true;
441 0 : goto end;
442 : }
443 9 : return result;
444 : }
445 :
446 22 : if (!final) {
447 : // add path before the glob to result
448 22 : int len = prev_sep < 1 ? prev_sep + 1 : prev_sep; // handle /*
449 22 : char *path_until_glob = tor_strndup(pattern, len);
450 22 : smartlist_add(result, path_until_glob);
451 : }
452 :
453 22 : smartlist_t *unglobbed_paths = unglob(pattern, prev_sep, next_sep);
454 22 : if (!unglobbed_paths) {
455 : error_found = true;
456 : } else {
457 : // for each path for current fragment, add the rest of the pattern
458 : // and call recursively to get all expanded paths
459 37 : SMARTLIST_FOREACH_BEGIN(unglobbed_paths, char *, current_path) {
460 15 : char *next_path;
461 15 : tor_asprintf(&next_path, "%s"PATH_SEPARATOR"%s", current_path,
462 15 : &pattern[next_sep+1]);
463 15 : smartlist_t *opened_next = get_glob_paths(next_path, unglob, final);
464 15 : tor_free(next_path);
465 15 : if (!opened_next) {
466 0 : error_found = true;
467 0 : break;
468 : }
469 15 : smartlist_add_all(result, opened_next);
470 15 : smartlist_free(opened_next);
471 15 : } SMARTLIST_FOREACH_END(current_path);
472 37 : SMARTLIST_FOREACH(unglobbed_paths, char *, p, tor_free(p));
473 22 : smartlist_free(unglobbed_paths);
474 : }
475 :
476 22 : end:
477 22 : if (error_found) {
478 0 : SMARTLIST_FOREACH(result, char *, p, tor_free(p));
479 0 : smartlist_free(result);
480 0 : result = NULL;
481 : }
482 : return result;
483 : }
484 :
485 : #ifdef _WIN32
486 : /** Expands globs in <b>pattern</b> for the path fragment between
487 : * <b>prev_sep</b> and <b>next_sep</b> using the WIN32 API. Returns NULL on
488 : * failure. Used by the WIN32 implementation of tor_glob. Implements unglob_fn,
489 : * see its description for more details. */
490 : static struct smartlist_t *
491 : unglob_win32(const char *pattern, int prev_sep, int next_sep)
492 : {
493 : smartlist_t *result = smartlist_new();
494 : int len = prev_sep < 1 ? prev_sep + 1 : prev_sep; // handle /*
495 : char *path_until_glob = tor_strndup(pattern, len);
496 :
497 : if (!is_file(file_status(path_until_glob))) {
498 : smartlist_t *filenames = tor_listdir(path_until_glob);
499 : if (!filenames) {
500 : smartlist_free(result);
501 : result = NULL;
502 : } else {
503 : SMARTLIST_FOREACH_BEGIN(filenames, char *, filename) {
504 : TCHAR tpattern[MAX_PATH] = {0};
505 : TCHAR tfile[MAX_PATH] = {0};
506 : char *full_path;
507 : tor_asprintf(&full_path, "%s"PATH_SEPARATOR"%s",
508 : path_until_glob, filename);
509 : char *path_curr_glob = tor_strndup(pattern, next_sep + 1);
510 : // *\ must return only dirs, remove \ from the pattern so it matches
511 : if (is_dir(file_status(full_path))) {
512 : clean_fname_for_stat(path_curr_glob);
513 : }
514 : #ifdef UNICODE
515 : mbstowcs(tpattern, path_curr_glob, MAX_PATH);
516 : mbstowcs(tfile, full_path, MAX_PATH);
517 : #else /* !defined(UNICODE) */
518 : strlcpy(tpattern, path_curr_glob, MAX_PATH);
519 : strlcpy(tfile, full_path, MAX_PATH);
520 : #endif /* defined(UNICODE) */
521 : if (PathMatchSpec(tfile, tpattern)) {
522 : smartlist_add(result, full_path);
523 : } else {
524 : tor_free(full_path);
525 : }
526 : tor_free(path_curr_glob);
527 : } SMARTLIST_FOREACH_END(filename);
528 : SMARTLIST_FOREACH(filenames, char *, p, tor_free(p));
529 : smartlist_free(filenames);
530 : }
531 : }
532 : tor_free(path_until_glob);
533 : return result;
534 : }
535 : #elif HAVE_GLOB
536 : #ifdef GLOB_ALTDIRFUNC // prevent warning about unused functions
537 : /** Same as opendir but calls sandbox_intern_string before */
538 : static DIR *
539 30 : prot_opendir(const char *name)
540 : {
541 30 : if (sandbox_interned_string_is_missing(name)) {
542 0 : errno = EPERM;
543 0 : return NULL;
544 : }
545 30 : return opendir(sandbox_intern_string(name));
546 : }
547 :
548 : /** Same as stat but calls sandbox_intern_string before */
549 : static int
550 6 : prot_stat(const char *pathname, struct stat *buf)
551 : {
552 6 : if (sandbox_interned_string_is_missing(pathname)) {
553 0 : errno = EPERM;
554 0 : return -1;
555 : }
556 6 : return stat(sandbox_intern_string(pathname), buf);
557 : }
558 :
559 : /** Same as lstat but calls sandbox_intern_string before */
560 : static int
561 7 : prot_lstat(const char *pathname, struct stat *buf)
562 : {
563 7 : if (sandbox_interned_string_is_missing(pathname)) {
564 0 : errno = EPERM;
565 0 : return -1;
566 : }
567 7 : return lstat(sandbox_intern_string(pathname), buf);
568 : }
569 : /** As closedir, but has the right type for gl_closedir */
570 : static void
571 28 : wrap_closedir(void *arg)
572 : {
573 28 : closedir(arg);
574 28 : }
575 : #endif /* defined(GLOB_ALTDIRFUNC) */
576 :
577 : /** Function passed to glob to handle processing errors. <b>epath</b> is the
578 : * path that caused the error and <b>eerrno</b> is the errno set by the
579 : * function that failed. We want to ignore ENOENT and ENOTDIR because, in BSD
580 : * systems, these are not ignored automatically, which makes glob fail when
581 : * globs expand to non-existing paths and GLOB_ERR is set.
582 : */
583 : static int
584 1 : glob_errfunc(const char *epath, int eerrno)
585 : {
586 1 : (void)epath;
587 1 : return eerrno == ENOENT || eerrno == ENOTDIR ? 0 : -1;
588 : }
589 : #endif /* defined(HAVE_GLOB) */
590 :
591 : /** Return a new list containing the paths that match the pattern
592 : * <b>pattern</b>. Return NULL on error. On POSIX systems, errno is set by the
593 : * glob function or is set to EPERM if glob tried to access a file not allowed
594 : * by the seccomp sandbox.
595 : */
596 : struct smartlist_t *
597 30 : tor_glob(const char *pattern)
598 : {
599 30 : smartlist_t *result = NULL;
600 :
601 : #ifdef _WIN32
602 : // PathMatchSpec does not support forward slashes, change them to backslashes
603 : char *pattern_normalized = tor_strdup(pattern);
604 : tor_strreplacechar(pattern_normalized, '/', *PATH_SEPARATOR);
605 : result = get_glob_paths(pattern_normalized, unglob_win32, true);
606 : tor_free(pattern_normalized);
607 : #elif HAVE_GLOB /* !(defined(_WIN32)) */
608 30 : glob_t matches;
609 30 : int flags = GLOB_NOSORT;
610 : #ifdef GLOB_ALTDIRFUNC
611 : /* use functions that call sandbox_intern_string */
612 30 : flags |= GLOB_ALTDIRFUNC;
613 30 : typedef void *(*gl_opendir)(const char * name);
614 30 : typedef struct dirent *(*gl_readdir)(void *);
615 30 : typedef void (*gl_closedir)(void *);
616 30 : matches.gl_opendir = (gl_opendir) &prot_opendir;
617 30 : matches.gl_readdir = (gl_readdir) &readdir;
618 30 : matches.gl_closedir = (gl_closedir) &wrap_closedir;
619 30 : matches.gl_stat = &prot_stat;
620 30 : matches.gl_lstat = &prot_lstat;
621 : #endif /* defined(GLOB_ALTDIRFUNC) */
622 : // use custom error handler to workaround BSD quirks and do not set GLOB_ERR
623 : // because it would make glob fail on error even if the error handler ignores
624 : // the error
625 30 : int ret = glob(pattern, flags, glob_errfunc, &matches);
626 30 : if (ret == GLOB_NOMATCH) {
627 6 : return smartlist_new();
628 24 : } else if (ret != 0) {
629 : return NULL;
630 : }
631 :
632 : // #40141, !249: workaround for glibc bug where patterns ending in path
633 : // separator match files and folders instead of folders only.
634 : // this could be in #ifdef __GLIBC__ but: 1. it might affect other libcs too,
635 : // and 2. it doesn't cost much to stat each match again since libc is already
636 : // supposed to do it (otherwise the file may be on slow NFS or something)
637 23 : size_t pattern_len = strlen(pattern);
638 23 : bool dir_only = pattern_len > 0 && pattern[pattern_len-1] == *PATH_SEPARATOR;
639 :
640 23 : result = smartlist_new();
641 23 : size_t i;
642 117 : for (i = 0; i < matches.gl_pathc; i++) {
643 71 : char *match = tor_strdup(matches.gl_pathv[i]);
644 71 : size_t len = strlen(match);
645 71 : if (len > 0 && match[len-1] == *PATH_SEPARATOR) {
646 5 : match[len-1] = '\0';
647 : }
648 :
649 71 : if (!dir_only || (dir_only && is_dir(file_status(match)))) {
650 70 : smartlist_add(result, match);
651 : } else {
652 1 : tor_free(match);
653 : }
654 : }
655 23 : globfree(&matches);
656 : #else
657 : (void)pattern;
658 : return result;
659 : #endif /* defined(_WIN32) || ... */
660 :
661 23 : return result;
662 : }
663 :
664 : /** Returns true if <b>s</b> contains characters that can be globbed.
665 : * Returns false otherwise. */
666 : bool
667 147 : has_glob(const char *s)
668 : {
669 147 : int i;
670 7780 : for (i = 0; s[i]; i++) {
671 7645 : if (is_glob_char(s, i)) {
672 : return true;
673 : }
674 : }
675 : return false;
676 : }
677 :
678 : /** Expands globs in <b>pattern</b> for the path fragment between
679 : * <b>prev_sep</b> and <b>next_sep</b> using tor_glob. Returns NULL on
680 : * failure. Used by get_glob_opened_files. Implements unglob_fn, see its
681 : * description for more details. */
682 : static struct smartlist_t *
683 22 : unglob_opened_files(const char *pattern, int prev_sep, int next_sep)
684 : {
685 22 : (void)prev_sep;
686 22 : smartlist_t *result = smartlist_new();
687 : // if the following fragments have no globs, we're done
688 22 : if (has_glob(&pattern[next_sep+1])) {
689 : // if there is a glob after next_sep, we know next_sep is a separator and
690 : // not the last char and glob_path will have the path without the separator
691 3 : char *glob_path = tor_strndup(pattern, next_sep);
692 3 : smartlist_t *child_paths = tor_glob(glob_path);
693 3 : tor_free(glob_path);
694 3 : if (!child_paths) {
695 0 : smartlist_free(result);
696 0 : result = NULL;
697 : } else {
698 3 : smartlist_add_all(result, child_paths);
699 3 : smartlist_free(child_paths);
700 : }
701 : }
702 22 : return result;
703 : }
704 :
705 : /** Returns a list of files that are opened by the tor_glob function when
706 : * called with <b>pattern</b>. Returns NULL on error. The purpose of this
707 : * function is to create a list of files to be added to the sandbox white list
708 : * before the sandbox is enabled. */
709 : struct smartlist_t *
710 16 : get_glob_opened_files(const char *pattern)
711 : {
712 16 : return get_glob_paths(pattern, unglob_opened_files, false);
713 : }
|