Skip to content

Commit 4b99d49

Browse files
cypharAl Viro
authored andcommitted
namei: LOOKUP_NO_MAGICLINKS: block magic-link resolution
/* Background. */ There has always been a special class of symlink-like objects in procfs (and a few other pseudo-filesystems) which allow for non-lexical resolution of paths using nd_jump_link(). These "magic-links" do not follow traditional mount namespace boundaries, and have been used consistently in container escape attacks because they can be used to trick unsuspecting privileged processes into resolving unexpected paths. It is also non-trivial for userspace to unambiguously avoid resolving magic-links, because they do not have a reliable indication that they are a magic-link (in order to verify them you'd have to manually open the path given by readlink(2) and then verify that the two file descriptors reference the same underlying file, which is plagued with possible race conditions or supplementary attack scenarios). It would therefore be very helpful for userspace to be able to avoid these symlinks easily, thus hopefully removing a tool from attackers' toolboxes. This is part of a refresh of Al's AT_NO_JUMPS patchset[1] (which was a variation on David Drysdale's O_BENEATH patchset[2], which in turn was based on the Capsicum project[3]). /* Userspace API. */ LOOKUP_NO_MAGICLINKS will be exposed to userspace through openat2(2). /* Semantics. */ Unlike most other LOOKUP flags (most notably LOOKUP_FOLLOW), LOOKUP_NO_MAGICLINKS applies to all components of the path. With LOOKUP_NO_MAGICLINKS, any magic-link path component encountered during path resolution will yield -ELOOP. The handling of ~LOOKUP_FOLLOW for a trailing magic-link is identical to LOOKUP_NO_SYMLINKS. LOOKUP_NO_SYMLINKS implies LOOKUP_NO_MAGICLINKS. /* Testing. */ LOOKUP_NO_MAGICLINKS is tested as part of the openat2(2) selftests. [1]: https://lore.kernel.org/lkml/[email protected]/ [2]: https://lore.kernel.org/lkml/[email protected]/ [3]: https://lore.kernel.org/lkml/[email protected]/ Cc: Christian Brauner <[email protected]> Suggested-by: David Drysdale <[email protected]> Suggested-by: Al Viro <[email protected]> Suggested-by: Andy Lutomirski <[email protected]> Suggested-by: Linus Torvalds <[email protected]> Signed-off-by: Aleksa Sarai <[email protected]> Signed-off-by: Al Viro <[email protected]>
1 parent 2781214 commit 4b99d49

File tree

2 files changed

+10
-1
lines changed

2 files changed

+10
-1
lines changed

fs/namei.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -867,13 +867,21 @@ static int nd_jump_root(struct nameidata *nd)
867867
*/
868868
int nd_jump_link(struct path *path)
869869
{
870+
int error = -ELOOP;
870871
struct nameidata *nd = current->nameidata;
871-
path_put(&nd->path);
872872

873+
if (unlikely(nd->flags & LOOKUP_NO_MAGICLINKS))
874+
goto err;
875+
876+
path_put(&nd->path);
873877
nd->path = *path;
874878
nd->inode = nd->path.dentry->d_inode;
875879
nd->flags |= LOOKUP_JUMPED;
876880
return 0;
881+
882+
err:
883+
path_put(path);
884+
return error;
877885
}
878886

879887
static inline void put_link(struct nameidata *nd)

include/linux/namei.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
4141

4242
/* Scoping flags for lookup. */
4343
#define LOOKUP_NO_SYMLINKS 0x010000 /* No symlink crossing. */
44+
#define LOOKUP_NO_MAGICLINKS 0x020000 /* No nd_jump_link() crossing. */
4445

4546
extern int path_pts(struct path *path);
4647

0 commit comments

Comments
 (0)