• Aleksa Sarai's avatar
    namei: LOOKUP_IN_ROOT: chroot-like scoped resolution · 8db52c7e
    Aleksa Sarai authored
    /* Background. */
    Container runtimes or other administrative management processes will
    often interact with root filesystems while in the host mount namespace,
    because the cost of doing a chroot(2) on every operation is too
    prohibitive (especially in Go, which cannot safely use vfork). However,
    a malicious program can trick the management process into doing
    operations on files outside of the root filesystem through careful
    crafting of symlinks.
    
    Most programs that need this feature have attempted to make this process
    safe, by doing all of the path resolution in userspace (with symlinks
    being scoped to the root of the malicious root filesystem).
    Unfortunately, this method is prone to foot-guns and usually such
    implementations have subtle security bugs.
    
    Thus, what userspace needs is a way to resolve a path as though it were
    in a chroot(2) -- with all absolute symlinks being resolved relative to
    the dirfd root (and ".." components being stuck under the dirfd root).
    It is much simpler and more straight-forward to provide this
    functionality in-kernel (because it can be done far more cheaply and
    correctly).
    
    More classical applications that also have this problem (which have
    their own potentially buggy userspace path sanitisation code) include
    web servers, archive extraction tools, network file servers, and so on.
    
    /* Userspace API. */
    LOOKUP_IN_ROOT will be exposed to userspace through openat2(2).
    
    /* Semantics. */
    Unlike most other LOOKUP flags (most notably LOOKUP_FOLLOW),
    LOOKUP_IN_ROOT applies to all components of the path.
    
    With LOOKUP_IN_ROOT, any path component which attempts to cross the
    starting point of the pathname lookup (the dirfd passed to openat) will
    remain at the starting point. Thus, all absolute paths and symlinks will
    be scoped within the starting point.
    
    There is a slight change in behaviour regarding pathnames -- if the
    pathname is absolute then the dirfd is still used as the root of
    resolution of LOOKUP_IN_ROOT is specified (this is to avoid obvious
    foot-guns, at the cost of a minor API inconsistency).
    
    As with LOOKUP_BENEATH, Jann's security concern about ".."[1] applies to
    LOOKUP_IN_ROOT -- therefore ".." resolution is blocked. This restriction
    will be lifted in a future patch, but requires more work to ensure that
    permitting ".." is done safely.
    
    Magic-link jumps are also blocked, because they can beam the path lookup
    across the starting point. It would be possible to detect and block
    only the "bad" crossings with path_is_under() checks, but it's unclear
    whether it makes sense to permit magic-links at all. However, userspace
    is recommended to pass LOOKUP_NO_MAGICLINKS if they want to ensure that
    magic-link crossing is entirely disabled.
    
    /* Testing. */
    LOOKUP_IN_ROOT is tested as part of the openat2(2) selftests.
    
    [1]: https://lore.kernel.org/lkml/CAG48ez1jzNvxB+bfOBnERFGp=oMM0vHWuLD6EULmne3R6xa53w@mail.gmail.com/
    
    Cc: Christian Brauner <christian.brauner@ubuntu.com>
    Signed-off-by: default avatarAleksa Sarai <cyphar@cyphar.com>
    Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
    8db52c7e
namei.c 127 KB