Merge "Keystore 2.0: Allow by key id usage of granted keys."

This commit is contained in:
Treehugger Robot 2021-01-27 02:29:21 +00:00 committed by Gerrit Code Review
commit 13ffc59553
3 changed files with 250 additions and 60 deletions

View file

@ -1229,18 +1229,18 @@ impl KeystoreDB {
// Domain::KEY_ID. In this case we load the domain and namespace from the
// keyentry database because we need them for access control.
Domain::KEY_ID => {
let mut stmt = tx
.prepare(
"SELECT domain, namespace FROM persistent.keyentry
WHERE
id = ?
AND state = ?;",
)
.context("Domain::KEY_ID: prepare statement failed")?;
let mut rows = stmt
.query(params![key.nspace, KeyLifeCycle::Live])
.context("Domain::KEY_ID: query failed.")?;
let (domain, namespace): (Domain, i64) =
let (domain, namespace): (Domain, i64) = {
let mut stmt = tx
.prepare(
"SELECT domain, namespace FROM persistent.keyentry
WHERE
id = ?
AND state = ?;",
)
.context("Domain::KEY_ID: prepare statement failed")?;
let mut rows = stmt
.query(params![key.nspace, KeyLifeCycle::Live])
.context("Domain::KEY_ID: query failed.")?;
db_utils::with_rows_extract_one(&mut rows, |row| {
let r =
row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?;
@ -1249,13 +1249,37 @@ impl KeystoreDB {
r.get(1).context("Failed to unpack namespace.")?,
))
})
.context("Domain::KEY_ID.")?;
.context("Domain::KEY_ID.")?
};
// We may use a key by id after loading it by grant.
// In this case we have to check if the caller has a grant for this particular
// key. We can skip this if we already know that the caller is the owner.
// But we cannot know this if domain is anything but App. E.g. in the case
// of Domain::SELINUX we have to speculatively check for grants because we have to
// consult the SEPolicy before we know if the caller is the owner.
let access_vector: Option<KeyPermSet> =
if domain != Domain::APP || namespace != caller_uid as i64 {
let access_vector: Option<i32> = tx
.query_row(
"SELECT access_vector FROM persistent.grant
WHERE grantee = ? AND keyentryid = ?;",
params![caller_uid as i64, key.nspace],
|row| row.get(0),
)
.optional()
.context("Domain::KEY_ID: query grant failed.")?;
access_vector.map(|p| p.into())
} else {
None
};
let key_id = key.nspace;
let mut access_key = key;
access_key.domain = domain;
access_key.nspace = namespace;
Ok((key_id, access_key, None))
Ok((key_id, access_key, access_vector))
}
_ => Err(anyhow!(KsError::sys())),
}
@ -2521,6 +2545,90 @@ mod tests {
Ok(())
}
// This test attempts to load a key by key id while the caller is not the owner
// but a grant exists for the given key and the caller.
#[test]
fn test_insert_and_load_full_keyentry_from_grant_by_key_id() -> Result<()> {
let mut db = new_test_db()?;
const OWNER_UID: u32 = 1u32;
const GRANTEE_UID: u32 = 2u32;
const SOMEONE_ELSE_UID: u32 = 3u32;
let key_id = make_test_key_entry(&mut db, Domain::APP, OWNER_UID as i64, TEST_ALIAS, None)
.context("test_insert_and_load_full_keyentry_from_grant_by_key_id")?
.0;
db.grant(
KeyDescriptor {
domain: Domain::APP,
nspace: 0,
alias: Some(TEST_ALIAS.to_string()),
blob: None,
},
OWNER_UID,
GRANTEE_UID,
key_perm_set![KeyPerm::use_()],
|_k, _av| Ok(()),
)
.unwrap();
debug_dump_grant_table(&mut db)?;
let id_descriptor =
KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, ..Default::default() };
let (_, key_entry) = db
.load_key_entry(
id_descriptor.clone(),
KeyType::Client,
KeyEntryLoadBits::BOTH,
GRANTEE_UID,
|k, av| {
assert_eq!(Domain::APP, k.domain);
assert_eq!(OWNER_UID as i64, k.nspace);
assert!(av.unwrap().includes(KeyPerm::use_()));
Ok(())
},
)
.unwrap();
assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
let (_, key_entry) = db
.load_key_entry(
id_descriptor.clone(),
KeyType::Client,
KeyEntryLoadBits::BOTH,
SOMEONE_ELSE_UID,
|k, av| {
assert_eq!(Domain::APP, k.domain);
assert_eq!(OWNER_UID as i64, k.nspace);
assert!(av.is_none());
Ok(())
},
)
.unwrap();
assert_eq!(key_entry, make_test_key_entry_test_vector(key_id, None));
db.unbind_key(id_descriptor.clone(), KeyType::Client, OWNER_UID, |_, _| Ok(())).unwrap();
assert_eq!(
Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)),
db.load_key_entry(
id_descriptor,
KeyType::Client,
KeyEntryLoadBits::NONE,
GRANTEE_UID,
|_k, _av| Ok(()),
)
.unwrap_err()
.root_cause()
.downcast_ref::<KsError>()
);
Ok(())
}
static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key";
#[test]

