blob: 68c9b746a248d975e29ba969001d83596cd22ddb [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.
//! Access control tests.
use super::*;
use crate::key_perm_set;
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::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_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 key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
check_grant_permission(0, &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(0, &system_server_ctx, KeyPerm::Grant.into(), &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(0, &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(0, &sctx, KeyPerm::Grant.into(), &key));
} else {
// unprivileged grant attempts always fail. shell does not have the grant permission.
assert_perm_failed!(check_grant_permission(0, &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::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));
}