Add VTS test to check grants are deleted upon grantee user ID uninstall.

Bug: 372415590
Test: atest keystore2_client_tests
Change-Id: I67b68a6a3fc029a701c80bf8a9fb198ceb5eba3c
diff --git a/keystore2/tests/keystore2_client_grant_key_tests.rs b/keystore2/tests/keystore2_client_grant_key_tests.rs
index c171ab1..2494e81 100644
--- a/keystore2/tests/keystore2_client_grant_key_tests.rs
+++ b/keystore2/tests/keystore2_client_grant_key_tests.rs
@@ -18,6 +18,7 @@
 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
     Digest::Digest, KeyPurpose::KeyPurpose,
 };
+use android_security_maintenance::aidl::android::security::maintenance::IKeystoreMaintenance::IKeystoreMaintenance;
 use android_system_keystore2::aidl::android::system::keystore2::{
     Domain::Domain, IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor,
     KeyEntryResponse::KeyEntryResponse, KeyPermission::KeyPermission, ResponseCode::ResponseCode,
@@ -30,6 +31,8 @@
 use nix::unistd::getuid;
 use rustutils::users::AID_USER_OFFSET;
 
+static USER_MANAGER_SERVICE_NAME: &str = "android.security.maintenance";
+
 /// Produce a [`KeyDescriptor`] for a granted key.
 fn granted_key_descriptor(nspace: i64) -> KeyDescriptor {
     KeyDescriptor { domain: Domain::GRANT, nspace, alias: None, blob: None }
@@ -88,6 +91,10 @@
     Ok(())
 }
 
+fn get_maintenance() -> binder::Strong<dyn IKeystoreMaintenance> {
+    binder::get_interface(USER_MANAGER_SERVICE_NAME).unwrap()
+}
+
 /// Try to grant an SELINUX key with permission that does not map to any of the `KeyPermission`
 /// values.  An error is expected with values that does not map to set of permissions listed in
 /// `KeyPermission`.
@@ -600,6 +607,112 @@
     assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
 }
 
+/// Grant a key to a UID (user ID A + app B) then uninstall user ID A. Initialize a new user with
+/// the same user ID as the now-removed user. Check that app B for the new user can't load the key.
+#[test]
+fn grant_removed_when_grantee_user_id_removed() {
+    const GRANTOR_USER_ID: u32 = 97;
+    const GRANTOR_APPLICATION_ID: u32 = 10003;
+    static GRANTOR_UID: u32 = GRANTOR_USER_ID * AID_USER_OFFSET + GRANTOR_APPLICATION_ID;
+    static GRANTOR_GID: u32 = GRANTOR_UID;
+
+    const GRANTEE_USER_ID: u32 = 99;
+    const GRANTEE_APPLICATION_ID: u32 = 10001;
+    static GRANTEE_UID: u32 = GRANTEE_USER_ID * AID_USER_OFFSET + GRANTEE_APPLICATION_ID;
+    static GRANTEE_GID: u32 = GRANTEE_UID;
+
+    // Add a new user.
+    let create_grantee_user_id_fn = || {
+        let maint = get_maintenance();
+        maint.onUserAdded(GRANTEE_USER_ID.try_into().unwrap()).expect("failed to add user");
+    };
+
+    // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
+    // `--test-threads=1`), and nothing yet done with binder on the main thread.
+    unsafe { run_as::run_as_root(create_grantee_user_id_fn) };
+
+    // Child function to generate a key and grant it to GRANTEE_UID with `GET_INFO` permission.
+    let grantor_fn = || {
+        let sl = SecLevel::tee();
+        let access_vector = KeyPermission::GET_INFO.0;
+        let alias = format!("ks_grant_single_{}", getuid());
+        let mut grant_keys = generate_ec_key_and_grant_to_users(
+            &sl,
+            Some(alias),
+            vec![GRANTEE_UID.try_into().unwrap()],
+            access_vector,
+        )
+        .unwrap();
+
+        grant_keys.remove(0)
+    };
+
+    // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
+    // `--test-threads=1`), and nothing yet done with binder on the main thread.
+    let grant_key_nspace = unsafe { run_as::run_as_app(GRANTOR_UID, GRANTOR_GID, grantor_fn) };
+
+    // Child function for the grantee context: can load the granted key.
+    let grantee_fn = move || {
+        let keystore2 = get_keystore_service();
+        let rsp = get_granted_key(&keystore2, grant_key_nspace).expect("failed to get granted key");
+
+        // Return the underlying key ID to simulate an ID leak.
+        assert_eq!(rsp.metadata.key.domain, Domain::KEY_ID);
+        rsp.metadata.key.nspace
+    };
+
+    // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
+    // `--test-threads=1`), and nothing yet done with binder on the main thread.
+    let key_id = unsafe { run_as::run_as_app(GRANTEE_UID, GRANTEE_GID, grantee_fn) };
+
+    // Remove the grantee user and create a new user with the same user ID.
+    let overwrite_grantee_user_id_fn = || {
+        let maint = get_maintenance();
+        maint.onUserRemoved(GRANTEE_USER_ID.try_into().unwrap()).expect("failed to remove user");
+        maint.onUserAdded(GRANTEE_USER_ID.try_into().unwrap()).expect("failed to add user");
+    };
+
+    // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
+    // `--test-threads=1`), and nothing yet done with binder on the main thread.
+    unsafe { run_as::run_as_root(overwrite_grantee_user_id_fn) };
+
+    // Second context identified by <uid, grant_nspace> (where uid is the same because the
+    // now-deleted and newly-created grantee users have the same user ID) does not have access to
+    // the above granted key.
+    let new_grantee_fn = move || {
+        // Check that the key cannot be accessed via grant (i.e. KeyDescriptor with Domain::GRANT).
+        let keystore2 = get_keystore_service();
+        let result = get_granted_key(&keystore2, grant_key_nspace);
+        assert!(result.is_err());
+        assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+
+        // Check that the key also cannot be accessed via key ID (i.e. KeyDescriptor with
+        // Domain::KEY_ID) if the second context somehow gets a hold of it.
+        let result = map_ks_error(keystore2.getKeyEntry(&KeyDescriptor {
+            domain: Domain::KEY_ID,
+            nspace: key_id,
+            alias: None,
+            blob: None,
+        }));
+        assert!(result.is_err());
+        assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+    };
+
+    // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
+    // `--test-threads=1`), and nothing yet done with binder on the main thread.
+    unsafe { run_as::run_as_app(GRANTEE_UID, GRANTEE_GID, new_grantee_fn) };
+
+    // Clean up: remove grantee user.
+    let remove_grantee_user_id_fn = || {
+        let maint = get_maintenance();
+        maint.onUserRemoved(GRANTEE_USER_ID.try_into().unwrap()).expect("failed to remove user");
+    };
+
+    // Safety: only one thread at this point (enforced by `AndroidTest.xml` setting
+    // `--test-threads=1`), and nothing yet done with binder on the main thread.
+    unsafe { run_as::run_as_root(remove_grantee_user_id_fn) };
+}
+
 /// Grant an APP key to the user and immediately ungrant the granted key. In grantee context try to load
 /// the key. Grantee should fail to load the ungranted key with `KEY_NOT_FOUND` error response.
 #[test]