]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
fs-util: when calling chase_symlinks() with root path, leave root part unresolved
authorLennart Poettering <lennart@poettering.net>
Tue, 28 Jan 2020 20:02:29 +0000 (21:02 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 28 Jan 2020 21:53:59 +0000 (22:53 +0100)
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

index 5723c845e436285ec6f5b01a2e0fe4051789feaf..5ec32854c307e1551ab5c48d48bb2f4586d929af 100644 (file)
@@ -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