keystore: add "migrate" command

To support the WiFi service, we need to support migration from the
system UID to the wifi UID. This adds a command to achieve the
migration.

Bug: 8122243
Change-Id: I31e2ba3b3a92c582a6f8d71bbb139c408c06814f
diff --git a/keystore/IKeystoreService.cpp b/keystore/IKeystoreService.cpp
index 9db6696..d1df44e 100644
--- a/keystore/IKeystoreService.cpp
+++ b/keystore/IKeystoreService.cpp
@@ -487,6 +487,26 @@
         }
         return ret;
     }
+
+    virtual int32_t migrate(const String16& name, int32_t targetUid)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+        data.writeString16(name);
+        data.writeInt32(targetUid);
+        status_t status = remote()->transact(BnKeystoreService::MIGRATE, data, &reply);
+        if (status != NO_ERROR) {
+            ALOGD("migrate() could not contact remote: %d\n", status);
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        int32_t ret = reply.readInt32();
+        if (err < 0) {
+            ALOGD("migrate() caught exception %d\n", err);
+            return -1;
+        }
+        return ret;
+    }
 };
 
 IMPLEMENT_META_INTERFACE(KeystoreService, "android.security.keystore");
@@ -738,6 +758,15 @@
             reply->writeInt64(ret);
             return NO_ERROR;
         } break;
+        case MIGRATE: {
+            CHECK_INTERFACE(IKeystoreService, data, reply);
+            String16 name = data.readString16();
+            int32_t targetUid = data.readInt32();
+            int32_t ret = migrate(name, targetUid);
+            reply->writeNoException();
+            reply->writeInt32(ret);
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/keystore/include/keystore/IKeystoreService.h b/keystore/include/keystore/IKeystoreService.h
index b4560b9..20ba9c5 100644
--- a/keystore/include/keystore/IKeystoreService.h
+++ b/keystore/include/keystore/IKeystoreService.h
@@ -49,6 +49,7 @@
         GRANT = IBinder::FIRST_CALL_TRANSACTION + 17,
         UNGRANT = IBinder::FIRST_CALL_TRANSACTION + 18,
         GETMTIME = IBinder::FIRST_CALL_TRANSACTION + 19,
+        MIGRATE = IBinder::FIRST_CALL_TRANSACTION + 20,
     };
 
     DECLARE_META_INTERFACE(KeystoreService);
@@ -94,6 +95,8 @@
     virtual int32_t ungrant(const String16& name, int32_t granteeUid) = 0;
 
     virtual int64_t getmtime(const String16& name) = 0;
+
+    virtual int32_t migrate(const String16& name, int32_t targetUid) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/keystore/keystore.cpp b/keystore/keystore.cpp
index 09bcf33..0b26bc8 100644
--- a/keystore/keystore.cpp
+++ b/keystore/keystore.cpp
@@ -136,6 +136,7 @@
     P_SIGN     = 1 << 11,
     P_VERIFY   = 1 << 12,
     P_GRANT    = 1 << 13,
+    P_MIGRATE  = 1 << 14,
 } perm_t;
 
 static struct user_euid {
@@ -1584,6 +1585,51 @@
         return static_cast<int64_t>(s.st_mtime);
     }
 
+    int32_t migrate(const String16& name, int32_t targetUid) {
+        uid_t callingUid = IPCThreadState::self()->getCallingUid();
+        if (!has_permission(callingUid, P_MIGRATE)) {
+            ALOGW("permission denied for %d: migrate", callingUid);
+            return -1L;
+        }
+
+        State state = mKeyStore->getState();
+        if (!isKeystoreUnlocked(state)) {
+            ALOGD("calling migrate in state: %d", state);
+            return state;
+        }
+
+        if (!is_granted_to(callingUid, targetUid)) {
+            ALOGD("migrate not granted: %d -> %d", callingUid, targetUid);
+            return ::PERMISSION_DENIED;
+        }
+
+        String8 source8(name);
+        char source[NAME_MAX];
+
+        encode_key_for_uid(source, callingUid, source8);
+
+        if (access(source, W_OK) == -1) {
+            return (errno != ENOENT) ? ::SYSTEM_ERROR : ::KEY_NOT_FOUND;
+        }
+
+        String8 target8(name);
+        char target[NAME_MAX];
+
+        encode_key_for_uid(target, targetUid, target8);
+
+        // Make sure the target doesn't exist
+        if (access(target, R_OK) == 0 || errno != ENOENT) {
+            ALOGD("migrate target already exists: %s", strerror(errno));
+            return ::SYSTEM_ERROR;
+        }
+
+        if (rename(source, target)) {
+            ALOGD("migrate could not rename: %s", strerror(errno));
+            return ::SYSTEM_ERROR;
+        }
+        return ::NO_ERROR;
+    }
+
 private:
     inline bool isKeystoreUnlocked(State state) {
         switch (state) {