From c2595d3b02849b7baa94483f03cbc888e0c63ebd Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 28 Jan 2020 21:02:29 +0100 Subject: [PATCH] fs-util: when calling chase_symlinks() with root path, leave root part unresolved Previously there was a weird asymmetry: initially we'd resolve the specified prefix path when chasing symlinks together with the actual path we were supposed to cover, except when we hit an absolute symlink where we'd use the root as it was. Let's unify handling here: the prefix path is never resolved, and always left as it is. This in particular fixes issues with symlinks in the prefix path, as that confused the check that made sure we never left the root directory. Fixes: #14634 Replaces: #14635 --- src/basic/fs-util.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 5723c845e43..5ec32854c30 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -810,7 +810,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (r < 0) return r; - fd = open("/", O_CLOEXEC|O_NOFOLLOW|O_PATH); + fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH); if (fd < 0) return -errno; @@ -819,6 +819,33 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, return -errno; } + if (root) { + _cleanup_free_ char *absolute = NULL; + const char *e; + + /* If we are operating on a root directory, let's take the root directory as it is. */ + + e = path_startswith(buffer, root); + if (!e) + return log_full_errno(flags & CHASE_WARN ? LOG_WARNING : LOG_DEBUG, + SYNTHETIC_ERRNO(ECHRNG), + "Specified path '%s' is outside of specified root directory '%s', refusing to resolve.", + path, root); + + /* Make sure "done" ends without a slash */ + done = strdup(root); + if (!done) + return -ENOMEM; + delete_trailing_chars(done, "/"); + + /* Make sure "todo" starts with a slash */ + absolute = strjoin("/", e); + if (!absolute) + return -ENOMEM; + + free_and_replace(buffer, absolute); + } + todo = buffer; for (;;) { _cleanup_free_ char *first = NULL; @@ -930,7 +957,6 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(child, &st) < 0) return -errno; if ((flags & CHASE_SAFE) && - (empty_or_root(root) || (size_t)(todo - buffer) > strlen(root)) && unsafe_transition(&previous_stat, &st)) return log_unsafe_transition(fd, child, path, flags); @@ -961,7 +987,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, * directory as base. */ safe_close(fd); - fd = open(root ?: "/", O_CLOEXEC|O_NOFOLLOW|O_PATH); + fd = open(root ?: "/", O_CLOEXEC|O_DIRECTORY|O_PATH); if (fd < 0) return -errno; @@ -984,6 +1010,8 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, done = strdup(root); if (!done) return -ENOMEM; + + delete_trailing_chars(done, "/"); } /* Prefix what's left to do with what we just read, and start the loop again, but