summaryrefslogtreecommitdiff
path: root/src/basic
diff options
context:
space:
mode:
authorLuca Boccassi <bluca@debian.org>2023-02-17 11:44:47 +0000
committerGitHub <noreply@github.com>2023-02-17 11:44:47 +0000
commitb8933fa843406390f8412a60ff1a726886b2b405 (patch)
treeccb5db5c2b216d96ca3df28b3804bb663c501ea7 /src/basic
parentfdc2ad76b47292a1431d3fc41b308dd020f1ad85 (diff)
parentaccc26a0e543c9f9e2e5adc1ad6c5a2b9d64670d (diff)
downloadsystemd-b8933fa843406390f8412a60ff1a726886b2b405.tar.gz
Merge pull request #26341 from DaanDeMeyer/chase-fixes
chase-symlinks fixes
Diffstat (limited to 'src/basic')
-rw-r--r--src/basic/chase-symlinks.c65
1 files changed, 36 insertions, 29 deletions
diff --git a/src/basic/chase-symlinks.c b/src/basic/chase-symlinks.c
index 4cc0a01df8..3a4fdcd1e0 100644
--- a/src/basic/chase-symlinks.c
+++ b/src/basic/chase-symlinks.c
@@ -102,20 +102,22 @@ int chase_symlinks_at(
path = ".";
/* This function resolves symlinks of the path relative to the given directory file descriptor. If
- * CHASE_SYMLINKS_RESOLVE_IN_ROOT is specified, symlinks are resolved relative to the given directory
- * file descriptor. Otherwise, they are resolved relative to the root directory of the host.
+ * CHASE_SYMLINKS_RESOLVE_IN_ROOT is specified and a directory file descriptor is provided, symlinks
+ * are resolved relative to the given directory file descriptor. Otherwise, they are resolved
+ * relative to the root directory of the host.
*
- * Note that when CHASE_SYMLINKS_RESOLVE_IN_ROOT is specified and we find an absolute symlink, it is
- * resolved relative to given directory file descriptor and not the root of the host. Also, when
- * following relative symlinks, this functions ensure they cannot be used to "escape" the given
- * directory file descriptor. The "path" parameter is always interpreted relative to the given
- * directory file descriptor. If the given directory file descriptor is AT_FDCWD and "path" is
- * absolute, it is interpreted relative to the root directory of the host.
+ * Note that when a positive directory file descriptor is provided and CHASE_AT_RESOLVE_IN_ROOT is
+ * specified and we find an absolute symlink, it is resolved relative to given directory file
+ * descriptor and not the root of the host. Also, when following relative symlinks, this functions
+ * ensures they cannot be used to "escape" the given directory file descriptor. If a positive
+ * directory file descriptor is provided, the "path" parameter is always interpreted relative to the
+ * given directory file descriptor, even if it is absolute. If the given directory file descriptor is
+ * AT_FDCWD and "path" is absolute, it is interpreted relative to the root directory of the host.
*
* If "dir_fd" is a valid directory fd, "path" is an absolute path and "ret_path" is not NULL, this
* functions returns a relative path in "ret_path" because openat() like functions generally ignore
* the directory fd if they are provided with an absolute path. On the other hand, if "dir_fd" is
- * AT_FDCWD and "path" is an absolute path, we need to return an absolute path in "ret_path" because
+ * AT_FDCWD and "path" is an absolute path, we return an absolute path in "ret_path" because
* otherwise, if the caller passes the returned relative path to another openat() like function, it
* would be resolved relative to the current working directory instead of to "/".
*
@@ -177,21 +179,30 @@ int chase_symlinks_at(
if (!buffer)
return -ENOMEM;
- bool need_absolute = !FLAGS_SET(flags, CHASE_AT_RESOLVE_IN_ROOT) && path_is_absolute(path);
+ /* If we receive an absolute path together with AT_FDCWD, we need to return an absolute path, because
+ * a relative path would be interpreted relative to the current working directory. */
+ bool need_absolute = dir_fd == AT_FDCWD && path_is_absolute(path);
if (need_absolute) {
done = strdup("/");
if (!done)
return -ENOMEM;
}
- if (FLAGS_SET(flags, CHASE_AT_RESOLVE_IN_ROOT))
+ /* If we get AT_FDCWD, we always resolve symlinks relative to the host's root. Only if a positive
+ * directory file descriptor is provided will we look at CHASE_AT_RESOLVE_IN_ROOT to determine
+ * whether to resolve symlinks in it or not. */
+ if (dir_fd >= 0 && FLAGS_SET(flags, CHASE_AT_RESOLVE_IN_ROOT))
root_fd = openat(dir_fd, ".", O_CLOEXEC|O_DIRECTORY|O_PATH);
else
root_fd = open("/", O_CLOEXEC|O_DIRECTORY|O_PATH);
if (root_fd < 0)
return -errno;
- if (FLAGS_SET(flags, CHASE_AT_RESOLVE_IN_ROOT) || !path_is_absolute(path))
+ /* If a positive directory file descriptor is provided, always resolve the given path relative to it,
+ * regardless of whether it is absolute or not. If we get AT_FDCWD, follow regular openat()
+ * semantics, if the path is relative, resolve against the current working directory. Otherwise,
+ * resolve against root. */
+ if (dir_fd >= 0 || !path_is_absolute(path))
fd = openat(dir_fd, ".", O_CLOEXEC|O_DIRECTORY|O_PATH);
else
fd = open("/", O_CLOEXEC|O_DIRECTORY|O_PATH);
@@ -453,30 +464,26 @@ int chase_symlinks(
return r;
}
- if (root) {
- path = path_startswith(absolute, root);
- if (!path)
- 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.",
- absolute, root);
+ path = path_startswith(absolute, empty_to_root(root));
+ if (!path)
+ 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.",
+ absolute, empty_to_root(root));
- fd = open(root, O_CLOEXEC|O_DIRECTORY|O_PATH);
- if (fd < 0)
- return -errno;
+ fd = open(empty_to_root(root), O_CLOEXEC|O_DIRECTORY|O_PATH);
+ if (fd < 0)
+ return -errno;
- flags |= CHASE_AT_RESOLVE_IN_ROOT;
- } else {
- path = absolute;
- fd = AT_FDCWD;
- }
+ flags |= CHASE_AT_RESOLVE_IN_ROOT;
+ flags &= ~CHASE_PREFIX_ROOT;
- r = chase_symlinks_at(fd, path, flags & ~CHASE_PREFIX_ROOT, ret_path ? &p : NULL, ret_fd ? &pfd : NULL);
+ r = chase_symlinks_at(fd, path, flags, ret_path ? &p : NULL, ret_fd ? &pfd : NULL);
if (r < 0)
return r;
if (ret_path) {
- char *q = path_join(root, p);
+ char *q = path_join(empty_to_root(root), p);
if (!q)
return -ENOMEM;