minijail: Support pivot_root
Add an option that allows user to use pivot_root(2) when one want to
jail process in a chrooted environment. This implies entering a new
mount namespace since pivot_root(2) will really move the root
filesystem.
BUG=chromium:517844
TEST=security_Minijail0 passes
Change-Id: Ie990670703b00e333fa4abc3804d6384d36fa7c9
Reviewed-on: https://chromium-review.googlesource.com/293128
Commit-Ready: Yu-hsi Chiang <yuhsi@google.com>
Tested-by: Yu-hsi Chiang <yuhsi@google.com>
Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
diff --git a/libminijail.c b/libminijail.c
index 5378d84..8e05094 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -95,6 +95,7 @@
int seccomp_filter:1;
int log_seccomp_filter:1;
int chroot:1;
+ int pivot_root:1;
int mount_tmp:1;
int do_init:1;
int pid_file:1;
@@ -362,6 +363,17 @@
return 0;
}
+int API minijail_enter_pivot_root(struct minijail *j, const char *dir)
+{
+ if (j->chrootdir)
+ return -EINVAL;
+ j->chrootdir = strdup(dir);
+ if (!j->chrootdir)
+ return -ENOMEM;
+ j->flags.pivot_root = 1;
+ return 0;
+}
+
void API minijail_mount_tmp(struct minijail *j)
{
j->flags.mount_tmp = 1;
@@ -730,6 +742,36 @@
return 0;
}
+int enter_pivot_root(const struct minijail *j)
+{
+ int ret;
+ if (j->bindings_head && (ret = bind_one(j, j->bindings_head)))
+ return ret;
+
+ /* To ensure chrootdir is the root of a file system, do a self bind mount. */
+ if (mount(j->chrootdir, j->chrootdir, "bind", MS_BIND | MS_REC, ""))
+ pdie("failed to bind mount '%s'", j->chrootdir);
+ if (chdir(j->chrootdir))
+ return -errno;
+ if (mkdir(".minijail_pivot", 0755))
+ pdie("mkdir(.minijail_pivot)");
+ if (syscall(SYS_pivot_root, ".", ".minijail_pivot")) {
+ remove(".minijail_pivot");
+ pdie("pivot_root");
+ }
+ /* The old root might be busy, so use lazy unmount. */
+ if (umount2(".minijail_pivot", MNT_DETACH))
+ pdie("umount(.minijail_pivot");
+ if (chdir("/"))
+ return -errno;
+ if (chroot("/"))
+ return -errno;
+ if (remove(".minijail_pivot"))
+ return -errno;
+
+ return 0;
+}
+
int mount_tmp(void)
{
return mount("none", "/tmp", "tmpfs", 0, "size=64M,mode=777");
@@ -927,6 +969,9 @@
if (j->flags.chroot && enter_chroot(j))
pdie("chroot");
+ if (j->flags.pivot_root && enter_pivot_root(j))
+ pdie("pivot_root");
+
if (j->flags.mount_tmp && mount_tmp())
pdie("mount_tmp");
diff --git a/libminijail.h b/libminijail.h
index 89abf6a..d3d1d37 100644
--- a/libminijail.h
+++ b/libminijail.h
@@ -75,6 +75,7 @@
* Returns 0 on success.
*/
int minijail_enter_chroot(struct minijail *j, const char *dir);
+int minijail_enter_pivot_root(struct minijail *j, const char *dir);
/* minijail_mount_tmp: enables mounting of a tmpfs filesystem on /tmp.
* As be rules of bind mounts, /tmp must exist in chroot.
diff --git a/minijail0.c b/minijail0.c
index d199fab..ce8e058 100644
--- a/minijail0.c
+++ b/minijail0.c
@@ -85,6 +85,7 @@
"instances allowed\n"
" -c <caps>: restrict caps to <caps>\n"
" -C <dir>: chroot to <dir>\n"
+ " Not compatible with -P\n"
" -e: enter new network namespace\n"
" -f <file>: write the pid of the jailed process to <file>\n"
" -G: inherit secondary groups from uid\n"
@@ -109,6 +110,8 @@
" Not compatible with -b without writable\n"
" -n: set no_new_privs\n"
" -p: enter new pid namespace (implies -vr)\n"
+ " -P <dir>: pivot_root to <dir> (implies -v)\n"
+ " Not compatible with -C\n"
" -r: remount /proc read-only (implies -v)\n"
" -s: use seccomp\n"
" -S <file>: set seccomp filter using <file>\n"
@@ -136,11 +139,12 @@
{
int opt;
int use_seccomp_filter = 0;
+ int pivot_root = 0, chroot = 0;
const size_t path_max = 4096;
const char *filter_path;
if (argc > 1 && argv[1][0] != '-')
return 1;
- while ((opt = getopt(argc, argv, "u:g:sS:c:C:b:V:f:m:M:vrGhHinpLetIU")) != -1) {
+ while ((opt = getopt(argc, argv, "u:g:sS:c:C:P:b:V:f:m:M:vrGhHinpLetIU")) != -1) {
switch (opt) {
case 'u':
set_user(j, optarg);
@@ -179,10 +183,29 @@
use_caps(j, optarg);
break;
case 'C':
+ if (pivot_root) {
+ fprintf(stderr, "Could not set chroot because "
+ "'-P' was specified.\n");
+ exit(1);
+ }
if (0 != minijail_enter_chroot(j, optarg)) {
fprintf(stderr, "Could not set chroot.\n");
exit(1);
}
+ chroot = 1;
+ break;
+ case 'P':
+ if (chroot) {
+ fprintf(stderr, "Could not set pivot_root because "
+ "'-C' was specified.\n");
+ exit(1);
+ }
+ if (0 != minijail_enter_pivot_root(j, optarg)) {
+ fprintf(stderr, "Could not set pivot_root.\n");
+ exit(1);
+ }
+ minijail_namespace_vfs(j);
+ pivot_root = 1;
break;
case 'f':
if (0 != minijail_write_pid_file(j, optarg)) {