Add API for PR_SET_NO_NEW_PRIVS and set seccomp filter before dropping root.

BUG=chromium-os:27878
TEST=minijail_unittest, syscall_filter_unittest
TEST=security_Minijail0
TEST=security_Minijail_seccomp

Change-Id: I78495fda8c14ca5b4f398806eb564b0756876735
Reviewed-on: https://gerrit.chromium.org/gerrit/21545
Tested-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Reviewed-by: Will Drewry <wad@chromium.org>
Reviewed-by: Kees Cook <keescook@chromium.org>
Commit-Ready: Jorge Lucangeli Obes <jorgelo@chromium.org>
diff --git a/libminijail.c b/libminijail.c
index 78cfbc1..2b66042 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -6,6 +6,7 @@
 
 #define _BSD_SOURCE
 #define _GNU_SOURCE
+
 #include <asm/unistd.h>
 #include <ctype.h>
 #include <errno.h>
@@ -40,7 +41,7 @@
 
 /* Until these are reliably available in linux/prctl.h */
 #ifndef PR_SET_SECCOMP
-# define PR_SET_SECCOMP	22
+# define PR_SET_SECCOMP 22
 #endif
 
 /* For seccomp_filter using BPF. */
@@ -80,6 +81,7 @@
 		int readonly:1;
 		int usergroups:1;
 		int ptrace:1;
+		int no_new_privs:1;
 		int seccomp_filter:1;
 		int chroot:1;
 	} flags;
@@ -185,6 +187,11 @@
 	j->flags.seccomp = 1;
 }
 
+void API minijail_no_new_privs(struct minijail *j)
+{
+	j->flags.no_new_privs = 1;
+}
+
 void API minijail_use_seccomp_filter(struct minijail *j)
 {
 	/* TODO(jorgelo): re-enable this when the seccomp BPF merge is done. */
@@ -641,6 +648,26 @@
 			pdie("prctl(PR_SET_SECUREBITS)");
 	}
 
+	/*
+	 * Set no_new_privs before installing seccomp filter.
+	 * TODO(jorgelo): document call to PR_SET_NO_NEW_PRIVS.
+	 */
+	if (j->flags.no_new_privs) {
+		if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
+			pdie("prctl(PR_SET_NO_NEW_PRIVS)");
+	}
+
+	/*
+	 * Install seccomp filter before dropping root and caps.
+	 * WARNING: this means that filter policies *must* allow
+	 * setgroups()/setresgid()/setresuid() for dropping root and
+	 * capget()/capset()/prctl() for dropping caps.
+	 */
+	if (j->flags.seccomp_filter) {
+		if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, j->filter_prog))
+			pdie("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER)");
+	}
+
 	if (j->flags.usergroups) {
 		if (initgroups(j->user, j->usergid))
 			pdie("initgroups");
@@ -664,16 +691,6 @@
 	 * seccomp has to come last since it cuts off all the other
 	 * privilege-dropping syscalls :)
 	 */
-	if (j->flags.seccomp_filter) {
-		/* TODO(jorgelo): document call to PR_SET_NO_NEW_PRIVS. */
-		if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
-			pdie("prctl(PR_SET_NO_NEW_PRIVS)");
-		}
-		if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, j->filter_prog)) {
-			pdie("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER)");
-		}
-	}
-
 	if (j->flags.seccomp && prctl(PR_SET_SECCOMP, 1))
 		pdie("prctl(PR_SET_SECCOMP)");
 }
@@ -896,8 +913,11 @@
 	int st;
 	if (waitpid(j->initpid, &st, 0) < 0)
 		return -errno;
-	if (!WIFEXITED(st))
+	if (!WIFEXITED(st)) {
+		if (WIFSIGNALED(st))
+			warn("child process received signal %d", WTERMSIG(st));
 		return MINIJAIL_ERR_JAIL;
+	}
 	return WEXITSTATUS(st);
 }
 
diff --git a/libminijail.h b/libminijail.h
index 2f4ed7e..b1c425c 100644
--- a/libminijail.h
+++ b/libminijail.h
@@ -43,13 +43,10 @@
 /* Does not take ownership of |group|. */
 int minijail_change_group(struct minijail *j, const char *group);
 void minijail_use_seccomp(struct minijail *j);
+void minijail_no_new_privs(struct minijail *j);
 void minijail_use_seccomp_filter(struct minijail *j);
 void minijail_force_seccomp_filter(struct minijail *j);
 void minijail_parse_seccomp_filters(struct minijail *j, const char *path);
-int minijail_add_seccomp_filter(struct minijail *j, int nr,
-				const char *filter);
-void minijail_use_seccomp_bpf(struct minijail *j);
-void minijail_parse_seccomp_bpf(struct minijail *j, const char *path);
 void minijail_use_caps(struct minijail *j, uint64_t capmask);
 void minijail_namespace_vfs(struct minijail *j);
 /* Implies namespace_vfs and remount_readonly */
diff --git a/minijail0.c b/minijail0.c
index 524ac7f..2403100 100644
--- a/minijail0.c
+++ b/minijail0.c
@@ -69,9 +69,9 @@
 
 static void usage(const char *progn)
 {
-	printf("Usage: %s [-Ghprsv] [-b <src>,<dest>[,<writeable>]] [-c <caps>] "
-	       "[-C <dir>] [-g <group>] [-S <file>] [-u <user>] <program> "
-	       "[args...]\n"
+	printf("Usage: %s [-Ghnprsv] [-b <src>,<dest>[,<writeable>]] "
+	       "[-c <caps>] [-C <dir>] [-g <group>] [-S <file>] [-u <user>] "
+	       "<program> [args...]\n"
 	       "  -b:         binds <src> to <dest> in chroot. Multiple "
 	       "instances allowed\n"
 	       "  -c <caps>:  restrict caps to <caps>\n"
@@ -80,6 +80,7 @@
 	       "  -g <group>: change gid to <group>\n"
 	       "  -h:         help (this message)\n"
 	       "  -H:         seccomp filter help message\n"
+	       "  -n:         set no_new_privs\n"
 	       "  -p:         use pid namespace (implies -vr)\n"
 	       "  -r:         remount /proc readonly (implies -v)\n"
 	       "  -s:         use seccomp\n"
@@ -108,7 +109,7 @@
 	int opt;
 	int use_seccomp_filter = 0;
 	int dry_run = 1;
-	while ((opt = getopt(argc, argv, "u:g:sS:c:C:b:vrGhHpF")) != -1) {
+	while ((opt = getopt(argc, argv, "u:g:sS:c:C:b:vrGhHnpF")) != -1) {
 		switch (opt) {
 		case 'u':
 			set_user(j, optarg);
@@ -116,6 +117,8 @@
 		case 'g':
 			set_group(j, optarg);
 			break;
+		case 'n':
+			minijail_no_new_privs(j);
 		case 's':
 			minijail_use_seccomp(j);
 			break;