Merge "adb: Correctly drop caps when ambient capabilities are used" am: 44f78bed19
am: 00b2dffd38

Change-Id: I452f9f2620a0fbd4e2e8d08e90330fb275e2a161
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 527a264..232d9c5 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -24,6 +24,7 @@
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/capability.h>
 #include <sys/prctl.h>
 
 #include <memory>
@@ -50,13 +51,13 @@
 
 static const char* root_seclabel = nullptr;
 
-static void drop_capabilities_bounding_set_if_needed(struct minijail *j) {
+static bool should_drop_capabilities_bounding_set() {
 #if defined(ALLOW_ADBD_ROOT)
     if (__android_log_is_debuggable()) {
-        return;
+        return false;
     }
 #endif
-    minijail_capbset_drop(j, CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID));
+    return true;
 }
 
 static bool should_drop_privileges() {
@@ -117,13 +118,37 @@
     // Don't listen on a port (default 5037) if running in secure mode.
     // Don't run as root if running in secure mode.
     if (should_drop_privileges()) {
-        drop_capabilities_bounding_set_if_needed(jail.get());
+        const bool should_drop_caps = should_drop_capabilities_bounding_set();
+
+        if (should_drop_caps) {
+            minijail_use_caps(jail.get(), CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID));
+        }
 
         minijail_change_gid(jail.get(), AID_SHELL);
         minijail_change_uid(jail.get(), AID_SHELL);
         // minijail_enter() will abort if any priv-dropping step fails.
         minijail_enter(jail.get());
 
+        // Whenever ambient capabilities are being used, minijail cannot
+        // simultaneously drop the bounding capability set to just
+        // CAP_SETUID|CAP_SETGID while clearing the inheritable, effective,
+        // and permitted sets. So we need to do that in two steps.
+        using ScopedCaps =
+            std::unique_ptr<std::remove_pointer<cap_t>::type, std::function<void(cap_t)>>;
+        ScopedCaps caps(cap_get_proc(), &cap_free);
+        if (cap_clear_flag(caps.get(), CAP_INHERITABLE) == -1) {
+            PLOG(FATAL) << "cap_clear_flag(INHERITABLE) failed";
+        }
+        if (cap_clear_flag(caps.get(), CAP_EFFECTIVE) == -1) {
+            PLOG(FATAL) << "cap_clear_flag(PEMITTED) failed";
+        }
+        if (cap_clear_flag(caps.get(), CAP_PERMITTED) == -1) {
+            PLOG(FATAL) << "cap_clear_flag(PEMITTED) failed";
+        }
+        if (cap_set_proc(caps.get()) != 0) {
+            PLOG(FATAL) << "cap_set_proc() failed";
+        }
+
         D("Local port disabled");
     } else {
         // minijail_enter() will abort if any priv-dropping step fails.