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);
 }