Merge "List apps affected by secure user ID" into main
This commit is contained in:
commit
1841a55ebf
3 changed files with 242 additions and 3 deletions
|
@ -143,4 +143,22 @@ interface IKeystoreMaintenance {
|
||||||
* Tag::ROLLBACK_RESISTANCE may or may not be rendered unusable.
|
* Tag::ROLLBACK_RESISTANCE may or may not be rendered unusable.
|
||||||
*/
|
*/
|
||||||
void deleteAllKeys();
|
void deleteAllKeys();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of App UIDs that have keys associated with the given SID, under the
|
||||||
|
* given user ID.
|
||||||
|
* When a given user's LSKF is removed or biometric authentication methods are changed
|
||||||
|
* (addition of a fingerprint, for example), authentication-bound keys may be invalidated.
|
||||||
|
* This method allows the platform to find out which apps would be affected (for a given user)
|
||||||
|
* when a given user secure ID is removed.
|
||||||
|
* Callers require 'ChangeUser' permission.
|
||||||
|
*
|
||||||
|
* @param userId The affected user.
|
||||||
|
* @param sid The user secure ID - identifier of the authentication method.
|
||||||
|
*
|
||||||
|
* @return A list of APP UIDs, in the form of (AID + userId*AID_USER_OFFSET), that have
|
||||||
|
* keys auth-bound to the given SID. These values can be passed into the
|
||||||
|
* PackageManager for resolution.
|
||||||
|
*/
|
||||||
|
long[] getAppUidsAffectedBySid(in int userId, in long sid);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2910,6 +2910,75 @@ impl KeystoreDB {
|
||||||
})
|
})
|
||||||
.context(ks_err!())
|
.context(ks_err!())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a list of app UIDs that have keys authenticated by the given secure_user_id
|
||||||
|
/// (for the given user_id).
|
||||||
|
/// This is helpful for finding out which apps will have their keys invalidated when
|
||||||
|
/// the user changes biometrics enrollment or removes their LSKF.
|
||||||
|
pub fn get_app_uids_affected_by_sid(
|
||||||
|
&mut self,
|
||||||
|
user_id: i32,
|
||||||
|
secure_user_id: i64,
|
||||||
|
) -> Result<Vec<i64>> {
|
||||||
|
let _wp = wd::watch_millis("KeystoreDB::get_app_uids_affected_by_sid", 500);
|
||||||
|
|
||||||
|
let key_ids_and_app_uids = self.with_transaction(TransactionBehavior::Immediate, |tx| {
|
||||||
|
let mut stmt = tx
|
||||||
|
.prepare(&format!(
|
||||||
|
"SELECT id, namespace from persistent.keyentry
|
||||||
|
WHERE key_type = ?
|
||||||
|
AND domain = ?
|
||||||
|
AND cast ( (namespace/{AID_USER_OFFSET}) as int) = ?
|
||||||
|
AND state = ?;",
|
||||||
|
))
|
||||||
|
.context(concat!(
|
||||||
|
"In get_app_uids_affected_by_sid, ",
|
||||||
|
"failed to prepare the query to find the keys created by apps."
|
||||||
|
))?;
|
||||||
|
|
||||||
|
let mut rows = stmt
|
||||||
|
.query(params![KeyType::Client, Domain::APP.0 as u32, user_id, KeyLifeCycle::Live,])
|
||||||
|
.context(ks_err!("Failed to query the keys created by apps."))?;
|
||||||
|
|
||||||
|
let mut key_ids_and_app_uids: HashMap<i64, i64> = Default::default();
|
||||||
|
db_utils::with_rows_extract_all(&mut rows, |row| {
|
||||||
|
key_ids_and_app_uids.insert(
|
||||||
|
row.get(0).context("Failed to read key id of a key created by an app.")?,
|
||||||
|
row.get(1).context("Failed to read the app uid")?,
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
Ok(key_ids_and_app_uids).no_gc()
|
||||||
|
})?;
|
||||||
|
let mut app_uids_affected_by_sid: HashSet<i64> = Default::default();
|
||||||
|
for (key_id, app_uid) in key_ids_and_app_uids {
|
||||||
|
// Read the key parameters for each key in its own transaction. It is OK to ignore
|
||||||
|
// an error to get the properties of a particular key since it might have been deleted
|
||||||
|
// under our feet after the previous transaction concluded. If the key was deleted
|
||||||
|
// then it is no longer applicable if it was auth-bound or not.
|
||||||
|
if let Ok(is_key_bound_to_sid) =
|
||||||
|
self.with_transaction(TransactionBehavior::Immediate, |tx| {
|
||||||
|
let params = Self::load_key_parameters(key_id, tx)
|
||||||
|
.context("Failed to load key parameters.")?;
|
||||||
|
// Check if the key is bound to this secure user ID.
|
||||||
|
let is_key_bound_to_sid = params.iter().any(|kp| {
|
||||||
|
matches!(
|
||||||
|
kp.key_parameter_value(),
|
||||||
|
KeyParameterValue::UserSecureID(sid) if *sid == secure_user_id
|
||||||
|
)
|
||||||
|
});
|
||||||
|
Ok(is_key_bound_to_sid).no_gc()
|
||||||
|
})
|
||||||
|
{
|
||||||
|
if is_key_bound_to_sid {
|
||||||
|
app_uids_affected_by_sid.insert(app_uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let app_uids_vec: Vec<i64> = app_uids_affected_by_sid.into_iter().collect();
|
||||||
|
Ok(app_uids_vec)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -4492,10 +4561,17 @@ pub mod tests {
|
||||||
.collect::<Result<Vec<_>>>()
|
.collect::<Result<Vec<_>>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> {
|
||||||
|
make_test_params_with_sids(max_usage_count, &[42])
|
||||||
|
}
|
||||||
|
|
||||||
// Note: The parameters and SecurityLevel associations are nonsensical. This
|
// Note: The parameters and SecurityLevel associations are nonsensical. This
|
||||||
// collection is only used to check if the parameters are preserved as expected by the
|
// collection is only used to check if the parameters are preserved as expected by the
|
||||||
// database.
|
// database.
|
||||||
fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> {
|
fn make_test_params_with_sids(
|
||||||
|
max_usage_count: Option<i32>,
|
||||||
|
user_secure_ids: &[i64],
|
||||||
|
) -> Vec<KeyParameter> {
|
||||||
let mut params = vec![
|
let mut params = vec![
|
||||||
KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::TRUSTED_ENVIRONMENT),
|
KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::TRUSTED_ENVIRONMENT),
|
||||||
KeyParameter::new(
|
KeyParameter::new(
|
||||||
|
@ -4594,7 +4670,6 @@ pub mod tests {
|
||||||
SecurityLevel::TRUSTED_ENVIRONMENT,
|
SecurityLevel::TRUSTED_ENVIRONMENT,
|
||||||
),
|
),
|
||||||
KeyParameter::new(KeyParameterValue::UserID(1), SecurityLevel::STRONGBOX),
|
KeyParameter::new(KeyParameterValue::UserID(1), SecurityLevel::STRONGBOX),
|
||||||
KeyParameter::new(KeyParameterValue::UserSecureID(42), SecurityLevel::STRONGBOX),
|
|
||||||
KeyParameter::new(
|
KeyParameter::new(
|
||||||
KeyParameterValue::NoAuthRequired,
|
KeyParameterValue::NoAuthRequired,
|
||||||
SecurityLevel::TRUSTED_ENVIRONMENT,
|
SecurityLevel::TRUSTED_ENVIRONMENT,
|
||||||
|
@ -4722,6 +4797,13 @@ pub mod tests {
|
||||||
SecurityLevel::SOFTWARE,
|
SecurityLevel::SOFTWARE,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for sid in user_secure_ids.iter() {
|
||||||
|
params.push(KeyParameter::new(
|
||||||
|
KeyParameterValue::UserSecureID(*sid),
|
||||||
|
SecurityLevel::STRONGBOX,
|
||||||
|
));
|
||||||
|
}
|
||||||
params
|
params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4731,6 +4813,17 @@ pub mod tests {
|
||||||
namespace: i64,
|
namespace: i64,
|
||||||
alias: &str,
|
alias: &str,
|
||||||
max_usage_count: Option<i32>,
|
max_usage_count: Option<i32>,
|
||||||
|
) -> Result<KeyIdGuard> {
|
||||||
|
make_test_key_entry_with_sids(db, domain, namespace, alias, max_usage_count, &[42])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn make_test_key_entry_with_sids(
|
||||||
|
db: &mut KeystoreDB,
|
||||||
|
domain: Domain,
|
||||||
|
namespace: i64,
|
||||||
|
alias: &str,
|
||||||
|
max_usage_count: Option<i32>,
|
||||||
|
sids: &[i64],
|
||||||
) -> Result<KeyIdGuard> {
|
) -> Result<KeyIdGuard> {
|
||||||
let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
|
let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
|
||||||
let mut blob_metadata = BlobMetaData::new();
|
let mut blob_metadata = BlobMetaData::new();
|
||||||
|
@ -4749,7 +4842,7 @@ pub mod tests {
|
||||||
db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
|
db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
|
||||||
db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
|
db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
|
||||||
|
|
||||||
let params = make_test_params(max_usage_count);
|
let params = make_test_params_with_sids(max_usage_count, sids);
|
||||||
db.insert_keyparameter(&key_id, ¶ms)?;
|
db.insert_keyparameter(&key_id, ¶ms)?;
|
||||||
|
|
||||||
let mut metadata = KeyMetaData::new();
|
let mut metadata = KeyMetaData::new();
|
||||||
|
@ -5418,4 +5511,111 @@ pub mod tests {
|
||||||
assert_eq!(db.load_key_descriptor(key_id + 1)?, None);
|
assert_eq!(db.load_key_descriptor(key_id + 1)?, None);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_list_app_uids_for_sid() -> Result<()> {
|
||||||
|
let uid: i32 = 1;
|
||||||
|
let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
|
||||||
|
let first_sid = 667;
|
||||||
|
let second_sid = 669;
|
||||||
|
let first_app_id: i64 = 123 + uid_offset;
|
||||||
|
let second_app_id: i64 = 456 + uid_offset;
|
||||||
|
let third_app_id: i64 = 789 + uid_offset;
|
||||||
|
let unrelated_app_id: i64 = 1011 + uid_offset;
|
||||||
|
let mut db = new_test_db()?;
|
||||||
|
make_test_key_entry_with_sids(
|
||||||
|
&mut db,
|
||||||
|
Domain::APP,
|
||||||
|
first_app_id,
|
||||||
|
TEST_ALIAS,
|
||||||
|
None,
|
||||||
|
&[first_sid],
|
||||||
|
)
|
||||||
|
.context("test_get_list_app_uids_for_sid")?;
|
||||||
|
make_test_key_entry_with_sids(
|
||||||
|
&mut db,
|
||||||
|
Domain::APP,
|
||||||
|
second_app_id,
|
||||||
|
"alias2",
|
||||||
|
None,
|
||||||
|
&[first_sid],
|
||||||
|
)
|
||||||
|
.context("test_get_list_app_uids_for_sid")?;
|
||||||
|
make_test_key_entry_with_sids(
|
||||||
|
&mut db,
|
||||||
|
Domain::APP,
|
||||||
|
second_app_id,
|
||||||
|
TEST_ALIAS,
|
||||||
|
None,
|
||||||
|
&[second_sid],
|
||||||
|
)
|
||||||
|
.context("test_get_list_app_uids_for_sid")?;
|
||||||
|
make_test_key_entry_with_sids(
|
||||||
|
&mut db,
|
||||||
|
Domain::APP,
|
||||||
|
third_app_id,
|
||||||
|
"alias3",
|
||||||
|
None,
|
||||||
|
&[second_sid],
|
||||||
|
)
|
||||||
|
.context("test_get_list_app_uids_for_sid")?;
|
||||||
|
make_test_key_entry_with_sids(
|
||||||
|
&mut db,
|
||||||
|
Domain::APP,
|
||||||
|
unrelated_app_id,
|
||||||
|
TEST_ALIAS,
|
||||||
|
None,
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.context("test_get_list_app_uids_for_sid")?;
|
||||||
|
|
||||||
|
let mut first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
|
||||||
|
first_sid_apps.sort();
|
||||||
|
assert_eq!(first_sid_apps, vec![first_app_id, second_app_id]);
|
||||||
|
let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
|
||||||
|
second_sid_apps.sort();
|
||||||
|
assert_eq!(second_sid_apps, vec![second_app_id, third_app_id]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_get_list_app_uids_with_multiple_sids() -> Result<()> {
|
||||||
|
let uid: i32 = 1;
|
||||||
|
let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
|
||||||
|
let first_sid = 667;
|
||||||
|
let second_sid = 669;
|
||||||
|
let third_sid = 772;
|
||||||
|
let first_app_id: i64 = 123 + uid_offset;
|
||||||
|
let second_app_id: i64 = 456 + uid_offset;
|
||||||
|
let mut db = new_test_db()?;
|
||||||
|
make_test_key_entry_with_sids(
|
||||||
|
&mut db,
|
||||||
|
Domain::APP,
|
||||||
|
first_app_id,
|
||||||
|
TEST_ALIAS,
|
||||||
|
None,
|
||||||
|
&[first_sid, second_sid],
|
||||||
|
)
|
||||||
|
.context("test_get_list_app_uids_for_sid")?;
|
||||||
|
make_test_key_entry_with_sids(
|
||||||
|
&mut db,
|
||||||
|
Domain::APP,
|
||||||
|
second_app_id,
|
||||||
|
"alias2",
|
||||||
|
None,
|
||||||
|
&[second_sid, third_sid],
|
||||||
|
)
|
||||||
|
.context("test_get_list_app_uids_for_sid")?;
|
||||||
|
|
||||||
|
let first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
|
||||||
|
assert_eq!(first_sid_apps, vec![first_app_id]);
|
||||||
|
|
||||||
|
let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
|
||||||
|
second_sid_apps.sort();
|
||||||
|
assert_eq!(second_sid_apps, vec![first_app_id, second_app_id]);
|
||||||
|
|
||||||
|
let third_sid_apps = db.get_app_uids_affected_by_sid(uid, third_sid)?;
|
||||||
|
assert_eq!(third_sid_apps, vec![second_app_id]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -286,6 +286,17 @@ impl Maintenance {
|
||||||
|
|
||||||
Maintenance::call_on_all_security_levels("deleteAllKeys", |dev| dev.deleteAllKeys())
|
Maintenance::call_on_all_security_levels("deleteAllKeys", |dev| dev.deleteAllKeys())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_app_uids_affected_by_sid(
|
||||||
|
user_id: i32,
|
||||||
|
secure_user_id: i64,
|
||||||
|
) -> Result<std::vec::Vec<i64>> {
|
||||||
|
// This method is intended to be called by Settings and discloses a list of apps
|
||||||
|
// associated with a user, so it requires the ChangeUser permission.
|
||||||
|
check_keystore_permission(KeystorePerm::ChangeUser).context(ks_err!())?;
|
||||||
|
DB.with(|db| db.borrow_mut().get_app_uids_affected_by_sid(user_id, secure_user_id))
|
||||||
|
.context(ks_err!("Failed to get app UIDs affected by SID"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interface for Maintenance {}
|
impl Interface for Maintenance {}
|
||||||
|
@ -363,4 +374,14 @@ impl IKeystoreMaintenance for Maintenance {
|
||||||
let _wp = wd::watch_millis("IKeystoreMaintenance::deleteAllKeys", 500);
|
let _wp = wd::watch_millis("IKeystoreMaintenance::deleteAllKeys", 500);
|
||||||
map_or_log_err(Self::delete_all_keys(), Ok)
|
map_or_log_err(Self::delete_all_keys(), Ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn getAppUidsAffectedBySid(
|
||||||
|
&self,
|
||||||
|
user_id: i32,
|
||||||
|
secure_user_id: i64,
|
||||||
|
) -> BinderResult<std::vec::Vec<i64>> {
|
||||||
|
log::info!("getAppUidsAffectedBySid(secure_user_id={secure_user_id:?})");
|
||||||
|
let _wp = wd::watch_millis("IKeystoreMaintenance::getAppUidsAffectedBySid", 500);
|
||||||
|
map_or_log_err(Self::get_app_uids_affected_by_sid(user_id, secure_user_id), Ok)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue