Skip to content

Commit 5451b8d

Browse files
hvdijkedolstra
authored andcommitted
Use pivot_root in addition to chroot when possible
chroot only changes the process root directory, not the mount namespace root directory, and it is well-known that any process with chroot capability can break out of a chroot "jail". By using pivot_root as well, and unmounting the original mount namespace root directory, breaking out becomes impossible. Non-root processes typically have no ability to use chroot() anyway, but they can gain that capability through the use of clone() or unshare(). For security reasons, these syscalls are limited in functionality when used inside a normal chroot environment. Using pivot_root() this way does allow those syscalls to be put to their full use.
1 parent b0bad3e commit 5451b8d

File tree

2 files changed

+29
-7
lines changed

2 files changed

+29
-7
lines changed

configure.ac

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ AC_CHECK_HEADERS([sys/mount.h], [], [],
8787
# include <sys/param.h>
8888
# endif
8989
])
90+
AC_CHECK_HEADERS([sys/syscall.h])
9091

9192

9293
# Check for lutimes, optionally used for changing the mtime of

src/libstore/build.cc

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
#if HAVE_SYS_MOUNT_H
3939
#include <sys/mount.h>
4040
#endif
41+
#if HAVE_SYS_SYSCALL_H
42+
#include <sys/syscall.h>
43+
#endif
4144
#if HAVE_SCHED_H
4245
#include <sched.h>
4346
#endif
@@ -48,7 +51,7 @@
4851
#include <linux/fs.h>
4952
#endif
5053

51-
#define CHROOT_ENABLED HAVE_CHROOT && HAVE_UNSHARE && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS)
54+
#define CHROOT_ENABLED HAVE_CHROOT && HAVE_UNSHARE && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS) && defined(SYS_pivot_root)
5255

5356
/* chroot-like behavior from Apple's sandbox */
5457
#if __APPLE__
@@ -2059,6 +2062,11 @@ void DerivationGoal::runChild()
20592062
throw SysError(format("unable to make filesystem ‘%1%’ private") % fs);
20602063
}
20612064

2065+
/* Bind-mount chroot directory to itself, to treat it as a
2066+
different filesystem from /, as needed for pivot_root. */
2067+
if (mount(chrootRootDir.c_str(), chrootRootDir.c_str(), 0, MS_BIND, 0) == -1)
2068+
throw SysError(format("unable to bind mount ‘%1%’") % chrootRootDir);
2069+
20622070
/* Set up a nearly empty /dev, unless the user asked to
20632071
bind-mount the host /dev. */
20642072
if (dirsInChroot.find("/dev") == dirsInChroot.end()) {
@@ -2130,13 +2138,26 @@ void DerivationGoal::runChild()
21302138
chmod_(chrootRootDir + "/dev/pts/ptmx", 0666);
21312139
}
21322140

2133-
/* Do the chroot(). Below we do a chdir() to the
2134-
temporary build directory to make sure the current
2135-
directory is in the chroot. (Actually the order
2136-
doesn't matter, since due to the bind mount tmpDir and
2137-
tmpRootDit/tmpDir are the same directories.) */
2138-
if (chroot(chrootRootDir.c_str()) == -1)
2141+
/* Do the chroot(). */
2142+
if (chdir(chrootRootDir.c_str()) == -1)
2143+
throw SysError(format("cannot change directory to ‘%1%’") % chrootRootDir);
2144+
2145+
if (mkdir("real-root", 0) == -1)
2146+
throw SysError("cannot create real-root directory");
2147+
2148+
#define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old))
2149+
if (pivot_root(".", "real-root") == -1)
2150+
throw SysError(format("cannot pivot old root directory onto ‘%1%’") % (chrootRootDir + "/real-root"));
2151+
#undef pivot_root
2152+
2153+
if (chroot(".") == -1)
21392154
throw SysError(format("cannot change root directory to ‘%1%’") % chrootRootDir);
2155+
2156+
if (umount2("real-root", MNT_DETACH) == -1)
2157+
throw SysError("cannot unmount real root filesystem");
2158+
2159+
if (rmdir("real-root") == -1)
2160+
throw SysError("cannot remove real-root directory");
21402161
}
21412162
#endif
21422163

0 commit comments

Comments
 (0)