Drop inheritable caps and caps bounding set before executing payload
This change basically does the following things:
* Add rust_bindgen for the libcap.
* Add libcap_rust wrapping the bindgen and providing
drop_inhertiable_caps and drop_bounding_set APIs;
* Call the libcap_rust APIs before execve'ing into the payload binary.
This is done using the CommandExt::pre_exec function.
Additionally this change adds basic tests for libcap_rust library and
the e2e test to verify that binary running payload have zero
capabilities.
Bug: 243633980
Test: atest libcap_rust.test
Test: atest MicrodroidTestApp
Test: adb shell /apex/com.android.virt/bin/vm run-microdroid
Test: enter microdroid shell & check microdroid_launcher has empty caps
Change-Id: Ibfb45ec912df0ad0a1db62b24c22fbe5a61ff5f3
diff --git a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
index a4ecc45..c936e1b 100644
--- a/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
+++ b/tests/aidl/com/android/microdroid/testservice/ITestService.aidl
@@ -46,4 +46,7 @@
* each line reverse.
*/
void runEchoReverseServer();
+
+ /** Returns a mask of effective capabilities that the process running the payload binary has. */
+ String[] getEffectiveCapabilities();
}
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index edb4759..e3c9961 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -40,6 +40,7 @@
header_libs: ["vm_payload_restricted_headers"],
shared_libs: [
"libbinder_ndk",
+ "libcap",
"MicrodroidTestNativeLibSub",
"libvm_payload#current",
],
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index eb756d8..f3bbbf1 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -1117,6 +1117,25 @@
assertThat(testResults.mEncryptedStoragePath).isEqualTo("/mnt/encryptedstore");
}
+ @Test
+ @CddTest(requirements = {"9.17/C-1-1", "9.17/C-2-1"})
+ public void microdroidLauncherHasEmptyCapabilities() throws Exception {
+ assumeSupportedKernel();
+
+ final VirtualMachineConfig vmConfig =
+ newVmConfigBuilder()
+ .setPayloadBinaryPath("MicrodroidTestNativeLib.so")
+ .setMemoryMib(minMemoryRequired())
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .build();
+ final VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_caps", vmConfig);
+
+ final TestResults testResults = runVmTestService(vm);
+
+ assertThat(testResults.mException).isNull();
+ assertThat(testResults.mEffectiveCapabilities).isEmpty();
+ }
+
private void assertFileContentsAreEqualInTwoVms(String fileName, String vmName1, String vmName2)
throws IOException {
File file1 = getVmFile(vmName1, fileName);
@@ -1171,6 +1190,7 @@
String mExtraApkTestProp;
String mApkContentsPath;
String mEncryptedStoragePath;
+ String[] mEffectiveCapabilities;
}
private TestResults runVmTestService(VirtualMachine vm) throws Exception {
@@ -1194,6 +1214,8 @@
testResults.mApkContentsPath = testService.getApkContentsPath();
testResults.mEncryptedStoragePath =
testService.getEncryptedStoragePath();
+ testResults.mEffectiveCapabilities =
+ testService.getEffectiveCapabilities();
} catch (Exception e) {
testResults.mException = e;
}
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index b6a7aa2..da408e4 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -18,12 +18,14 @@
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/result.h>
+#include <android-base/scopeguard.h>
#include <android/log.h>
#include <fcntl.h>
#include <fsverity_digests.pb.h>
#include <linux/vm_sockets.h>
#include <stdint.h>
#include <stdio.h>
+#include <sys/capability.h>
#include <sys/system_properties.h>
#include <unistd.h>
#include <vm_main.h>
@@ -35,6 +37,7 @@
using android::base::borrowed_fd;
using android::base::ErrnoError;
using android::base::Error;
+using android::base::make_scope_guard;
using android::base::Result;
using android::base::unique_fd;
@@ -197,6 +200,29 @@
return ScopedAStatus::ok();
}
+ ScopedAStatus getEffectiveCapabilities(std::vector<std::string>* out) override {
+ if (out == nullptr) {
+ return ScopedAStatus::ok();
+ }
+ cap_t cap = cap_get_proc();
+ auto guard = make_scope_guard([&cap]() { cap_free(cap); });
+ for (cap_value_t cap_id = 0; cap_id < CAP_LAST_CAP + 1; cap_id++) {
+ cap_flag_value_t value;
+ if (cap_get_flag(cap, cap_id, CAP_EFFECTIVE, &value) != 0) {
+ return ScopedAStatus::
+ fromServiceSpecificErrorWithMessage(0, "cap_get_flag failed");
+ }
+ if (value == CAP_SET) {
+ // Ideally we would just send back the cap_ids, but I wasn't able to find java
+ // APIs for linux capabilities, hence we transform to the human readable name
+ // here.
+ char* name = cap_to_name(cap_id);
+ out->push_back(std::string(name) + "(" + std::to_string(cap_id) + ")");
+ }
+ }
+ return ScopedAStatus::ok();
+ }
+
virtual ::ScopedAStatus runEchoReverseServer() override {
auto result = start_echo_reverse_server();
if (result.ok()) {