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/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) {