Commit 4b99d499 authored by Aleksa Sarai's avatar Aleksa Sarai Committed by Al Viro

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/20170429220414.GT29622@ZenIV.linux.org.uk/
[2]: https://lore.kernel.org/lkml/1415094884-18349-1-git-send-email-drysdale@google.com/
[3]: https://lore.kernel.org/lkml/1404124096-21445-1-git-send-email-drysdale@google.com/

Cc: Christian Brauner <christian.brauner@ubuntu.com>
Suggested-by: default avatarDavid Drysdale <drysdale@google.com>
Suggested-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Suggested-by: default avatarAndy Lutomirski <luto@kernel.org>
Suggested-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarAleksa Sarai <cyphar@cyphar.com>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 27812141
...@@ -867,13 +867,21 @@ static int nd_jump_root(struct nameidata *nd) ...@@ -867,13 +867,21 @@ static int nd_jump_root(struct nameidata *nd)
*/ */
int nd_jump_link(struct path *path) int nd_jump_link(struct path *path)
{ {
int error = -ELOOP;
struct nameidata *nd = current->nameidata; struct nameidata *nd = current->nameidata;
path_put(&nd->path);
if (unlikely(nd->flags & LOOKUP_NO_MAGICLINKS))
goto err;
path_put(&nd->path);
nd->path = *path; nd->path = *path;
nd->inode = nd->path.dentry->d_inode; nd->inode = nd->path.dentry->d_inode;
nd->flags |= LOOKUP_JUMPED; nd->flags |= LOOKUP_JUMPED;
return 0; return 0;
err:
path_put(path);
return error;
} }
static inline void put_link(struct nameidata *nd) static inline void put_link(struct nameidata *nd)
......
...@@ -41,6 +41,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; ...@@ -41,6 +41,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
/* Scoping flags for lookup. */ /* Scoping flags for lookup. */
#define LOOKUP_NO_SYMLINKS 0x010000 /* No symlink crossing. */ #define LOOKUP_NO_SYMLINKS 0x010000 /* No symlink crossing. */
#define LOOKUP_NO_MAGICLINKS 0x020000 /* No nd_jump_link() crossing. */
extern int path_pts(struct path *path); extern int path_pts(struct path *path);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment