include!(concat!(env!("OUT_DIR"), "/permission_controller.rs"));
pub use android::os::IPermissionController::{
BnPermissionController, BpPermissionController, IPermissionController,
IPermissionControllerDefault, IPermissionControllerDefaultRef,
};
#[cfg(feature = "async")]
pub use android::os::IPermissionController::{
IPermissionControllerAsync, IPermissionControllerAsyncService,
};
use std::sync::{Arc, Mutex, PoisonError, RwLock};
use crate::error::Result;
use crate::{hub, Caller, FromIBinder, Parcel, Strong};
pub const SERVICE_NAME: &str = "permission";
pub trait PermissionAuthority: Send + Sync {
fn check(&self, permission: &str, caller: &Caller) -> bool;
}
static AUTHORITY: RwLock<Option<Arc<dyn PermissionAuthority>>> = RwLock::new(None);
pub fn set_permission_authority(authority: Arc<dyn PermissionAuthority>) {
*AUTHORITY.write().expect("permission authority poisoned") = Some(authority);
}
pub fn clear_permission_authority() {
*AUTHORITY.write().expect("permission authority poisoned") = None;
}
pub fn default() -> Result<Strong<dyn IPermissionController>> {
let binder = hub::try_get_service(SERVICE_NAME)?.ok_or(crate::StatusCode::NameNotFound)?;
<dyn IPermissionController>::try_from(binder)
}
pub fn check_permission(reader: &Parcel, permission_name: &str) -> bool {
let authority = AUTHORITY
.read()
.expect("permission authority poisoned")
.clone();
if let Some(authority) = authority {
return crate::calling_caller()
.is_some_and(|caller| authority.check(permission_name, &caller));
}
if !reader.is_kernel_backed() {
warn_enforce_permission_over_rpc();
return false;
}
if !crate::is_handling_transaction() {
return false;
}
let calling_pid = crate::get_calling_pid() as i32;
let calling_uid = crate::get_calling_uid() as i32;
ask_controller(&CONTROLLER, default, |pc| {
pc.checkPermission(permission_name, calling_pid, calling_uid)
})
}
type ControllerSlot = Mutex<Option<Arc<Strong<dyn IPermissionController>>>>;
static CONTROLLER: ControllerSlot = Mutex::new(None);
fn ask_controller(
slot: &ControllerSlot,
lookup: impl FnOnce() -> Result<Strong<dyn IPermissionController>>,
ask: impl Fn(&Strong<dyn IPermissionController>) -> crate::BinderResult<bool>,
) -> bool {
let lock = || slot.lock().unwrap_or_else(PoisonError::into_inner);
let cached = lock().clone();
if let Some(pc) = cached {
if let Ok(granted) = ask(&pc) {
return granted;
}
let mut slot = lock();
if slot.as_ref().is_some_and(|c| Arc::ptr_eq(c, &pc)) {
*slot = None;
}
}
let Ok(pc) = lookup() else {
return false;
};
let Ok(granted) = ask(&pc) else {
return false;
};
*lock() = Some(Arc::new(pc));
granted
}
fn warn_enforce_permission_over_rpc() {
use std::sync::Once;
static WARNED: Once = Once::new();
WARNED.call_once(|| {
log::warn!(
"@EnforcePermission is unsupported over RPC and denies every \
guarded method (Plan 2-16 Phase A); use PeerIdentity / \
set_authorizer or uid ACLs for RPC authorization"
);
});
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "rpc")]
const UNIX_CAPS: crate::TransportCaps =
crate::TransportCaps::TRUSTED_UID.union(crate::TransportCaps::SAME_HOST);
#[test]
fn test_descriptor_matches_aosp_wire() {
assert_eq!(
<BpPermissionController as IPermissionController>::descriptor(),
"android.os.IPermissionController"
);
}
#[test]
fn test_service_name_matches_system_server_registration() {
assert_eq!(SERVICE_NAME, "permission");
}
struct FakeController {
grant: bool,
dead: Arc<std::sync::atomic::AtomicBool>,
}
impl crate::Interface for FakeController {}
impl IPermissionController for FakeController {
fn checkPermission(&self, _: &str, _: i32, _: i32) -> crate::BinderResult<bool> {
if self.dead.load(std::sync::atomic::Ordering::SeqCst) {
return Err(crate::StatusCode::DeadObject.into());
}
Ok(self.grant)
}
fn noteOp(&self, _: &str, _: i32, _: &str) -> crate::BinderResult<i32> {
Ok(0)
}
fn getPackagesForUid(&self, _: i32) -> crate::BinderResult<Vec<String>> {
Ok(Vec::new())
}
fn isRuntimePermission(&self, _: &str) -> crate::BinderResult<bool> {
Ok(false)
}
fn getPackageUid(&self, _: &str, _: i32) -> crate::BinderResult<i32> {
Ok(0)
}
}
fn fake(
grant: bool,
) -> (
Strong<dyn IPermissionController>,
Arc<std::sync::atomic::AtomicBool>,
) {
let dead = Arc::new(std::sync::atomic::AtomicBool::new(false));
let pc = BnPermissionController::new_binder(FakeController {
grant,
dead: dead.clone(),
});
(pc, dead)
}
#[test]
fn the_controller_is_cached_until_it_fails() {
let ask = |pc: &Strong<dyn IPermissionController>| pc.checkPermission("p", 1, 2);
let slot: ControllerSlot = Mutex::new(None);
let (first, first_dead) = fake(true);
assert!(ask_controller(&slot, || Ok(first), ask));
assert!(
ask_controller(&slot, || panic!("a cached controller needs no lookup"), ask),
"the cached controller answers"
);
first_dead.store(true, std::sync::atomic::Ordering::SeqCst);
let (second, _second_dead) = fake(false);
let second_binder = second.as_binder();
assert!(
!ask_controller(&slot, || Ok(second), ask),
"the fresh controller's answer, not a failure's deny"
);
let cached = slot
.lock()
.unwrap()
.clone()
.expect("the fresh controller is cached");
assert!(cached.as_binder() == second_binder);
let slot: ControllerSlot = Mutex::new(None);
let (broken, broken_dead) = fake(true);
broken_dead.store(true, std::sync::atomic::Ordering::SeqCst);
assert!(!ask_controller(&slot, || Ok(broken), ask));
assert!(slot.lock().unwrap().is_none());
assert!(!ask_controller(
&slot,
|| Err(crate::StatusCode::NameNotFound),
ask
));
}
#[cfg(feature = "rpc")]
#[serial_test::serial(authority)]
#[test]
fn check_permission_denies_rpc_parcel() {
use crate::rpc::transport::PeerIdentity;
use crate::thread_state::RpcCallingGuard;
use std::sync::Arc;
let mut rpc_parcel = Parcel::new();
rpc_parcel.set_for_rpc(true).unwrap();
let _g = RpcCallingGuard::install(
Arc::new(PeerIdentity::Local { uid: 1000, pid: 7 }),
UNIX_CAPS,
);
assert!(crate::is_handling_transaction());
assert!(
!check_permission(&rpc_parcel, "android.permission.INTERNET"),
"RPC parcel must fail-closed regardless of uid/PMS"
);
drop(_g);
let kernel_parcel = Parcel::new();
assert!(kernel_parcel.is_kernel_backed());
assert!(!crate::is_handling_transaction());
assert!(
!check_permission(&kernel_parcel, "android.permission.INTERNET"),
"outside a transaction the kernel path denies before PMS"
);
}
#[cfg(feature = "rpc")]
#[serial_test::serial(authority)]
#[test]
fn injected_authority_overrides_default_rpc_deny() {
use crate::rpc::transport::PeerIdentity;
use crate::thread_state::RpcCallingGuard;
use crate::Caller;
use std::sync::Arc;
struct UidGrant {
allow_uid: u32,
permission: String,
}
impl PermissionAuthority for UidGrant {
fn check(&self, permission: &str, caller: &Caller) -> bool {
matches!(caller, Caller::Rpc(PeerIdentity::Local { uid, .. })
if *uid == self.allow_uid && permission == self.permission)
}
}
set_permission_authority(Arc::new(UidGrant {
allow_uid: 1000,
permission: "com.example.DO_THING".to_string(),
}));
let mut rpc_parcel = Parcel::new();
rpc_parcel.set_for_rpc(true).unwrap();
{
let _g = RpcCallingGuard::install(
Arc::new(PeerIdentity::Local { uid: 1000, pid: 7 }),
UNIX_CAPS,
);
assert!(
check_permission(&rpc_parcel, "com.example.DO_THING"),
"authority must grant the allowed uid+permission over RPC"
);
assert!(
!check_permission(&rpc_parcel, "com.example.OTHER"),
"authority must deny an unknown permission"
);
}
{
let _g = RpcCallingGuard::install(
Arc::new(PeerIdentity::Local { uid: 2000, pid: 7 }),
UNIX_CAPS,
);
assert!(
!check_permission(&rpc_parcel, "com.example.DO_THING"),
"authority must deny a non-allowed uid"
);
}
assert!(!check_permission(&rpc_parcel, "com.example.DO_THING"));
clear_permission_authority();
let _g = RpcCallingGuard::install(
Arc::new(PeerIdentity::Local { uid: 1000, pid: 7 }),
UNIX_CAPS,
);
assert!(
!check_permission(&rpc_parcel, "com.example.DO_THING"),
"after clear, the default RPC deny is restored"
);
}
}