Expose binder security context to rust binder services
This patch exposes AIBinder_setRequestingSid and AIBinder_getCallingSid
to binder services written in Rust. This is required by services to
effectively enforce SEPolicy because relying only on the caller's PID is
racy.
Bug: 165070170
Test: atest rustBinderTest
Change-Id: Iae25d4fedded7d133354ba4c82527f33e5d5e1ce
diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs
index fe59416..953d328 100644
--- a/libs/binder/rust/tests/integration.rs
+++ b/libs/binder/rust/tests/integration.rs
@@ -18,7 +18,8 @@
use binder::declare_binder_interface;
use binder::parcel::Parcel;
-use binder::{Binder, IBinder, Interface, SpIBinder, TransactionCode};
+use binder::{Binder, IBinder, Interface, SpIBinder, StatusCode, ThreadState, TransactionCode};
+use std::convert::{TryFrom, TryInto};
/// Name of service runner.
///
@@ -49,6 +50,7 @@
let mut service = Binder::new(BnTest(Box::new(TestService {
s: service_name.clone(),
})));
+ service.set_requesting_sid(true);
if let Some(extension_name) = extension_name {
let extension = BnTest::new_binder(TestService { s: extension_name });
service
@@ -79,18 +81,47 @@
s: String,
}
+#[repr(u32)]
+enum TestTransactionCode {
+ Test = SpIBinder::FIRST_CALL_TRANSACTION,
+ GetSelinuxContext,
+}
+
+impl TryFrom<u32> for TestTransactionCode {
+ type Error = StatusCode;
+
+ fn try_from(c: u32) -> Result<Self, Self::Error> {
+ match c {
+ _ if c == TestTransactionCode::Test as u32 => Ok(TestTransactionCode::Test),
+ _ if c == TestTransactionCode::GetSelinuxContext as u32 => {
+ Ok(TestTransactionCode::GetSelinuxContext)
+ }
+ _ => Err(StatusCode::UNKNOWN_TRANSACTION),
+ }
+ }
+}
+
impl Interface for TestService {}
impl ITest for TestService {
fn test(&self) -> binder::Result<String> {
Ok(self.s.clone())
}
+
+ fn get_selinux_context(&self) -> binder::Result<String> {
+ let sid =
+ ThreadState::with_calling_sid(|sid| sid.map(|s| s.to_string_lossy().into_owned()));
+ sid.ok_or(StatusCode::UNEXPECTED_NULL)
+ }
}
/// Trivial testing binder interface
pub trait ITest: Interface {
/// Returns a test string
fn test(&self) -> binder::Result<String>;
+
+ /// Returns the caller's SELinux context
+ fn get_selinux_context(&self) -> binder::Result<String>;
}
declare_binder_interface! {
@@ -104,19 +135,30 @@
fn on_transact(
service: &dyn ITest,
- _code: TransactionCode,
+ code: TransactionCode,
_data: &Parcel,
reply: &mut Parcel,
) -> binder::Result<()> {
- reply.write(&service.test()?)?;
- Ok(())
+ match code.try_into()? {
+ TestTransactionCode::Test => reply.write(&service.test()?),
+ TestTransactionCode::GetSelinuxContext => reply.write(&service.get_selinux_context()?),
+ }
}
impl ITest for BpTest {
fn test(&self) -> binder::Result<String> {
- let reply = self
- .binder
- .transact(SpIBinder::FIRST_CALL_TRANSACTION, 0, |_| Ok(()))?;
+ let reply =
+ self.binder
+ .transact(TestTransactionCode::Test as TransactionCode, 0, |_| Ok(()))?;
+ reply.read()
+ }
+
+ fn get_selinux_context(&self) -> binder::Result<String> {
+ let reply = self.binder.transact(
+ TestTransactionCode::GetSelinuxContext as TransactionCode,
+ 0,
+ |_| Ok(()),
+ )?;
reply.read()
}
}
@@ -125,12 +167,19 @@
fn test(&self) -> binder::Result<String> {
self.0.test()
}
+
+ fn get_selinux_context(&self) -> binder::Result<String> {
+ self.0.get_selinux_context()
+ }
}
#[cfg(test)]
mod tests {
+ use selinux_bindgen as selinux_sys;
+ use std::ffi::CStr;
use std::fs::File;
use std::process::{Child, Command};
+ use std::ptr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread;
@@ -203,6 +252,24 @@
assert_eq!(test_client.test().unwrap(), "trivial_client_test");
}
+ #[test]
+ fn get_selinux_context() {
+ let service_name = "get_selinux_context";
+ let _process = ScopedServiceProcess::new(service_name);
+ let test_client: Box<dyn ITest> =
+ binder::get_interface(service_name).expect("Did not get manager binder service");
+ let expected_context = unsafe {
+ let mut out_ptr = ptr::null_mut();
+ assert_eq!(selinux_sys::getcon(&mut out_ptr), 0);
+ assert!(!out_ptr.is_null());
+ CStr::from_ptr(out_ptr)
+ };
+ assert_eq!(
+ test_client.get_selinux_context().unwrap(),
+ expected_context.to_str().expect("context was invalid UTF-8"),
+ );
+ }
+
fn register_death_notification(binder: &mut SpIBinder) -> (Arc<AtomicBool>, DeathRecipient) {
let binder_died = Arc::new(AtomicBool::new(false));