blob: d9bdf79174b693b4b2380efc71549bdc808304cd [file] [log] [blame]
// Copyright 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.
//! This crate provides access control primitives for Keystore 2.0.
//! It provides high level functions for checking permissions in the keystore2 and keystore2_key
//! SELinux classes based on the keystore2_selinux backend.
//! It also provides KeystorePerm and KeyPerm as convenience wrappers for the SELinux permission
//! defined by keystore2 and keystore2_key respectively.
use crate::error::Error as KsError;
use crate::error::ResponseCode;
use crate::ks_err;
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
};
use anyhow::Context as AnyhowContext;
use keystore2_selinux as selinux;
use lazy_static::lazy_static;
use selinux::{implement_class, Backend, ClassPermission};
use std::cmp::PartialEq;
use std::convert::From;
use std::ffi::CStr;
// Replace getcon with a mock in the test situation
#[cfg(not(test))]
use selinux::getcon;
#[cfg(test)]
use tests::test_getcon as getcon;
lazy_static! {
// Panicking here is allowed because keystore cannot function without this backend
// and it would happen early and indicate a gross misconfiguration of the device.
static ref KEYSTORE2_KEY_LABEL_BACKEND: selinux::KeystoreKeyBackend =
selinux::KeystoreKeyBackend::new().unwrap();
}
fn lookup_keystore2_key_context(namespace: i64) -> anyhow::Result<selinux::Context> {
KEYSTORE2_KEY_LABEL_BACKEND.lookup(&namespace.to_string())
}
implement_class!(
/// KeyPerm provides a convenient abstraction from the SELinux class `keystore2_key`.
/// At the same time it maps `KeyPermissions` from the Keystore 2.0 AIDL Grant interface to
/// the SELinux permissions.
#[repr(i32)]
#[selinux(class_name = keystore2_key)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum KeyPerm {
/// Checked when convert_storage_key_to_ephemeral is called.
#[selinux(name = convert_storage_key_to_ephemeral)]
ConvertStorageKeyToEphemeral = KeyPermission::CONVERT_STORAGE_KEY_TO_EPHEMERAL.0,
/// Checked when the caller tries do delete a key.
#[selinux(name = delete)]
Delete = KeyPermission::DELETE.0,
/// Checked when the caller tries to use a unique id.
#[selinux(name = gen_unique_id)]
GenUniqueId = KeyPermission::GEN_UNIQUE_ID.0,
/// Checked when the caller tries to load a key.
#[selinux(name = get_info)]
GetInfo = KeyPermission::GET_INFO.0,
/// Checked when the caller attempts to grant a key to another uid.
/// Also used for gating key migration attempts.
#[selinux(name = grant)]
Grant = KeyPermission::GRANT.0,
/// Checked when the caller attempts to use Domain::BLOB.
#[selinux(name = manage_blob)]
ManageBlob = KeyPermission::MANAGE_BLOB.0,
/// Checked when the caller tries to create a key which implies rebinding
/// an alias to the new key.
#[selinux(name = rebind)]
Rebind = KeyPermission::REBIND.0,
/// Checked when the caller attempts to create a forced operation.
#[selinux(name = req_forced_op)]
ReqForcedOp = KeyPermission::REQ_FORCED_OP.0,
/// Checked when the caller attempts to update public key artifacts.
#[selinux(name = update)]
Update = KeyPermission::UPDATE.0,
/// Checked when the caller attempts to use a private or public key.
#[selinux(name = use)]
Use = KeyPermission::USE.0,
/// Does nothing, and is not checked. For use of device identifiers,
/// the caller must hold the READ_PRIVILEGED_PHONE_STATE Android
/// permission.
#[selinux(name = use_dev_id)]
UseDevId = KeyPermission::USE_DEV_ID.0,
}
);
implement_class!(
/// KeystorePerm provides a convenient abstraction from the SELinux class `keystore2`.
/// Using the implement_permission macro we get the same features as `KeyPerm`.
#[selinux(class_name = keystore2)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum KeystorePerm {
/// Checked when a new auth token is installed.
#[selinux(name = add_auth)]
AddAuth,
/// Checked when an app is uninstalled or wiped.
#[selinux(name = clear_ns)]
ClearNs,
/// Checked when the user state is queried from Keystore 2.0.
#[selinux(name = get_state)]
GetState,
/// Checked when Keystore 2.0 is asked to list a namespace that the caller
/// does not have the get_info permission for.
#[selinux(name = list)]
List,
/// Checked when Keystore 2.0 gets locked.
#[selinux(name = lock)]
Lock,
/// Checked when Keystore 2.0 shall be reset.
#[selinux(name = reset)]
Reset,
/// Checked when Keystore 2.0 shall be unlocked.
#[selinux(name = unlock)]
Unlock,
/// Checked when user is added or removed.
#[selinux(name = change_user)]
ChangeUser,
/// Checked when password of the user is changed.
#[selinux(name = change_password)]
ChangePassword,
/// Checked when a UID is cleared.
#[selinux(name = clear_uid)]
ClearUID,
/// Checked when Credstore calls IKeystoreAuthorization to obtain auth tokens.
#[selinux(name = get_auth_token)]
GetAuthToken,
/// Checked when earlyBootEnded() is called.
#[selinux(name = early_boot_ended)]
EarlyBootEnded,
/// Checked when IKeystoreMaintenance::onDeviceOffBody is called.
#[selinux(name = report_off_body)]
ReportOffBody,
/// Checked when IkeystoreMetrics::pullMetrics is called.
#[selinux(name = pull_metrics)]
PullMetrics,
/// Checked when IKeystoreMaintenance::deleteAllKeys is called.
#[selinux(name = delete_all_keys)]
DeleteAllKeys,
/// Checked on calls to IRemotelyProvisionedKeyPool::getAttestationKey
#[selinux(name = get_attestation_key)]
GetAttestationKey,
}
);
/// Represents a set of `KeyPerm` permissions.
/// `IntoIterator` is implemented for this struct allowing the iteration through all the
/// permissions in the set.
/// It also implements a function `includes(self, other)` that checks if the permissions
/// in `other` are included in `self`.
///
/// KeyPermSet can be created with the macro `key_perm_set![]`.
///
/// ## Example
/// ```
/// let perms1 = key_perm_set![KeyPerm::Use, KeyPerm::ManageBlob, KeyPerm::Grant];
/// let perms2 = key_perm_set![KeyPerm::Use, KeyPerm::ManageBlob];
///
/// assert!(perms1.includes(perms2))
/// assert!(!perms2.includes(perms1))
///
/// let i = perms1.into_iter();
/// // iteration in ascending order of the permission's numeric representation.
/// assert_eq(Some(KeyPerm::ManageBlob), i.next());
/// assert_eq(Some(KeyPerm::Grant), i.next());
/// assert_eq(Some(KeyPerm::Use), i.next());
/// assert_eq(None, i.next());
/// ```
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct KeyPermSet(pub i32);
mod perm {
use super::*;
pub struct IntoIter {
vec: KeyPermSet,
pos: u8,
}
impl IntoIter {
pub fn new(v: KeyPermSet) -> Self {
Self { vec: v, pos: 0 }
}
}
impl std::iter::Iterator for IntoIter {
type Item = KeyPerm;
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.pos == 32 {
return None;
}
let p = self.vec.0 & (1 << self.pos);
self.pos += 1;
if p != 0 {
return Some(KeyPerm::from(p));
}
}
}
}
}
impl From<KeyPerm> for KeyPermSet {
fn from(p: KeyPerm) -> Self {
Self(p as i32)
}
}
/// allow conversion from the AIDL wire type i32 to a permission set.
impl From<i32> for KeyPermSet {
fn from(p: i32) -> Self {
Self(p)
}
}
impl From<KeyPermSet> for i32 {
fn from(p: KeyPermSet) -> i32 {
p.0
}
}
impl KeyPermSet {
/// Returns true iff this permission set has all of the permissions that are in `other`.
pub fn includes<T: Into<KeyPermSet>>(&self, other: T) -> bool {
let o: KeyPermSet = other.into();
(self.0 & o.0) == o.0
}
}
/// This macro can be used to create a `KeyPermSet` from a list of `KeyPerm` values.
///
/// ## Example
/// ```
/// let v = key_perm_set![Perm::delete(), Perm::manage_blob()];
/// ```
#[macro_export]
macro_rules! key_perm_set {
() => { KeyPermSet(0) };
($head:expr $(, $tail:expr)* $(,)?) => {
KeyPermSet($head as i32 $(| $tail as i32)*)
};
}
impl IntoIterator for KeyPermSet {
type Item = KeyPerm;
type IntoIter = perm::IntoIter;
fn into_iter(self) -> Self::IntoIter {
Self::IntoIter::new(self)
}
}
/// Uses `selinux::check_permission` to check if the given caller context `caller_cxt` may access
/// the given permision `perm` of the `keystore2` security class.
pub fn check_keystore_permission(caller_ctx: &CStr, perm: KeystorePerm) -> anyhow::Result<()> {
let target_context = getcon().context("check_keystore_permission: getcon failed.")?;
selinux::check_permission(caller_ctx, &target_context, perm)
}
/// Uses `selinux::check_permission` to check if the given caller context `caller_cxt` has
/// all the permissions indicated in `access_vec` for the target domain indicated by the key
/// descriptor `key` in the security class `keystore2_key`.
///
/// Also checks if the caller has the grant permission for the given target domain.
///
/// Attempts to grant the grant permission are always denied.
///
/// The only viable target domains are
/// * `Domain::APP` in which case u:r:keystore:s0 is used as target context and
/// * `Domain::SELINUX` in which case the `key.nspace` parameter is looked up in
/// SELinux keystore key backend, and the result is used
/// as target context.
pub fn check_grant_permission(
caller_ctx: &CStr,
access_vec: KeyPermSet,
key: &KeyDescriptor,
) -> anyhow::Result<()> {
let target_context = match key.domain {
Domain::APP => getcon().context("check_grant_permission: getcon failed.")?,
Domain::SELINUX => lookup_keystore2_key_context(key.nspace)
.context("check_grant_permission: Domain::SELINUX: Failed to lookup namespace.")?,
_ => return Err(KsError::sys()).context(format!("Cannot grant {:?}.", key.domain)),
};
selinux::check_permission(caller_ctx, &target_context, KeyPerm::Grant)
.context("Grant permission is required when granting.")?;
if access_vec.includes(KeyPerm::Grant) {
return Err(selinux::Error::perm()).context("Grant permission cannot be granted.");
}
for p in access_vec.into_iter() {
selinux::check_permission(caller_ctx, &target_context, p).context(ks_err!(
"check_permission failed. \
The caller may have tried to grant a permission that they don't possess. {:?}",
p
))?
}
Ok(())
}
/// Uses `selinux::check_permission` to check if the given caller context `caller_cxt`
/// has the permissions indicated by `perm` for the target domain indicated by the key
/// descriptor `key` in the security class `keystore2_key`.
///
/// The behavior differs slightly depending on the selected target domain:
/// * `Domain::APP` u:r:keystore:s0 is used as target context.
/// * `Domain::SELINUX` `key.nspace` parameter is looked up in the SELinux keystore key
/// backend, and the result is used as target context.
/// * `Domain::BLOB` Same as SELinux but the "manage_blob" permission is always checked additionally
/// to the one supplied in `perm`.
/// * `Domain::GRANT` Does not use selinux::check_permission. Instead the `access_vector`
/// parameter is queried for permission, which must be supplied in this case.
///
/// ## Return values.
/// * Ok(()) If the requested permissions were granted.
/// * Err(selinux::Error::perm()) If the requested permissions were denied.
/// * Err(KsError::sys()) This error is produced if `Domain::GRANT` is selected but no `access_vec`
/// was supplied. It is also produced if `Domain::KEY_ID` was selected, and
/// on various unexpected backend failures.
pub fn check_key_permission(
caller_uid: u32,
caller_ctx: &CStr,
perm: KeyPerm,
key: &KeyDescriptor,
access_vector: &Option<KeyPermSet>,
) -> anyhow::Result<()> {
// If an access vector was supplied, the key is either accessed by GRANT or by KEY_ID.
// In the former case, key.domain was set to GRANT and we check the failure cases
// further below. If the access is requested by KEY_ID, key.domain would have been
// resolved to APP or SELINUX depending on where the key actually resides.
// Either way we can return here immediately if the access vector covers the requested
// permission. If it does not, we can still check if the caller has access by means of
// ownership.
if let Some(access_vector) = access_vector {
if access_vector.includes(perm) {
return Ok(());
}
}
let target_context = match key.domain {
// apps get the default keystore context
Domain::APP => {
if caller_uid as i64 != key.nspace {
return Err(selinux::Error::perm())
.context("Trying to access key without ownership.");
}
getcon().context(ks_err!("getcon failed."))?
}
Domain::SELINUX => lookup_keystore2_key_context(key.nspace)
.context(ks_err!("Domain::SELINUX: Failed to lookup namespace."))?,
Domain::GRANT => {
match access_vector {
Some(_) => {
return Err(selinux::Error::perm())
.context(format!("\"{}\" not granted", perm.name()));
}
None => {
// If DOMAIN_GRANT was selected an access vector must be supplied.
return Err(KsError::sys()).context(ks_err!(
"Cannot check permission for Domain::GRANT without access vector.",
));
}
}
}
Domain::KEY_ID => {
// We should never be called with `Domain::KEY_ID. The database
// lookup should have converted this into one of `Domain::APP`
// or `Domain::SELINUX`.
return Err(KsError::sys())
.context(ks_err!("Cannot check permission for Domain::KEY_ID.",));
}
Domain::BLOB => {
let tctx = lookup_keystore2_key_context(key.nspace)
.context(ks_err!("Domain::BLOB: Failed to lookup namespace."))?;
// If DOMAIN_KEY_BLOB was specified, we check for the "manage_blob"
// permission in addition to the requested permission.
selinux::check_permission(caller_ctx, &tctx, KeyPerm::ManageBlob)?;
tctx
}
_ => {
return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT))
.context(format!("Unknown domain value: \"{:?}\".", key.domain))
}
};
selinux::check_permission(caller_ctx, &target_context, perm)
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::anyhow;
use anyhow::Result;
use keystore2_selinux::*;
const ALL_PERMS: KeyPermSet = key_perm_set![
KeyPerm::ManageBlob,
KeyPerm::Delete,
KeyPerm::UseDevId,
KeyPerm::ReqForcedOp,
KeyPerm::GenUniqueId,
KeyPerm::Grant,
KeyPerm::GetInfo,
KeyPerm::Rebind,
KeyPerm::Update,
KeyPerm::Use,
KeyPerm::ConvertStorageKeyToEphemeral,
];
const SYSTEM_SERVER_PERMISSIONS_NO_GRANT: KeyPermSet = key_perm_set![
KeyPerm::Delete,
KeyPerm::UseDevId,
// No KeyPerm::Grant
KeyPerm::GetInfo,
KeyPerm::Rebind,
KeyPerm::Update,
KeyPerm::Use,
];
const NOT_GRANT_PERMS: KeyPermSet = key_perm_set![
KeyPerm::ManageBlob,
KeyPerm::Delete,
KeyPerm::UseDevId,
KeyPerm::ReqForcedOp,
KeyPerm::GenUniqueId,
// No KeyPerm::Grant
KeyPerm::GetInfo,
KeyPerm::Rebind,
KeyPerm::Update,
KeyPerm::Use,
KeyPerm::ConvertStorageKeyToEphemeral,
];
const UNPRIV_PERMS: KeyPermSet = key_perm_set![
KeyPerm::Delete,
KeyPerm::GetInfo,
KeyPerm::Rebind,
KeyPerm::Update,
KeyPerm::Use,
];
/// The su_key namespace as defined in su.te and keystore_key_contexts of the
/// SePolicy (system/sepolicy).
const SU_KEY_NAMESPACE: i32 = 0;
/// The shell_key namespace as defined in shell.te and keystore_key_contexts of the
/// SePolicy (system/sepolicy).
const SHELL_KEY_NAMESPACE: i32 = 1;
pub fn test_getcon() -> Result<Context> {
Context::new("u:object_r:keystore:s0")
}
// This macro evaluates the given expression and checks that
// a) evaluated to Result::Err() and that
// b) the wrapped error is selinux::Error::perm() (permission denied).
// We use a macro here because a function would mask which invocation caused the failure.
//
// TODO b/164121720 Replace this macro with a function when `track_caller` is available.
macro_rules! assert_perm_failed {
($test_function:expr) => {
let result = $test_function;
assert!(result.is_err(), "Permission check should have failed.");
assert_eq!(
Some(&selinux::Error::perm()),
result.err().unwrap().root_cause().downcast_ref::<selinux::Error>()
);
};
}
fn check_context() -> Result<(selinux::Context, i32, bool)> {
// Calling the non mocked selinux::getcon here intended.
let context = selinux::getcon()?;
match context.to_str().unwrap() {
"u:r:su:s0" => Ok((context, SU_KEY_NAMESPACE, true)),
"u:r:shell:s0" => Ok((context, SHELL_KEY_NAMESPACE, false)),
c => Err(anyhow!(format!(
"This test must be run as \"su\" or \"shell\". Current context: \"{}\"",
c
))),
}
}
#[test]
fn check_keystore_permission_test() -> Result<()> {
let system_server_ctx = Context::new("u:r:system_server:s0")?;
assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::AddAuth).is_ok());
assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearNs).is_ok());
assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::GetState).is_ok());
assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Lock).is_ok());
assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Reset).is_ok());
assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::Unlock).is_ok());
assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangeUser).is_ok());
assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ChangePassword).is_ok());
assert!(check_keystore_permission(&system_server_ctx, KeystorePerm::ClearUID).is_ok());
let shell_ctx = Context::new("u:r:shell:s0")?;
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::AddAuth));
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearNs));
assert!(check_keystore_permission(&shell_ctx, KeystorePerm::GetState).is_ok());
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::List));
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Lock));
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Reset));
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::Unlock));
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangeUser));
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ChangePassword));
assert_perm_failed!(check_keystore_permission(&shell_ctx, KeystorePerm::ClearUID));
Ok(())
}
#[test]
fn check_grant_permission_app() -> Result<()> {
let system_server_ctx = Context::new("u:r:system_server:s0")?;
let shell_ctx = Context::new("u:r:shell:s0")?;
let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
check_grant_permission(&system_server_ctx, SYSTEM_SERVER_PERMISSIONS_NO_GRANT, &key)
.expect("Grant permission check failed.");
// attempts to grant the grant permission must always fail even when privileged.
assert_perm_failed!(check_grant_permission(
&system_server_ctx,
KeyPerm::Grant.into(),
&key
));
// unprivileged grant attempts always fail. shell does not have the grant permission.
assert_perm_failed!(check_grant_permission(&shell_ctx, UNPRIV_PERMS, &key));
Ok(())
}
#[test]
fn check_grant_permission_selinux() -> Result<()> {
let (sctx, namespace, is_su) = check_context()?;
let key = KeyDescriptor {
domain: Domain::SELINUX,
nspace: namespace as i64,
alias: None,
blob: None,
};
if is_su {
assert!(check_grant_permission(&sctx, NOT_GRANT_PERMS, &key).is_ok());
// attempts to grant the grant permission must always fail even when privileged.
assert_perm_failed!(check_grant_permission(&sctx, KeyPerm::Grant.into(), &key));
} else {
// unprivileged grant attempts always fail. shell does not have the grant permission.
assert_perm_failed!(check_grant_permission(&sctx, UNPRIV_PERMS, &key));
}
Ok(())
}
#[test]
fn check_key_permission_domain_grant() -> Result<()> {
let key = KeyDescriptor { domain: Domain::GRANT, nspace: 0, alias: None, blob: None };
assert_perm_failed!(check_key_permission(
0,
&selinux::Context::new("ignored").unwrap(),
KeyPerm::Grant,
&key,
&Some(UNPRIV_PERMS)
));
check_key_permission(
0,
&selinux::Context::new("ignored").unwrap(),
KeyPerm::Use,
&key,
&Some(ALL_PERMS),
)
}
#[test]
fn check_key_permission_domain_app() -> Result<()> {
let system_server_ctx = Context::new("u:r:system_server:s0")?;
let shell_ctx = Context::new("u:r:shell:s0")?;
let gmscore_app = Context::new("u:r:gmscore_app:s0")?;
let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Use, &key, &None).is_ok());
assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Delete, &key, &None).is_ok());
assert!(check_key_permission(0, &system_server_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Rebind, &key, &None).is_ok());
assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Update, &key, &None).is_ok());
assert!(check_key_permission(0, &system_server_ctx, KeyPerm::Grant, &key, &None).is_ok());
assert!(check_key_permission(0, &system_server_ctx, KeyPerm::UseDevId, &key, &None).is_ok());
assert!(check_key_permission(0, &gmscore_app, KeyPerm::GenUniqueId, &key, &None).is_ok());
assert!(check_key_permission(0, &shell_ctx, KeyPerm::Use, &key, &None).is_ok());
assert!(check_key_permission(0, &shell_ctx, KeyPerm::Delete, &key, &None).is_ok());
assert!(check_key_permission(0, &shell_ctx, KeyPerm::GetInfo, &key, &None).is_ok());
assert!(check_key_permission(0, &shell_ctx, KeyPerm::Rebind, &key, &None).is_ok());
assert!(check_key_permission(0, &shell_ctx, KeyPerm::Update, &key, &None).is_ok());
assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::Grant, &key, &None));
assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ReqForcedOp, &key, &None));
assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::ManageBlob, &key, &None));
assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::UseDevId, &key, &None));
assert_perm_failed!(check_key_permission(0, &shell_ctx, KeyPerm::GenUniqueId, &key, &None));
// Also make sure that the permission fails if the caller is not the owner.
assert_perm_failed!(check_key_permission(
1, // the owner is 0
&system_server_ctx,
KeyPerm::Use,
&key,
&None
));
// Unless there was a grant.
assert!(check_key_permission(
1,
&system_server_ctx,
KeyPerm::Use,
&key,
&Some(key_perm_set![KeyPerm::Use])
)
.is_ok());
// But fail if the grant did not cover the requested permission.
assert_perm_failed!(check_key_permission(
1,
&system_server_ctx,
KeyPerm::Use,
&key,
&Some(key_perm_set![KeyPerm::GetInfo])
));
Ok(())
}
#[test]
fn check_key_permission_domain_selinux() -> Result<()> {
let (sctx, namespace, is_su) = check_context()?;
let key = KeyDescriptor {
domain: Domain::SELINUX,
nspace: namespace as i64,
alias: None,
blob: None,
};
assert!(check_key_permission(0, &sctx, KeyPerm::Use, &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::Delete, &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::GetInfo, &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::Rebind, &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::Update, &key, &None).is_ok());
if is_su {
assert!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None).is_ok());
} else {
assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::Grant, &key, &None));
assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ReqForcedOp, &key, &None));
assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::ManageBlob, &key, &None));
assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::UseDevId, &key, &None));
assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::GenUniqueId, &key, &None));
}
Ok(())
}
#[test]
fn check_key_permission_domain_blob() -> Result<()> {
let (sctx, namespace, is_su) = check_context()?;
let key = KeyDescriptor {
domain: Domain::BLOB,
nspace: namespace as i64,
alias: None,
blob: None,
};
if is_su {
check_key_permission(0, &sctx, KeyPerm::Use, &key, &None)
} else {
assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::Use, &key, &None));
Ok(())
}
}
#[test]
fn check_key_permission_domain_key_id() -> Result<()> {
let key = KeyDescriptor { domain: Domain::KEY_ID, nspace: 0, alias: None, blob: None };
assert_eq!(
Some(&KsError::sys()),
check_key_permission(
0,
&selinux::Context::new("ignored").unwrap(),
KeyPerm::Use,
&key,
&None
)
.err()
.unwrap()
.root_cause()
.downcast_ref::<KsError>()
);
Ok(())
}
#[test]
fn key_perm_set_all_test() {
let v = key_perm_set![
KeyPerm::ManageBlob,
KeyPerm::Delete,
KeyPerm::UseDevId,
KeyPerm::ReqForcedOp,
KeyPerm::GenUniqueId,
KeyPerm::Grant,
KeyPerm::GetInfo,
KeyPerm::Rebind,
KeyPerm::Update,
KeyPerm::Use // Test if the macro accepts missing comma at the end of the list.
];
let mut i = v.into_iter();
assert_eq!(i.next().unwrap().name(), "delete");
assert_eq!(i.next().unwrap().name(), "gen_unique_id");
assert_eq!(i.next().unwrap().name(), "get_info");
assert_eq!(i.next().unwrap().name(), "grant");
assert_eq!(i.next().unwrap().name(), "manage_blob");
assert_eq!(i.next().unwrap().name(), "rebind");
assert_eq!(i.next().unwrap().name(), "req_forced_op");
assert_eq!(i.next().unwrap().name(), "update");
assert_eq!(i.next().unwrap().name(), "use");
assert_eq!(i.next().unwrap().name(), "use_dev_id");
assert_eq!(None, i.next());
}
#[test]
fn key_perm_set_sparse_test() {
let v = key_perm_set![
KeyPerm::ManageBlob,
KeyPerm::ReqForcedOp,
KeyPerm::GenUniqueId,
KeyPerm::Update,
KeyPerm::Use, // Test if macro accepts the comma at the end of the list.
];
let mut i = v.into_iter();
assert_eq!(i.next().unwrap().name(), "gen_unique_id");
assert_eq!(i.next().unwrap().name(), "manage_blob");
assert_eq!(i.next().unwrap().name(), "req_forced_op");
assert_eq!(i.next().unwrap().name(), "update");
assert_eq!(i.next().unwrap().name(), "use");
assert_eq!(None, i.next());
}
#[test]
fn key_perm_set_empty_test() {
let v = key_perm_set![];
let mut i = v.into_iter();
assert_eq!(None, i.next());
}
#[test]
fn key_perm_set_include_subset_test() {
let v1 = key_perm_set![
KeyPerm::ManageBlob,
KeyPerm::Delete,
KeyPerm::UseDevId,
KeyPerm::ReqForcedOp,
KeyPerm::GenUniqueId,
KeyPerm::Grant,
KeyPerm::GetInfo,
KeyPerm::Rebind,
KeyPerm::Update,
KeyPerm::Use,
];
let v2 = key_perm_set![
KeyPerm::ManageBlob,
KeyPerm::Delete,
KeyPerm::Rebind,
KeyPerm::Update,
KeyPerm::Use,
];
assert!(v1.includes(v2));
assert!(!v2.includes(v1));
}
#[test]
fn key_perm_set_include_equal_test() {
let v1 = key_perm_set![
KeyPerm::ManageBlob,
KeyPerm::Delete,
KeyPerm::Rebind,
KeyPerm::Update,
KeyPerm::Use,
];
let v2 = key_perm_set![
KeyPerm::ManageBlob,
KeyPerm::Delete,
KeyPerm::Rebind,
KeyPerm::Update,
KeyPerm::Use,
];
assert!(v1.includes(v2));
assert!(v2.includes(v1));
}
#[test]
fn key_perm_set_include_overlap_test() {
let v1 = key_perm_set![
KeyPerm::ManageBlob,
KeyPerm::Delete,
KeyPerm::Grant, // only in v1
KeyPerm::Rebind,
KeyPerm::Update,
KeyPerm::Use,
];
let v2 = key_perm_set![
KeyPerm::ManageBlob,
KeyPerm::Delete,
KeyPerm::ReqForcedOp, // only in v2
KeyPerm::Rebind,
KeyPerm::Update,
KeyPerm::Use,
];
assert!(!v1.includes(v2));
assert!(!v2.includes(v1));
}
#[test]
fn key_perm_set_include_no_overlap_test() {
let v1 = key_perm_set![KeyPerm::ManageBlob, KeyPerm::Delete, KeyPerm::Grant,];
let v2 =
key_perm_set![KeyPerm::ReqForcedOp, KeyPerm::Rebind, KeyPerm::Update, KeyPerm::Use,];
assert!(!v1.includes(v2));
assert!(!v2.includes(v1));
}
}