mirror of
https://github.com/systemd/systemd.git
synced 2026-08-08 00:51:19 +00:00
chase: tighten checks on ".." once we hit the root of an CHASE_AT_RESOLVE_IN_ROOT root tree
Let's harden things in case concurrent access is allowed to a root tree passed via CHASE_AT_RESOLVE_IN_ROOT: let's not just validate via the path if we hit the root of the tree, but also by comparing inodes + mount ids. Hardening opportunity reported by Sebastian Wick.
This commit is contained in:
@@ -147,10 +147,10 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
|
||||
_cleanup_free_ char *buffer = NULL, *done = NULL;
|
||||
_cleanup_close_ int fd = -EBADF, root_fd = -EBADF;
|
||||
bool exists = true, append_trail_slash = false;
|
||||
struct statx stx; /* statx obtained from fd */
|
||||
struct statx root_stx, stx;
|
||||
bool need_absolute = false; /* allocate early to avoid compiler warnings around goto */
|
||||
const char *todo;
|
||||
unsigned mask = STATX_TYPE|STATX_UID|STATX_INO;
|
||||
unsigned mask = STATX_TYPE|STATX_UID|STATX_INO|STATX_MNT_ID;
|
||||
int r;
|
||||
|
||||
assert(!FLAGS_SET(flags, CHASE_PREFIX_ROOT));
|
||||
@@ -244,7 +244,6 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0) {
|
||||
|
||||
/* Shortcut the common case where no root dir is specified, and no special flags are given to
|
||||
* a regular open() */
|
||||
if (!ret_path &&
|
||||
@@ -329,6 +328,7 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
|
||||
r = xstatx(fd, /* path= */ NULL, /* flags= */ 0, mask, &stx);
|
||||
if (r < 0)
|
||||
return r;
|
||||
root_stx = stx; /* remember stat data of the root, so that we can recognize it later */
|
||||
|
||||
/* If we get AT_FDCWD, we always resolve symlinks relative to the host's root. Only if a positive
|
||||
* directory file descriptor is provided we will look at CHASE_AT_RESOLVE_IN_ROOT to determine
|
||||
@@ -383,8 +383,13 @@ int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int
|
||||
struct statx stx_parent;
|
||||
|
||||
/* If we already are at the top, then going up will not change anything. This is
|
||||
* in-line with how the kernel handles this. */
|
||||
if (empty_or_root(done) && FLAGS_SET(flags, CHASE_AT_RESOLVE_IN_ROOT)) {
|
||||
* in-line with how the kernel handles this. We check this both by path and by
|
||||
* inode/mount identity check. The latter is load-bearing if concurrent access of the
|
||||
* root tree we operate in is allowed, where an inode is moved up the tree while we
|
||||
* look at it, and thus get the current path wrong and think we are deeper down than
|
||||
* we actually are. */
|
||||
if (FLAGS_SET(flags, CHASE_AT_RESOLVE_IN_ROOT) &&
|
||||
(empty_or_root(done) || (statx_inode_same(&stx, &root_stx) && statx_mount_same(&stx, &root_stx)))) {
|
||||
if (FLAGS_SET(flags, CHASE_STEP))
|
||||
goto chased_one;
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user