diff options
author | Luca Boccassi <bluca@debian.org> | 2023-02-17 11:44:47 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-17 11:44:47 +0000 |
commit | b8933fa843406390f8412a60ff1a726886b2b405 (patch) | |
tree | ccb5db5c2b216d96ca3df28b3804bb663c501ea7 /src/basic | |
parent | fdc2ad76b47292a1431d3fc41b308dd020f1ad85 (diff) | |
parent | accc26a0e543c9f9e2e5adc1ad6c5a2b9d64670d (diff) | |
download | systemd-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.c | 65 |
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; |