View file

@ -482,25 +482,41 @@ pub fn check_grant_permission(
/// 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 => getcon().context("check_key_permission: getcon failed.")?,
Domain::APP => {
if caller_uid as i64 != key.nspace {
return Err(selinux::Error::perm())
.context("Trying to access key without ownership.");
}
getcon().context("check_key_permission: getcon failed.")?
}
Domain::SELINUX => lookup_keystore2_key_context(key.nspace)
.context("check_key_permission: Domain::SELINUX: Failed to lookup namespace.")?,
Domain::GRANT => {
match access_vector {
Some(pv) => {
if pv.includes(perm) {
return Ok(());
} else {
return Err(selinux::Error::perm())
.context(format!("\"{}\" not granted", perm.to_selinux()));
}
Some(_) => {
return Err(selinux::Error::perm())
.context(format!("\"{}\" not granted", perm.to_selinux()));
}
None => {
// If DOMAIN_GRANT was selected an access vector must be supplied.
@ -685,6 +701,7 @@ mod tests {
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,
@ -692,6 +709,7 @@ mod tests {
));
check_key_permission(
0,
&selinux::Context::new("ignored").unwrap(),
KeyPerm::use_(),
&key,
@ -707,38 +725,82 @@ mod tests {
let key = KeyDescriptor { domain: Domain::APP, nspace: 0, alias: None, blob: None };
assert!(check_key_permission(&system_server_ctx, KeyPerm::use_(), &key, &None).is_ok());
assert!(check_key_permission(&system_server_ctx, KeyPerm::delete(), &key, &None).is_ok());
assert!(check_key_permission(&system_server_ctx, KeyPerm::get_info(), &key, &None).is_ok());
assert!(check_key_permission(&system_server_ctx, KeyPerm::rebind(), &key, &None).is_ok());
assert!(check_key_permission(&system_server_ctx, KeyPerm::update(), &key, &None).is_ok());
assert!(check_key_permission(&system_server_ctx, KeyPerm::grant(), &key, &None).is_ok());
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(&system_server_ctx, KeyPerm::use_dev_id(), &key, &None).is_ok()
check_key_permission(0, &system_server_ctx, KeyPerm::get_info(), &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::use_dev_id(), &key, &None).is_ok()
);
assert!(
check_key_permission(0, &gmscore_app, KeyPerm::gen_unique_id(), &key, &None).is_ok()
);
assert!(check_key_permission(&gmscore_app, KeyPerm::gen_unique_id(), &key, &None).is_ok());
assert!(check_key_permission(&shell_ctx, KeyPerm::use_(), &key, &None).is_ok());
assert!(check_key_permission(&shell_ctx, KeyPerm::delete(), &key, &None).is_ok());
assert!(check_key_permission(&shell_ctx, KeyPerm::get_info(), &key, &None).is_ok());
assert!(check_key_permission(&shell_ctx, KeyPerm::rebind(), &key, &None).is_ok());
assert!(check_key_permission(&shell_ctx, KeyPerm::update(), &key, &None).is_ok());
assert_perm_failed!(check_key_permission(&shell_ctx, KeyPerm::grant(), &key, &None));
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::get_info(), &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::req_forced_op(),
&key,
&None
));
assert_perm_failed!(check_key_permission(&shell_ctx, KeyPerm::manage_blob(), &key, &None));
assert_perm_failed!(check_key_permission(&shell_ctx, KeyPerm::use_dev_id(), &key, &None));
assert_perm_failed!(check_key_permission(
0,
&shell_ctx,
KeyPerm::manage_blob(),
&key,
&None
));
assert_perm_failed!(check_key_permission(
0,
&shell_ctx,
KeyPerm::use_dev_id(),
&key,
&None
));
assert_perm_failed!(check_key_permission(
0,
&shell_ctx,
KeyPerm::gen_unique_id(),
&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::get_info()])
));
Ok(())
}
@ -753,27 +815,45 @@ mod tests {
};
if is_su {
assert!(check_key_permission(&sctx, KeyPerm::use_(), &key, &None).is_ok());
assert!(check_key_permission(&sctx, KeyPerm::delete(), &key, &None).is_ok());
assert!(check_key_permission(&sctx, KeyPerm::get_info(), &key, &None).is_ok());
assert!(check_key_permission(&sctx, KeyPerm::rebind(), &key, &None).is_ok());
assert!(check_key_permission(&sctx, KeyPerm::update(), &key, &None).is_ok());
assert!(check_key_permission(&sctx, KeyPerm::grant(), &key, &None).is_ok());
assert!(check_key_permission(&sctx, KeyPerm::manage_blob(), &key, &None).is_ok());
assert!(check_key_permission(&sctx, KeyPerm::use_dev_id(), &key, &None).is_ok());
assert!(check_key_permission(&sctx, KeyPerm::gen_unique_id(), &key, &None).is_ok());
assert!(check_key_permission(&sctx, KeyPerm::req_forced_op(), &key, &None).is_ok());
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::get_info(), &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());
assert!(check_key_permission(0, &sctx, KeyPerm::grant(), &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::manage_blob(), &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::use_dev_id(), &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::gen_unique_id(), &key, &None).is_ok());
assert!(check_key_permission(0, &sctx, KeyPerm::req_forced_op(), &key, &None).is_ok());
} else {
assert!(check_key_permission(&sctx, KeyPerm::use_(), &key, &None).is_ok());
assert!(check_key_permission(&sctx, KeyPerm::delete(), &key, &None).is_ok());
assert!(check_key_permission(&sctx, KeyPerm::get_info(), &key, &None).is_ok());
assert!(check_key_permission(&sctx, KeyPerm::rebind(), &key, &None).is_ok());
assert!(check_key_permission(&sctx, KeyPerm::update(), &key, &None).is_ok());
assert_perm_failed!(check_key_permission(&sctx, KeyPerm::grant(), &key, &None));
assert_perm_failed!(check_key_permission(&sctx, KeyPerm::req_forced_op(), &key, &None));
assert_perm_failed!(check_key_permission(&sctx, KeyPerm::manage_blob(), &key, &None));
assert_perm_failed!(check_key_permission(&sctx, KeyPerm::use_dev_id(), &key, &None));
assert_perm_failed!(check_key_permission(&sctx, KeyPerm::gen_unique_id(), &key, &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::get_info(), &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());
assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::grant(), &key, &None));
assert_perm_failed!(check_key_permission(
0,
&sctx,
KeyPerm::req_forced_op(),
&key,
&None
));
assert_perm_failed!(check_key_permission(
0,
&sctx,
KeyPerm::manage_blob(),
&key,
&None
));
assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::use_dev_id(), &key, &None));
assert_perm_failed!(check_key_permission(
0,
&sctx,
KeyPerm::gen_unique_id(),
&key,
&None
));
}
Ok(())
}
@ -789,9 +869,9 @@ mod tests {
};
if is_su {
check_key_permission(&sctx, KeyPerm::use_(), &key, &None)
check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None)
} else {
assert_perm_failed!(check_key_permission(&sctx, KeyPerm::use_(), &key, &None));
assert_perm_failed!(check_key_permission(0, &sctx, KeyPerm::use_(), &key, &None));
Ok(())
}
}
@ -803,6 +883,7 @@ mod tests {
assert_eq!(
Some(&KsError::sys()),
check_key_permission(
0,
&selinux::Context::new("ignored").unwrap(),
KeyPerm::use_(),
&key,

View file

@ -77,6 +77,7 @@ pub fn check_key_permission(
) -> anyhow::Result<()> {
ThreadState::with_calling_sid(|calling_sid| {
permission::check_key_permission(
ThreadState::get_calling_uid(),
&calling_sid
.ok_or_else(Error::sys)
.context("In check_key_permission: Cannot check permission without calling_sid.")?,