keystore: command to clear all keys for UID

Add ability for system UID to clear all entries for a different UID.

(cherry picked from commit a9bb549868035e05450a9b918f8d7de9deca5343)

Bug: 3020069
Change-Id: Ibd5ce287f024b89df3dd7bfc3a4e5f979a34c75c
diff --git a/keystore/IKeystoreService.cpp b/keystore/IKeystoreService.cpp
index 520d266..e899c12 100644
--- a/keystore/IKeystoreService.cpp
+++ b/keystore/IKeystoreService.cpp
@@ -528,6 +528,25 @@
         }
         return ret;
     }
+
+    virtual int32_t clear_uid(int64_t uid)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+        data.writeInt64(uid);
+        status_t status = remote()->transact(BnKeystoreService::CLEAR_UID, data, &reply);
+        if (status != NO_ERROR) {
+            ALOGD("clear_uid() could not contact remote: %d\n", status);
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        int32_t ret = reply.readInt32();
+        if (err < 0) {
+            ALOGD("clear_uid() caught exception %d\n", err);
+            return -1;
+        }
+        return ret;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(KeystoreService, "android.security.keystore");
@@ -797,6 +816,14 @@
             reply->writeInt32(ret);
             return NO_ERROR;
         }
+        case CLEAR_UID: {
+            CHECK_INTERFACE(IKeystoreService, data, reply);
+            int64_t uid = data.readInt64();
+            int32_t ret = clear_uid(uid);
+            reply->writeNoException();
+            reply->writeInt32(ret);
+            return NO_ERROR;
+        }
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/keystore/include/keystore/IKeystoreService.h b/keystore/include/keystore/IKeystoreService.h
index 6b2f406..050dd5b 100644
--- a/keystore/include/keystore/IKeystoreService.h
+++ b/keystore/include/keystore/IKeystoreService.h
@@ -51,6 +51,7 @@
         GETMTIME = IBinder::FIRST_CALL_TRANSACTION + 19,
         DUPLICATE = IBinder::FIRST_CALL_TRANSACTION + 20,
         IS_HARDWARE_BACKED = IBinder::FIRST_CALL_TRANSACTION + 21,
+        CLEAR_UID = IBinder::FIRST_CALL_TRANSACTION + 22,
     };
 
     DECLARE_META_INTERFACE(KeystoreService);
@@ -101,6 +102,8 @@
             int32_t destUid) = 0;
 
     virtual int32_t is_hardware_backed() = 0;
+
+    virtual int32_t clear_uid(int64_t uid) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/keystore/keystore.cpp b/keystore/keystore.cpp
index 438a8e4..a413948 100644
--- a/keystore/keystore.cpp
+++ b/keystore/keystore.cpp
@@ -137,6 +137,7 @@
     P_VERIFY    = 1 << 12,
     P_GRANT     = 1 << 13,
     P_DUPLICATE = 1 << 14,
+    P_CLEAR_UID = 1 << 15,
 } perm_t;
 
 static struct user_euid {
@@ -1656,6 +1657,70 @@
         return mKeyStore->isHardwareBacked() ? 1 : 0;
     }
 
+    int32_t clear_uid(int64_t targetUid) {
+        uid_t callingUid = IPCThreadState::self()->getCallingUid();
+        if (!has_permission(callingUid, P_CLEAR_UID)) {
+            ALOGW("permission denied for %d: clear_uid", callingUid);
+            return ::PERMISSION_DENIED;
+        }
+
+        State state = mKeyStore->getState();
+        if (!isKeystoreUnlocked(state)) {
+            ALOGD("calling clear_uid in state: %d", state);
+            return state;
+        }
+
+        const keymaster_device_t* device = mKeyStore->getDevice();
+        if (device == NULL) {
+            return ::SYSTEM_ERROR;
+        }
+
+        DIR* dir = opendir(".");
+        if (!dir) {
+            return ::SYSTEM_ERROR;
+        }
+
+        char filename[NAME_MAX];
+        int n = snprintf(filename, NAME_MAX, "%u_", static_cast<uid_t>(targetUid));
+        char *end = &filename[n];
+
+        ResponseCode rc = ::NO_ERROR;
+
+        struct dirent* file;
+        while ((file = readdir(dir)) != NULL) {
+            if (strncmp(filename, file->d_name, n)) {
+                continue;
+            }
+
+            String8 file8(&file->d_name[n]);
+            encode_key(end, file8);
+
+            Blob keyBlob;
+            if (mKeyStore->get(filename, &keyBlob, ::TYPE_ANY) != ::NO_ERROR) {
+                ALOGW("couldn't open %s", filename);
+                continue;
+            }
+
+            if (keyBlob.getType() == ::TYPE_KEY_PAIR) {
+                // A device doesn't have to implement delete_keypair.
+                if (device->delete_keypair != NULL) {
+                    if (device->delete_keypair(device, keyBlob.getValue(), keyBlob.getLength())) {
+                        rc = ::SYSTEM_ERROR;
+                        ALOGW("device couldn't remove %s", filename);
+                    }
+                }
+            }
+
+            if (unlink(filename) && errno != ENOENT) {
+                rc = ::SYSTEM_ERROR;
+                ALOGW("couldn't unlink %s", filename);
+            }
+        }
+        closedir(dir);
+
+        return rc;
+    }
+
 private:
     inline bool isKeystoreUnlocked(State state) {
         switch (state) {