Delete grants to an app when its namespace is cleared.
Currently, if there is a grant to user ID 1 for app ID A and then app
ID A is uninstalled, a new app installed for user ID 1 with the same
app ID A will inherit the grant, which would give it access to a key
it shouldn't have access to.
Bug: 372415590
Test: atest keystore2_test
Change-Id: I5d354019881185c6629dd36f4bcd1ed51a88bb52
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index aacaa92..8f5617f 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -2528,7 +2528,19 @@
);",
params![domain.0, namespace, KeyType::Client],
)
- .context("Trying to delete grants.")?;
+ .context(format!(
+ "Trying to delete grants issued for keys in domain {:?} and namespace {:?}.",
+ domain.0, namespace
+ ))?;
+ if domain == Domain::APP {
+ // Keystore uses the UID instead of the namespace argument for Domain::APP, so we
+ // just need to delete rows where grantee == namespace.
+ tx.execute("DELETE FROM persistent.grant WHERE grantee = ?;", params![namespace])
+ .context(format!(
+ "Trying to delete received grants for domain {:?} and namespace {:?}.",
+ domain.0, namespace
+ ))?;
+ }
tx.execute(
"DELETE FROM persistent.keyentry
WHERE domain = ? AND namespace = ? AND key_type = ?;",
diff --git a/keystore2/src/database/tests.rs b/keystore2/src/database/tests.rs
index 7482a1c..fdcf254 100644
--- a/keystore2/src/database/tests.rs
+++ b/keystore2/src/database/tests.rs
@@ -2126,6 +2126,72 @@
Ok(())
}
+#[test]
+fn test_unbind_keys_for_namespace_removes_received_grants() -> Result<()> {
+ const USER_ID_1: u32 = 1;
+ const APPLICATION_ID_1: u32 = 11;
+ const APPLICATION_ID_2: u32 = 22;
+ const UID_1_FOR_USER_ID_1: u32 = USER_ID_1 * AID_USER_OFFSET + APPLICATION_ID_1;
+ const UID_2_FOR_USER_ID_1: u32 = USER_ID_1 * AID_USER_OFFSET + APPLICATION_ID_2;
+
+ // Check that grants are removed for Domain::APP.
+ {
+ let mut db = new_test_db()?;
+
+ // Pretend two application IDs for user ID 1 were granted access to 1 key each.
+ db.conn.execute(
+ &format!(
+ "INSERT INTO persistent.grant (id, grantee, keyentryid, access_vector)
+ VALUES (1, {UID_1_FOR_USER_ID_1}, 111, 222), (2, {UID_2_FOR_USER_ID_1}, 333, 444);"
+ ),
+ [],
+ )?;
+ // Keystore uses the UID as the namespace for Domain::APP keys.
+ db.unbind_keys_for_namespace(Domain::APP, UID_1_FOR_USER_ID_1.into())?;
+
+ let mut stmt = db.conn.prepare("SELECT id, grantee FROM persistent.grant")?;
+ let mut rows =
+ stmt.query_map::<(i64, u32), _, _>([], |row| Ok((row.get(0)?, row.get(1)?)))?;
+
+ // The row for the grant to the namespace that was cleared (UID_1_FOR_USER_ID_1) should be
+ // deleted. The other row should be untouched.
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (2, UID_2_FOR_USER_ID_1));
+ assert!(rows.next().is_none());
+ }
+
+ // Check that grants aren't removed for Domain::SELINUX.
+ {
+ let mut db = new_test_db()?;
+
+ // Pretend two application IDs for user ID 1 were granted access to 1 key each.
+ db.conn.execute(
+ &format!(
+ "INSERT INTO persistent.grant (id, grantee, keyentryid, access_vector)
+ VALUES (1, {UID_1_FOR_USER_ID_1}, 111, 222), (2, {UID_2_FOR_USER_ID_1}, 333, 444);"
+ ),
+ [],
+ )?;
+ // Keystore uses the UID as the namespace for Domain::APP keys. Here we're passing in
+ // Domain::SELINUX, but still pass the UID as the "namespace" argument to make sure the
+ // code's logic is correct.
+ db.unbind_keys_for_namespace(Domain::SELINUX, UID_1_FOR_USER_ID_1.into())?;
+
+ let mut stmt = db.conn.prepare("SELECT id, grantee FROM persistent.grant")?;
+ let mut rows =
+ stmt.query_map::<(i64, u32), _, _>([], |row| Ok((row.get(0)?, row.get(1)?)))?;
+
+ // Both rows should still be present.
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (1, UID_1_FOR_USER_ID_1));
+ let r = rows.next().unwrap().unwrap();
+ assert_eq!(r, (2, UID_2_FOR_USER_ID_1));
+ assert!(rows.next().is_none());
+ }
+
+ Ok(())
+}
+
fn app_key_exists(db: &mut KeystoreDB, nspace: i64, alias: &str) -> Result<bool> {
db.key_exists(Domain::APP, nspace, alias, KeyType::Client)
}