minijail: Add support for entering an existing VFS namespace.

Also, fix the Makefile while in there.

BUG=chromium:376987
TEST=security_Minijail0
CQ-DEPEND=CL:209242

Change-Id: I18877211549500cbb720805a2480b1cb3244c1e9
Reviewed-on: https://chromium-review.googlesource.com/209240
Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Tested-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Commit-Queue: Jorge Lucangeli Obes <jorgelo@chromium.org>
diff --git a/Makefile b/Makefile
index 204e64b..d931df0 100644
--- a/Makefile
+++ b/Makefile
@@ -81,5 +81,5 @@
 	@rm -f libminijail.so
 	@rm -f libminijail_unittest
 	@rm -f libsyscalls.gen.o libsyscalls.gen.c
-	@rm -f syscall_filter.o signal.o bpf.o util.o
+	@rm -f syscall_filter.o signal.o bpf.o util.o elfparse.o
 	@rm -f syscall_filter_unittest syscall_filter_unittest.o
diff --git a/libminijail.c b/libminijail.c
index 4e56b3e..9fafc51 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -10,6 +10,7 @@
 #include <asm/unistd.h>
 #include <ctype.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <grp.h>
 #include <inttypes.h>
 #include <limits.h>
@@ -27,6 +28,8 @@
 #include <sys/mount.h>
 #include <sys/param.h>
 #include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <sys/user.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -75,6 +78,7 @@
 		int gid:1;
 		int caps:1;
 		int vfs:1;
+		int enter_vfs:1;
 		int pids:1;
 		int net:1;
 		int seccomp:1;
@@ -93,6 +97,7 @@
 	char *user;
 	uint64_t caps;
 	pid_t initpid;
+	int mountns_fd;
 	int filter_len;
 	int binding_count;
 	char *chrootdir;
@@ -109,6 +114,7 @@
 void minijail_preenter(struct minijail *j)
 {
 	j->flags.vfs = 0;
+	j->flags.enter_vfs = 0;
 	j->flags.readonly = 0;
 	j->flags.pids = 0;
 }
@@ -120,6 +126,7 @@
 void minijail_preexec(struct minijail *j)
 {
 	int vfs = j->flags.vfs;
+	int enter_vfs = j->flags.enter_vfs;
 	int readonly = j->flags.readonly;
 	if (j->user)
 		free(j->user);
@@ -127,6 +134,7 @@
 	memset(&j->flags, 0, sizeof(j->flags));
 	/* Now restore anything we meant to keep. */
 	j->flags.vfs = vfs;
+	j->flags.enter_vfs = enter_vfs;
 	j->flags.readonly = readonly;
 	/* Note, |pids| will already have been used before this call. */
 }
@@ -249,6 +257,16 @@
 	j->flags.vfs = 1;
 }
 
+void API minijail_namespace_enter_vfs(struct minijail *j, const char *ns_path)
+{
+	int ns_fd = open(ns_path, O_RDONLY);
+	if (ns_fd < 0) {
+		pdie("failed to open namespace '%s'", ns_path);
+	}
+	j->mountns_fd = ns_fd;
+	j->flags.enter_vfs = 1;
+}
+
 void API minijail_namespace_pids(struct minijail *j)
 {
 	j->flags.vfs = 1;
@@ -745,7 +763,7 @@
 {
 	if (j->flags.pids)
 		die("tried to enter a pid-namespaced jail;"
-		    "try minijail_run()?");
+		    " try minijail_run()?");
 
 	if (j->flags.usergroups && !j->user)
 		die("usergroup inheritance without username");
@@ -755,6 +773,9 @@
 	 * so we don't even try. If any of our operations fail, we abort() the
 	 * entire process.
 	 */
+	if (j->flags.enter_vfs && setns(j->mountns_fd, CLONE_NEWNS))
+		pdie("setns(CLONE_NEWNS)");
+
 	if (j->flags.vfs && unshare(CLONE_NEWNS))
 		pdie("unshare(vfs)");
 
diff --git a/libminijail.h b/libminijail.h
index 865a4f1..6738a32 100644
--- a/libminijail.h
+++ b/libminijail.h
@@ -49,6 +49,7 @@
 void minijail_log_seccomp_filter_failures(struct minijail *j);
 void minijail_use_caps(struct minijail *j, uint64_t capmask);
 void minijail_namespace_vfs(struct minijail *j);
+void minijail_namespace_enter_vfs(struct minijail *j, const char *ns_path);
 void minijail_namespace_net(struct minijail *j);
 /* Implies namespace_vfs and remount_readonly.
  * WARNING: this is NOT THREAD SAFE. See the block comment in </libminijail.c>.
diff --git a/minijail0.c b/minijail0.c
index e5ca3d1..4d103b7 100644
--- a/minijail0.c
+++ b/minijail0.c
@@ -83,7 +83,7 @@
 	       "instances allowed\n"
 	       "  -c <caps>:  restrict caps to <caps>\n"
 	       "  -C <dir>:   chroot to <dir>\n"
-	       "  -e:         enter a network namespace\n"
+	       "  -e:         enter new network namespace\n"
 	       "  -G:         inherit secondary groups from uid\n"
 	       "  -g <group>: change gid to <group>\n"
 	       "  -h:         help (this message)\n"
@@ -98,14 +98,15 @@
 
 	printf("\n"
 	       "  -n:         set no_new_privs\n"
-	       "  -p:         use pid namespace (implies -vr)\n"
-	       "  -r:         remount /proc readonly (implies -v)\n"
+	       "  -p:         enter new pid namespace (implies -vr)\n"
+	       "  -r:         remount /proc read-only (implies -v)\n"
 	       "  -s:         use seccomp\n"
 	       "  -S <file>:  set seccomp filter using <file>\n"
 	       "              E.g., -S /usr/share/filters/<prog>.$(uname -m)\n"
 	       "  -t:         mount tmpfs at /tmp inside chroot\n"
 	       "  -u <user>:  change uid to <user>\n"
-	       "  -v:         use vfs namespace\n");
+	       "  -v:         enter new mount namespace\n"
+	       "  -V <file>:  enter specified mount namespace\n");
 }
 
 static void seccomp_filter_usage(const char *progn)
@@ -127,7 +128,7 @@
 	int mount_tmp = 0;
 	if (argc > 1 && argv[1][0] != '-')
 		return 1;
-	while ((opt = getopt(argc, argv, "u:g:sS:c:C:b:vrGhHinpLet")) != -1) {
+	while ((opt = getopt(argc, argv, "u:g:sS:c:C:b:V:vrGhHinpLet")) != -1) {
 		switch (opt) {
 		case 'u':
 			set_user(j, optarg);
@@ -155,8 +156,10 @@
 			use_caps(j, optarg);
 			break;
 		case 'C':
-			if (0 != minijail_enter_chroot(j, optarg))
+			if (0 != minijail_enter_chroot(j, optarg)) {
+				fprintf(stderr, "Could not set chroot.\n");
 				exit(1);
+			}
 			chroot = 1;
 			break;
 		case 't':
@@ -166,6 +169,9 @@
 		case 'v':
 			minijail_namespace_vfs(j);
 			break;
+		case 'V':
+			minijail_namespace_enter_vfs(j, optarg);
+			break;
 		case 'r':
 			minijail_remount_readonly(j);
 			break;