diff options
-rw-r--r-- | libs/binder/ndk/ibinder.cpp | 1 | ||||
-rw-r--r-- | libs/binder/ndk/include_platform/android/binder_context.h | 47 | ||||
-rw-r--r-- | libs/binder/ndk/include_platform/android/binder_ibinder_platform.h | 37 | ||||
-rw-r--r-- | libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp | 1 | ||||
-rw-r--r-- | libs/binder/rust/src/binder.rs | 3 | ||||
-rw-r--r-- | libs/binder/rust/src/proxy.rs | 6 | ||||
-rw-r--r-- | libs/binder/rust/src/state.rs | 32 | ||||
-rw-r--r-- | libs/binder/rust/sys/BinderBindings.h | 1 | ||||
-rw-r--r-- | libs/binder/rust/tests/Android.bp | 4 | ||||
-rw-r--r-- | libs/binder/rust/tests/integration.rs | 81 |
10 files changed, 174 insertions, 39 deletions
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index d287290a8d..6995e77667 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <android/binder_context.h> #include <android/binder_ibinder.h> #include <android/binder_ibinder_platform.h> #include "ibinder_internal.h" diff --git a/libs/binder/ndk/include_platform/android/binder_context.h b/libs/binder/ndk/include_platform/android/binder_context.h new file mode 100644 index 0000000000..a99d5559ee --- /dev/null +++ b/libs/binder/ndk/include_platform/android/binder_context.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2020 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. + */ + +#pragma once + +#include <android/binder_ibinder.h> + +__BEGIN_DECLS + +/** + * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This + * must be called on a local binder server before it is sent out to any othe + * process. If this is a remote binder, it will abort. If the kernel doesn't + * support this feature, you'll always get null from AIBinder_getCallingSid. + * + * \param binder local server binder to request security contexts on + */ +__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) + __INTRODUCED_IN(31); + +/** + * Returns the selinux context of the callee. + * + * In order for this to work, the following conditions must be met: + * - The kernel must be new enough to support this feature. + * - The server must have called AIBinder_setRequestingSid. + * - The callee must be a remote process. + * + * \return security context or null if unavailable. The lifetime of this context + * is the lifetime of the transaction. + */ +__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31); + +__END_DECLS diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h index 5811760d3a..2af65cf1ff 100644 --- a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h +++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h @@ -16,41 +16,14 @@ #pragma once -#include <android/binder_ibinder.h> +// binder_context.h used to be part of this header and is included for backwards +// compatibility. +#include <android/binder_context.h> #if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__) -#include <binder/IBinder.h> -#endif -__BEGIN_DECLS - -/** - * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This - * must be called on a local binder server before it is sent out to any othe - * process. If this is a remote binder, it will abort. If the kernel doesn't - * support this feature, you'll always get null from AIBinder_getCallingSid. - * - * \param binder local server binder to request security contexts on - */ -__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) - __INTRODUCED_IN(31); - -/** - * Returns the selinux context of the callee. - * - * In order for this to work, the following conditions must be met: - * - The kernel must be new enough to support this feature. - * - The server must have called AIBinder_setRequestingSid. - * - The callee must be a remote process. - * - * \return security context or null if unavailable. The lifetime of this context - * is the lifetime of the transaction. - */ -__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31); - -__END_DECLS - -#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__) +#include <android/binder_ibinder.h> +#include <binder/IBinder.h> /** * Get libbinder version of binder from AIBinder. diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index 6281014b3e..44d8ebf048 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -18,6 +18,7 @@ #include <aidl/BnBinderNdkUnitTest.h> #include <aidl/BnEmpty.h> #include <android-base/logging.h> +#include <android/binder_context.h> #include <android/binder_ibinder_jni.h> #include <android/binder_ibinder_platform.h> #include <android/binder_manager.h> diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index 6bf9cd5c4c..ceb6aded67 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -98,6 +98,9 @@ pub trait IBinder { /// Send a ping transaction to this object fn ping_binder(&mut self) -> Result<()>; + /// Indicate that the service intends to receive caller security contexts. + fn set_requesting_sid(&mut self, enable: bool); + /// Dump this object to the given file handle fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()>; diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs index 5602c9688c..82212d8120 100644 --- a/libs/binder/rust/src/proxy.rs +++ b/libs/binder/rust/src/proxy.rs @@ -249,6 +249,12 @@ impl<T: AsNative<sys::AIBinder>> IBinder for T { status_result(status) } + fn set_requesting_sid(&mut self, enable: bool) { + unsafe { + sys::AIBinder_setRequestingSid(self.as_native_mut(), enable) + }; + } + fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()> { let args: Vec<_> = args.iter().map(|a| CString::new(*a).unwrap()).collect(); let mut arg_ptrs: Vec<_> = args.iter().map(|a| a.as_ptr()).collect(); diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs index 992f074f26..0e05f10dfe 100644 --- a/libs/binder/rust/src/state.rs +++ b/libs/binder/rust/src/state.rs @@ -98,4 +98,36 @@ impl ThreadState { sys::AIBinder_getCallingPid() } } + + /// This function makes the client's security context available to the + /// service calling this function. This can be used for access control. + /// It does not suffer from the TOCTOU issues of get_calling_pid. + /// + /// Implementations of `check_permission` should use the given CStr + /// argument as context for selinux permission checks. If `None` is + /// given, the implementation should fall back to using the PID + /// instead. + /// + /// Note: `None` may be passed to the callback if the caller did not + /// `set_requesting_sid` on the serviced binder, or if the underlying + /// kernel is too old to support this feature. + pub fn with_calling_sid<T, F>(check_permission: F) -> T + where + for<'a> F: FnOnce(Option<&'a std::ffi::CStr>) -> T { + // Safety: AIBinder_getCallingSid returns a c-string pointer + // that is valid for a transaction. Also, the string returned + // is thread local. By restricting the lifetime of the CStr + // reference to the scope of the callback, we prevent it being + // used beyond the guaranteed lifetime. + check_permission(unsafe { + let sid = sys::AIBinder_getCallingSid(); + // AIBinder_getCallingSid() returns a '\0' terminated string + // or NULL. + if sid.is_null() { + None + } else { + Some(std::ffi::CStr::from_ptr(sid)) + } + }) + } } diff --git a/libs/binder/rust/sys/BinderBindings.h b/libs/binder/rust/sys/BinderBindings.h index c7a06d9a1c..303f4a5a86 100644 --- a/libs/binder/rust/sys/BinderBindings.h +++ b/libs/binder/rust/sys/BinderBindings.h @@ -14,6 +14,7 @@ * limitations under the License. */ +#include <android/binder_context.h> #include <android/binder_ibinder.h> #include <android/binder_manager.h> #include <android/binder_parcel.h> diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp index 622604f0ed..3db40ba92d 100644 --- a/libs/binder/rust/tests/Android.bp +++ b/libs/binder/rust/tests/Android.bp @@ -3,6 +3,10 @@ rust_test { srcs: ["integration.rs"], rustlibs: [ "libbinder_rs", + "libselinux_bindgen", + ], + shared_libs: [ + "libselinux", ], // For the binaries to be pushed properly as specified in AndroidTest.xml, // this cannot be the same as the module name. diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs index fe59416a99..953d328d23 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 @@ fn main() -> Result<(), &'static str> { 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 @@ struct TestService { 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 @@ declare_binder_interface! { 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 @@ impl ITest for Binder<BnTest> { 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 @@ mod tests { 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)); |