Implement initial ambient capabilities support.

Credit to Brian McGillion for the initial implementation (in
https://android-review.googlesource.com/#/c/302756/).

Current support allows callers to also set ambient capabilities when
using regular capabilities. A follow-up CL will clean up the preloading
situation wrt ambient capabilities.

Bug: 32066154
Test: Use 'drop_privs' executable, check that it gets ambient caps.
Change-Id: If493fb5886fe9798436a749b7ebdbc04f00000b6
diff --git a/libminijail.c b/libminijail.c
index dada8fa..cddbffe 100644
--- a/libminijail.c
+++ b/libminijail.c
@@ -100,6 +100,7 @@
 		int keep_suppl_gids : 1;
 		int use_caps : 1;
 		int capbset_drop : 1;
+		int set_ambient_caps : 1;
 		int vfs : 1;
 		int enter_vfs : 1;
 		int skip_remount_private : 1;
@@ -388,6 +389,11 @@
 	j->flags.capbset_drop = 1;
 }
 
+void API minijail_set_ambient_caps(struct minijail *j)
+{
+	j->flags.set_ambient_caps = 1;
+}
+
 void API minijail_reset_signal_mask(struct minijail *j)
 {
 	j->flags.reset_signal_mask = 1;
@@ -1358,17 +1364,15 @@
 
 	cap_t caps = cap_get_proc();
 	cap_value_t flag[1];
+	const size_t ncaps = sizeof(j->caps) * 8;
 	const uint64_t one = 1;
 	unsigned int i;
 	if (!caps)
 		die("can't get process caps");
-	if (cap_clear_flag(caps, CAP_INHERITABLE))
-		die("can't clear inheritable caps");
-	if (cap_clear_flag(caps, CAP_EFFECTIVE))
-		die("can't clear effective caps");
-	if (cap_clear_flag(caps, CAP_PERMITTED))
-		die("can't clear permitted caps");
-	for (i = 0; i < sizeof(j->caps) * 8 && i <= last_valid_cap; ++i) {
+	if (cap_clear(caps))
+		die("can't clear caps");
+
+	for (i = 0; i < ncaps && i <= last_valid_cap; ++i) {
 		/* Keep CAP_SETPCAP for dropping bounding set bits. */
 		if (i != CAP_SETPCAP && !(j->caps & (one << i)))
 			continue;
@@ -1405,6 +1409,31 @@
 	if (cap_set_proc(caps))
 		die("can't apply final cleaned capset");
 
+	/*
+	 * If ambient capabilities are supported, clear all capabilities first,
+	 * then raise the requested ones.
+	 */
+	if (j->flags.set_ambient_caps) {
+		if (!cap_ambient_supported()) {
+			pdie("ambient capabilities not supported");
+		}
+		if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL) != 0) {
+			pdie("can't clear ambient capabilities");
+		}
+
+		for (i = 0; i < ncaps && i <= last_valid_cap; ++i) {
+			if (!(j->caps & (one << i)))
+				continue;
+
+			if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0,
+				  0) != 0) {
+				pdie("prctl(PR_CAP_AMBIENT, "
+				     "PR_CAP_AMBIENT_RAISE, %u) failed",
+				     i);
+			}
+		}
+	}
+
 	cap_free(caps);
 }
 
diff --git a/libminijail.h b/libminijail.h
index 2bf3024..9177ca8 100644
--- a/libminijail.h
+++ b/libminijail.h
@@ -58,6 +58,8 @@
 /* 'minijail_use_caps' and 'minijail_capbset_drop' are mutually exclusive. */
 void minijail_use_caps(struct minijail *j, uint64_t capmask);
 void minijail_capbset_drop(struct minijail *j, uint64_t capmask);
+/* 'minijail_set_ambient_caps' requires 'minijail_use_caps'. */
+void minijail_set_ambient_caps(struct minijail *j);
 void minijail_reset_signal_mask(struct minijail *j);
 void minijail_namespace_vfs(struct minijail *j);
 void minijail_namespace_enter_vfs(struct minijail *j, const char *ns_path);
