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/libs/capabilities/Android.bp b/libs/capabilities/Android.bp
new file mode 100644
index 0000000..db3f4d4
--- /dev/null
+++ b/libs/capabilities/Android.bp
@@ -0,0 +1,62 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_bindgen {
+ name: "libcap_bindgen",
+ edition: "2021",
+ wrapper_src: "bindgen/libcap.h",
+ crate_name: "cap_bindgen",
+ source_stem: "bindings",
+ shared_libs: ["libcap"],
+ bindgen_flags: [
+ "--default-enum-style rust",
+ ],
+ visibility: [
+ "//packages/modules/Virtualization:__subpackages__",
+ ],
+}
+
+rust_test {
+ name: "libcap_bindgen_test",
+ srcs: [":libcap_bindgen"],
+ crate_name: "cap_bindgen_test",
+ test_suites: ["general-tests"],
+ auto_gen_config: true,
+ clippy_lints: "none",
+ lints: "none",
+}
+
+rust_defaults {
+ name: "libcap_rust.defaults",
+ crate_name: "cap",
+ srcs: ["src/caps.rs"],
+ rustlibs: [
+ "libanyhow",
+ "libcap_bindgen",
+ "liblibc",
+ "libnix",
+ "libscopeguard",
+ ],
+ edition: "2021",
+ prefer_rlib: true,
+ multilib: {
+ lib32: {
+ enabled: false,
+ },
+ },
+ shared_libs: [
+ "libcap",
+ ],
+}
+
+rust_library {
+ name: "libcap_rust",
+ defaults: ["libcap_rust.defaults"],
+}
+
+rust_test {
+ name: "libcap_rust.test",
+ defaults: ["libcap_rust.defaults"],
+ test_suites: ["general-tests"],
+}
diff --git a/libs/capabilities/bindgen/libcap.h b/libs/capabilities/bindgen/libcap.h
new file mode 100644
index 0000000..cbb90dc
--- /dev/null
+++ b/libs/capabilities/bindgen/libcap.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#include <sys/capability.h>
diff --git a/libs/capabilities/src/caps.rs b/libs/capabilities/src/caps.rs
new file mode 100644
index 0000000..1f44a34
--- /dev/null
+++ b/libs/capabilities/src/caps.rs
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! A rust library wrapping the libcap functionality.
+
+use anyhow::{bail, Result};
+use cap_bindgen::{
+ cap_clear_flag, cap_drop_bound, cap_flag_t, cap_free, cap_get_proc, cap_set_proc, cap_value_t,
+ CAP_LAST_CAP,
+};
+use nix::errno::Errno;
+
+/// Removes inheritable capabilities set for this process.
+/// See: https://man7.org/linux/man-pages/man7/capabilities.7.html
+pub fn drop_inheritable_caps() -> Result<()> {
+ unsafe {
+ // SAFETY: we do not manipulate memory handled by libcap.
+ let caps = cap_get_proc();
+ scopeguard::defer! {
+ cap_free(caps as *mut std::os::raw::c_void);
+ }
+ if cap_clear_flag(caps, cap_flag_t::CAP_INHERITABLE) < 0 {
+ let e = Errno::last();
+ bail!("cap_clear_flag failed: {:?}", e)
+ }
+ if cap_set_proc(caps) < 0 {
+ let e = Errno::last();
+ bail!("cap_set_proc failed: {:?}", e)
+ }
+ }
+ Ok(())
+}
+
+/// Drop bounding set capabitilies for this process.
+/// See: https://man7.org/linux/man-pages/man7/capabilities.7.html
+pub fn drop_bounding_set() -> Result<()> {
+ let mut cap_id: cap_value_t = 0;
+ while cap_id <= CAP_LAST_CAP.try_into().unwrap() {
+ unsafe {
+ // SAFETY: we do not manipulate memory handled by libcap.
+ if cap_drop_bound(cap_id) == -1 {
+ let e = Errno::last();
+ bail!("cap_drop_bound failed for {}: {:?}", cap_id, e);
+ }
+ }
+ cap_id += 1;
+ }
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ // Basic test to verify that calling drop_inheritable_caps doesn't fail
+ #[test]
+ fn test_drop_inheritable_caps() {
+ let result = drop_inheritable_caps();
+ assert!(result.is_ok(), "failed with: {:?}", result)
+ }
+}