Remove functionality to migrate keys across app UID
- Remove the listEntries API in IKeystoreMaintenance
- Remove the ability to migrate keys across UIDs in the APP domain
Test: m keystore2
Bug: 220015249
Change-Id: I513e5b485b026825b9e7f8c86a1e8fb89247ab3d
diff --git a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
index 3df5936..6a37c78 100644
--- a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
+++ b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
@@ -23,15 +23,10 @@
* user's password.
* @hide
*/
-@SensitiveData
+ @SensitiveData
interface IKeystoreMaintenance {
/**
- * Special value indicating the callers uid.
- */
- const int UID_SELF = -1;
-
- /**
* Allows LockSettingsService to inform keystore about adding a new user.
* Callers require 'AddUser' permission.
*
@@ -120,10 +115,6 @@
* The source may be specified by Domain::APP, Domain::SELINUX, or Domain::KEY_ID. The target
* may be specified by Domain::APP or Domain::SELINUX.
*
- * If Domain::APP is selected in either source or destination, nspace must be set to UID_SELF,
- * implying the caller's UID. If the caller has the MIGRATE_ANY_KEY permission, Domain::APP may
- * be used with other nspace values which then indicates the UID of a different application.
- *
* ## Error conditions:
* `ResponseCode::PERMISSION_DENIED` - If the caller lacks any of the required permissions.
* `ResponseCode::KEY_NOT_FOUND` - If the source did not exist.
@@ -140,22 +131,4 @@
* Tag::ROLLBACK_RESISTANCE may or may not be rendered unusable.
*/
void deleteAllKeys();
-
- /**
- * List all entries accessible by the caller in the given `domain` and `nspace`.
- *
- * Callers either has to have the `GET_INFO` permission for the requested namespace or `LIST`
- * permission to list all the entries.
- *
- * ## Error conditions
- * `ResponseCode::INVALID_ARGUMENT` if `domain` is other than `Domain::APP` or `Domain::SELINUX`
- * `ResponseCode::PERMISSION_DENIED` if the caller does not have the permission
- *
- * @param domain `Domain::APP` or `Domain::SELINUX`.
- *
- * @param nspace The SELinux keystore2_key namespace.
- *
- * @return List of KeyDescriptors.
- */
- KeyDescriptor[] listEntries(in Domain domain, in long nspace);
}
diff --git a/keystore2/src/legacy_importer.rs b/keystore2/src/legacy_importer.rs
index 5a64020..93e1735 100644
--- a/keystore2/src/legacy_importer.rs
+++ b/keystore2/src/legacy_importer.rs
@@ -280,116 +280,6 @@
result
}
- /// This function behaves exactly like with_try_import unless the src_key has an encrypted
- /// component (other than the key blob itself [1]) and super_key is None.
- /// In that case the files belonging to the src_key will be renamed to be moved to the
- /// namespace indicated by dst_key. The destination domain must be in Domain::APP.
- ///
- /// [1] Components that cannot be encrypted with the super key in keystore2 include the
- /// characteristics file, which was encrypted before Android Q, and certificate entries
- /// added by KeyChain before Android Q.
- pub fn with_try_import_or_migrate_namespaces<F, T>(
- &self,
- src: (u32, &KeyDescriptor),
- dest: (u32, &KeyDescriptor),
- super_key: Option<Arc<dyn AesGcm + Send + Sync>>,
- has_migrate_any_permission: bool,
- key_accessor: F,
- ) -> Result<Option<T>>
- where
- F: Fn() -> Result<T>,
- {
- let _wp = wd::watch_millis("LegacyImporter::with_try_import_or_migrate_namespaces", 500);
-
- let (src_uid, src_key) = src;
- let (dest_uid, dest_key) = dest;
-
- // Access the key and return on success.
- match key_accessor() {
- Ok(result) => return Ok(Some(result)),
- Err(e) => {
- if e.root_cause().downcast_ref::<Error>()
- != Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND))
- {
- return Err(e);
- }
- }
- }
-
- // Filter inputs. We can only load legacy app domain keys as well
- // as the SELINUX WIFI_NAMESPACE, which will be populated from AID_WIFI.
- let src_uid = match src_key {
- KeyDescriptor { domain: Domain::APP, alias: Some(_), .. } => src_uid,
- KeyDescriptor { domain: Domain::SELINUX, nspace, alias: Some(_), .. } => {
- match *nspace {
- Self::WIFI_NAMESPACE => Self::AID_WIFI,
- _ => {
- return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
- .context(format!("No legacy keys for namespace {}", nspace))
- }
- }
- }
- _ => {
- return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
- .context("No legacy keys for key descriptor.")
- }
- };
-
- let dest_uid = match dest_key {
- KeyDescriptor { domain: Domain::APP, alias: Some(_), .. } => Some(dest_uid),
- KeyDescriptor { domain: Domain::SELINUX, alias: Some(_), .. } => {
- // Domain::SELINUX cannot be migrated in place, but we cannot fail at this point
- // because the import may succeed at which point the actual migration will
- // be performed by the caller.
- None
- }
- _ => {
- return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
- .context("No legacy keys for key descriptor.")
- }
- };
-
- let src_key_clone = src_key.clone();
- let dest_key_clone = dest_key.clone();
- let result = self.do_serialized(move |importer_state| {
- let super_key = super_key.map(|sk| -> Arc<dyn AesGcm> { sk });
- match (
- importer_state.check_and_import(src_uid, src_key_clone.clone(), super_key),
- dest_uid,
- ) {
- // The import into the database was successful. Return Ok(true)
- (Ok(()), _) => Ok(true),
- // The import failed because a certificate and/or characteristics
- // file was encrypted and no super_key was available. Migration within the
- // legacy database is attempted and Ok(false) is returned on success.
- (Err(e), Some(dest_uid))
- if has_migrate_any_permission
- && e.root_cause().downcast_ref::<Error>()
- == Some(&Error::Rc(ResponseCode::LOCKED)) =>
- {
- importer_state
- .migrate_namespaces(src_uid, dest_uid, src_key_clone, dest_key_clone)
- .map(|_| false)
- }
- (Err(e), _) => Err(e),
- }
- });
-
- match result {
- None => {
- Err(Error::Rc(ResponseCode::KEY_NOT_FOUND)).context("Legacy database is empty.")
- }
-
- Some(Ok(true)) => {
- // After successful import try again.
- key_accessor().map(|v| Some(v))
- }
- // The entry was successfully migrated within the legacy database.
- Some(Ok(false)) => Ok(None),
- Some(Err(e)) => Err(e),
- }
- }
-
/// Runs the key_accessor function and returns its result. If it returns an error and the
/// root cause was KEY_NOT_FOUND, tries to import a key with the given parameters from
/// the legacy database to the new database and runs the key_accessor function again if
@@ -544,39 +434,6 @@
.context("In list_uid: Trying to list legacy entries.")
}
- fn migrate_namespaces(
- &mut self,
- src_uid: u32,
- dest_uid: u32,
- src_key: KeyDescriptor,
- dest_key: KeyDescriptor,
- ) -> Result<()> {
- let src_alias = src_key.alias.ok_or_else(|| {
- anyhow::anyhow!(Error::sys()).context(
- "In legacy_migrator::migrate_namespace: src_key.alias must be Some because \
- our caller must not have called us otherwise.",
- )
- })?;
-
- if dest_key.domain != Domain::APP {
- return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
- "In legacy_migrator::migrate_namespace: \
- Legacy in-place migration to SELinux namespace is not supported.",
- );
- }
-
- let dest_alias = dest_key.alias.ok_or_else(|| {
- anyhow::anyhow!(Error::sys()).context(concat!(
- "In legacy_migrator::migrate_namespace: dest_key.alias must be Some because ",
- "our caller must not have called us otherwise."
- ))
- })?;
-
- self.legacy_loader
- .move_keystore_entry(src_uid, dest_uid, &src_alias, &dest_alias)
- .context("In legacy_migrator::migrate_namespace: Moving key entry files.")
- }
-
/// Checks if the key can potentially be unlocked. And deletes the key entry otherwise.
/// If the super_key has already been imported, the super key database id is returned.
fn get_super_key_id_check_unlockable_or_delete(
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 0d637d8..1fca5d9 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -23,14 +23,13 @@
use crate::permission::{KeyPerm, KeystorePerm};
use crate::super_key::{SuperKeyManager, UserState};
use crate::utils::{
- check_key_permission, check_keystore_permission, list_key_entries, uid_to_android_user,
- watchdog as wd,
+ check_key_permission, check_keystore_permission, uid_to_android_user, watchdog as wd,
};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
IKeyMintDevice::IKeyMintDevice, SecurityLevel::SecurityLevel,
};
use android_security_maintenance::aidl::android::security::maintenance::{
- IKeystoreMaintenance::{BnKeystoreMaintenance, IKeystoreMaintenance, UID_SELF},
+ IKeystoreMaintenance::{BnKeystoreMaintenance, IKeystoreMaintenance},
UserState::UserState as AidlUserState,
};
use android_security_maintenance::binder::{
@@ -40,7 +39,6 @@
use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
use anyhow::{Context, Result};
use keystore2_crypto::Password;
-use keystore2_selinux as selinux;
/// Reexport Domain for the benefit of DeleteListener
pub use android_system_keystore2::aidl::android::system::keystore2::Domain::Domain;
@@ -225,15 +223,10 @@
}
fn migrate_key_namespace(source: &KeyDescriptor, destination: &KeyDescriptor) -> Result<()> {
- let migrate_any_key_permission =
- check_keystore_permission(KeystorePerm::MigrateAnyKey).is_ok();
+ let calling_uid = ThreadState::get_calling_uid();
- let src_uid = match source.domain {
- Domain::SELINUX | Domain::KEY_ID => ThreadState::get_calling_uid(),
- Domain::APP if source.nspace == UID_SELF.into() => ThreadState::get_calling_uid(),
- Domain::APP if source.nspace != UID_SELF.into() && migrate_any_key_permission => {
- source.nspace as u32
- }
+ match source.domain {
+ Domain::SELINUX | Domain::KEY_ID | Domain::APP => (),
_ => {
return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
"In migrate_key_namespace: \
@@ -242,12 +235,8 @@
}
};
- let dest_uid = match destination.domain {
- Domain::SELINUX => ThreadState::get_calling_uid(),
- Domain::APP if destination.nspace == UID_SELF.into() => ThreadState::get_calling_uid(),
- Domain::APP if destination.nspace != UID_SELF.into() && migrate_any_key_permission => {
- destination.nspace as u32
- }
+ match destination.domain {
+ Domain::SELINUX | Domain::APP => (),
_ => {
return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
"In migrate_key_namespace: \
@@ -256,54 +245,30 @@
}
};
- let user_id = uid_to_android_user(dest_uid);
-
- if user_id != uid_to_android_user(src_uid)
- && (source.domain == Domain::APP || destination.domain == Domain::APP)
- {
- return Err(Error::sys()).context(
- "In migrate_key_namespace: Keys cannot be migrated across android users.",
- );
- }
+ let user_id = uid_to_android_user(calling_uid);
let super_key = SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(user_id);
DB.with(|db| {
- if let Some((key_id_guard, _)) = LEGACY_IMPORTER
- .with_try_import_or_migrate_namespaces(
- (src_uid, source),
- (dest_uid, destination),
- super_key,
- migrate_any_key_permission,
- || {
- db.borrow_mut().load_key_entry(
- source,
- KeyType::Client,
- KeyEntryLoadBits::NONE,
- src_uid,
- |k, av| {
- if migrate_any_key_permission {
- Ok(())
- } else {
- check_key_permission(KeyPerm::Use, k, &av)?;
- check_key_permission(KeyPerm::Delete, k, &av)?;
- check_key_permission(KeyPerm::Grant, k, &av)
- }
- },
- )
- },
- )
- .context("In migrate_key_namespace: Failed to load key blob.")?
- {
- db.borrow_mut().migrate_key_namespace(key_id_guard, destination, dest_uid, |k| {
- if migrate_any_key_permission {
- Ok(())
- } else {
- check_key_permission(KeyPerm::Rebind, k, &None)
- }
+ let (key_id_guard, _) = LEGACY_IMPORTER
+ .with_try_import(source, calling_uid, super_key, || {
+ db.borrow_mut().load_key_entry(
+ source,
+ KeyType::Client,
+ KeyEntryLoadBits::NONE,
+ calling_uid,
+ |k, av| {
+ check_key_permission(KeyPerm::Use, k, &av)?;
+ check_key_permission(KeyPerm::Delete, k, &av)?;
+ check_key_permission(KeyPerm::Grant, k, &av)
+ },
+ )
})
- } else {
- Ok(())
+ .context("In migrate_key_namespace: Failed to load key blob.")?;
+ {
+ db.borrow_mut().migrate_key_namespace(key_id_guard, destination, calling_uid, |k| {
+ check_key_permission(KeyPerm::Rebind, k, &None)
+ })
}
})
}
@@ -316,30 +281,6 @@
Maintenance::call_on_all_security_levels("deleteAllKeys", |dev| dev.deleteAllKeys())
}
-
- fn list_entries(domain: Domain, nspace: i64) -> Result<Vec<KeyDescriptor>> {
- let k = match domain {
- Domain::APP | Domain::SELINUX => KeyDescriptor{domain, nspace, ..Default::default()},
- _ => return Err(Error::perm()).context(
- "In list_entries: List entries is only supported for Domain::APP and Domain::SELINUX."
- ),
- };
-
- // The caller has to have either GetInfo for the namespace or List permission
- check_key_permission(KeyPerm::GetInfo, &k, &None)
- .or_else(|e| {
- if Some(&selinux::Error::PermissionDenied)
- == e.root_cause().downcast_ref::<selinux::Error>()
- {
- check_keystore_permission(KeystorePerm::List)
- } else {
- Err(e)
- }
- })
- .context("In list_entries: While checking key and keystore permission.")?;
-
- DB.with(|db| list_key_entries(&mut db.borrow_mut(), domain, nspace))
- }
}
impl Interface for Maintenance {}
@@ -389,11 +330,6 @@
map_or_log_err(Self::migrate_key_namespace(source, destination), Ok)
}
- fn listEntries(&self, domain: Domain, namespace: i64) -> BinderResult<Vec<KeyDescriptor>> {
- let _wp = wd::watch_millis("IKeystoreMaintenance::listEntries", 500);
- map_or_log_err(Self::list_entries(domain, namespace), Ok)
- }
-
fn deleteAllKeys(&self) -> BinderResult<()> {
let _wp = wd::watch_millis("IKeystoreMaintenance::deleteAllKeys", 500);
map_or_log_err(Self::delete_all_keys(), Ok)
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index 1e6f10a..22509c4 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -145,10 +145,6 @@
/// Checked when IKeystoreMaintenance::deleteAllKeys is called.
#[selinux(name = delete_all_keys)]
DeleteAllKeys,
- /// Checked when migrating any key from any namespace to any other namespace. It was
- /// introduced for migrating keys when an app leaves a sharedUserId.
- #[selinux(name = migrate_any_key)]
- MigrateAnyKey,
/// Checked on calls to IRemotelyProvisionedKeyPool::getAttestationKey
#[selinux(name = get_attestation_key)]
GetAttestationKey,