diff --git a/minijail0.c b/minijail0.c
index 2c622b4..5917b44 100644
--- a/minijail0.c
+++ b/minijail0.c
@@ -116,7 +116,7 @@
 	       "  [-b <src>,<dest>[,<writeable>]] [-k <src>,<dest>,<type>[,<flags>][,<data>]]\n"
 	       "  [-c <caps>] [-C <dir>] [-P <dir>] [-e[file]] [-f <file>] [-g <group>]\n"
 	       "  [-m[<uid> <loweruid> <count>]*] [-M[<gid> <lowergid> <count>]*]\n"
-	       "  [-S <file>] [-T <type>] [-u <user>] [-V <file>]\n"
+	       "  [-S <file>] [-t[size]] [-T <type>] [-u <user>] [-V <file>]\n"
 	       "  <program> [args...]\n"
 	       "  -a <table>: Use alternate syscall table <table>.\n"
 	       "  -b:         Bind <src> to <dest> in chroot.\n"
@@ -177,7 +177,8 @@
 	       "  -v:         Enter new mount namespace.\n"
 	       "  -V <file>:  Enter specified mount namespace.\n"
 	       "  -w:         Create and join a new anonymous session keyring.\n"
-	       "  -Y:         Synchronize seccomp filters across thread group.\n");
+	       "  -Y:         Synchronize seccomp filters across thread group.\n"
+	       "  --ambient:  Raise ambient capabilities. Requires -c.\n");
 	/* clang-format on */
 }
 
@@ -201,6 +202,7 @@
 	int chroot = 0, pivot_root = 0;
 	int mount_ns = 0, skip_remount = 0;
 	int inherit_suppl_gids = 0, keep_suppl_gids = 0;
+	int caps = 0, ambient_caps = 0;
 	const size_t path_max = 4096;
 	char *map;
 	size_t size;
@@ -211,7 +213,12 @@
 	const char *optstring =
 	    "u:g:sS:c:C:P:b:V:f:m::M::k:a:e::T:vrGhHinNplLt::IUKwyY";
 	int longoption_index = 0;
-	const struct option long_options[] = {{0, 0, 0, 0}};
+	/* clang-format off */
+	const struct option long_options[] = {
+		{"ambient", no_argument, 0, 128},
+		{0, 0, 0, 0},
+	};
+	/* clang-format on */
 
 	while ((opt = getopt_long(argc, argv, optstring, long_options,
 				  &longoption_index)) != -1) {
@@ -253,6 +260,7 @@
 			binding = 1;
 			break;
 		case 'c':
+			caps = 1;
 			use_caps(j, optarg);
 			break;
 		case 'C':
@@ -427,6 +435,11 @@
 		case 'Y':
 			minijail_set_seccomp_filter_tsync(j);
 			break;
+		/* Long options. */
+		case 128: /* Ambient caps. */
+			ambient_caps = 1;
+			minijail_set_ambient_caps(j);
+			break;
 		default:
 			usage(argv[0]);
 			exit(1);
@@ -435,6 +448,13 @@
 			break;
 	}
 
+	/* Can only set ambient caps when using regular caps. */
+	if (ambient_caps && !caps) {
+		fprintf(stderr, "Can't set ambient capabilities (--ambient) "
+				"without actually using capabilities (-c).\n");
+		exit(1);
+	}
+
 	/* Only allow bind mounts when entering a chroot or using pivot_root. */
 	if (binding && !(chroot || pivot_root)) {
 		fprintf(stderr, "Can't add bind mounts without chroot or"
diff --git a/system.c b/system.c
index 05d0c47..49f8915 100644
--- a/system.c
+++ b/system.c
@@ -35,40 +35,33 @@
 #define SECURE_ALL_BITS 0x55
 #define SECURE_ALL_LOCKS (SECURE_ALL_BITS << 1)
 #endif
-/* For kernels < 4.3. */
-#define OLD_SECURE_ALL_BITS 0x15
-#define OLD_SECURE_ALL_LOCKS (OLD_SECURE_ALL_BITS << 1)
+
+#define SECURE_BITS_NO_AMBIENT 0x15
+#define SECURE_LOCKS_NO_AMBIENT (SECURE_BITS_NO_AMBIENT << 1)
 
 /*
  * Assert the value of SECURE_ALL_BITS at compile-time.
- * Brillo devices are currently compiled against 4.4 kernel headers. Kernel 4.3
+ * Android devices are currently compiled against 4.4 kernel headers. Kernel 4.3
  * added a new securebit.
  * When a new securebit is added, the new SECURE_ALL_BITS mask will return EPERM
  * when used on older kernels. The compile-time assert will catch this situation
  * at compile time.
  */
-#ifdef __BRILLO__
+#if defined(__ANDROID__)
 _Static_assert(SECURE_ALL_BITS == 0x55, "SECURE_ALL_BITS == 0x55.");
 #endif
 
 int lock_securebits(void)
 {
 	/*
-	 * Kernels 4.3+ define a new securebit (SECURE_NO_CAP_AMBIENT_RAISE),
-	 * so using the SECURE_ALL_BITS and SECURE_ALL_LOCKS masks from
-	 * newer kernel headers will return EPERM on older kernels. Detect this,
-	 * and retry with the right mask for older (2.6.26-4.2) kernels.
+	 * Ambient capabilities can only be raised if they're already present
+	 * in the permitted *and* inheritable set. Therefore, we don't really
+	 * need to lock the NO_CAP_AMBIENT_RAISE securebit, since we are already
+	 * configuring the permitted and inheritable set.
 	 */
 	int securebits_ret =
-	    prctl(PR_SET_SECUREBITS, SECURE_ALL_BITS | SECURE_ALL_LOCKS);
-	if (securebits_ret < 0) {
-		if (errno == EPERM) {
-			/* Possibly running on kernel < 4.3. */
-			securebits_ret =
-			    prctl(PR_SET_SECUREBITS,
-				  OLD_SECURE_ALL_BITS | OLD_SECURE_ALL_LOCKS);
-		}
-	}
+	    prctl(PR_SET_SECUREBITS,
+		  SECURE_BITS_NO_AMBIENT | SECURE_LOCKS_NO_AMBIENT);
 	if (securebits_ret < 0) {
 		pwarn("prctl(PR_SET_SECUREBITS) failed");
 		return -1;
@@ -144,6 +137,12 @@
 	return last_valid_cap;
 }
 
+int cap_ambient_supported(void)
+{
+	return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >=
+	       0;
+}
+
 int config_net_loopback(void)
 {
 	const char ifname[] = "lo";
diff --git a/system.h b/system.h
index 9ef2b29..6f1be49 100644
--- a/system.h
+++ b/system.h
@@ -19,15 +19,39 @@
 #ifndef _SYSTEM_H_
 #define _SYSTEM_H_
 
+#include <sys/capability.h>
+#include <sys/prctl.h>
 #include <sys/types.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+/* Control the ambient capability set. */
+#ifndef PR_CAP_AMBIENT
+#define PR_CAP_AMBIENT 47
+#endif
+
+#ifndef PR_CAP_AMBIENT_IS_SET
+#define PR_CAP_AMBIENT_IS_SET 1
+#endif
+
+#ifndef PR_CAP_AMBIENT_RAISE
+#define PR_CAP_AMBIENT_RAISE 2
+#endif
+
+#ifndef PR_CAP_AMBIENT_LOWER
+#define PR_CAP_AMBIENT_LOWER 3
+#endif
+
+#ifndef PR_CAP_AMBIENT_CLEAR_ALL
+#define PR_CAP_AMBIENT_CLEAR_ALL 4
+#endif
+
 int lock_securebits(void);
 
 unsigned int get_last_valid_cap(void);
+int cap_ambient_supported(void);
 
 int config_net_loopback(void);