keystore: change migrate to duplicate

After discussion, it was determined that duplicate would be less
disruptive and it still fit in the current HAL model.

Change-Id: Id6ff97bfa5ec4cca9def177677263e9be1c9619f
diff --git a/keystore/IKeystoreService.cpp b/keystore/IKeystoreService.cpp
index d1df44e..0803071 100644
--- a/keystore/IKeystoreService.cpp
+++ b/keystore/IKeystoreService.cpp
@@ -488,21 +488,24 @@
         return ret;
     }
 
-    virtual int32_t migrate(const String16& name, int32_t targetUid)
+    virtual int32_t duplicate(const String16& srcKey, int32_t srcUid, const String16& destKey,
+            int32_t destUid)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
-        data.writeString16(name);
-        data.writeInt32(targetUid);
-        status_t status = remote()->transact(BnKeystoreService::MIGRATE, data, &reply);
+        data.writeString16(srcKey);
+        data.writeInt32(srcUid);
+        data.writeString16(destKey);
+        data.writeInt32(destUid);
+        status_t status = remote()->transact(BnKeystoreService::DUPLICATE, data, &reply);
         if (status != NO_ERROR) {
-            ALOGD("migrate() could not contact remote: %d\n", status);
+            ALOGD("duplicate() 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);
+            ALOGD("duplicate() caught exception %d\n", err);
             return -1;
         }
         return ret;
@@ -758,11 +761,13 @@
             reply->writeInt64(ret);
             return NO_ERROR;
         } break;
-        case MIGRATE: {
+        case DUPLICATE: {
             CHECK_INTERFACE(IKeystoreService, data, reply);
-            String16 name = data.readString16();
-            int32_t targetUid = data.readInt32();
-            int32_t ret = migrate(name, targetUid);
+            String16 srcKey = data.readString16();
+            int32_t srcUid = data.readInt32();
+            String16 destKey = data.readString16();
+            int32_t destUid = data.readInt32();
+            int32_t ret = duplicate(srcKey, srcUid, destKey, destUid);
             reply->writeNoException();
             reply->writeInt32(ret);
             return NO_ERROR;
diff --git a/keystore/include/keystore/IKeystoreService.h b/keystore/include/keystore/IKeystoreService.h
index 20ba9c5..7659f47 100644
--- a/keystore/include/keystore/IKeystoreService.h
+++ b/keystore/include/keystore/IKeystoreService.h
@@ -49,7 +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,
+        DUPLICATE = IBinder::FIRST_CALL_TRANSACTION + 20,
     };
 
     DECLARE_META_INTERFACE(KeystoreService);
@@ -96,7 +96,8 @@
 
     virtual int64_t getmtime(const String16& name) = 0;
 
-    virtual int32_t migrate(const String16& name, int32_t targetUid) = 0;
+    virtual int32_t duplicate(const String16& srcKey, int32_t srcUid, const String16& destKey,
+            int32_t destUid) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/keystore/keystore.cpp b/keystore/keystore.cpp
index 0b26bc8..385f005 100644
--- a/keystore/keystore.cpp
+++ b/keystore/keystore.cpp
@@ -122,21 +122,21 @@
 
 /* Here are the permissions, actions, users, and the main function. */
 typedef enum {
-    P_TEST     = 1 << 0,
-    P_GET      = 1 << 1,
-    P_INSERT   = 1 << 2,
-    P_DELETE   = 1 << 3,
-    P_EXIST    = 1 << 4,
-    P_SAW      = 1 << 5,
-    P_RESET    = 1 << 6,
-    P_PASSWORD = 1 << 7,
-    P_LOCK     = 1 << 8,
-    P_UNLOCK   = 1 << 9,
-    P_ZERO     = 1 << 10,
-    P_SIGN     = 1 << 11,
-    P_VERIFY   = 1 << 12,
-    P_GRANT    = 1 << 13,
-    P_MIGRATE  = 1 << 14,
+    P_TEST      = 1 << 0,
+    P_GET       = 1 << 1,
+    P_INSERT    = 1 << 2,
+    P_DELETE    = 1 << 3,
+    P_EXIST     = 1 << 4,
+    P_SAW       = 1 << 5,
+    P_RESET     = 1 << 6,
+    P_PASSWORD  = 1 << 7,
+    P_LOCK      = 1 << 8,
+    P_UNLOCK    = 1 << 9,
+    P_ZERO      = 1 << 10,
+    P_SIGN      = 1 << 11,
+    P_VERIFY    = 1 << 12,
+    P_GRANT     = 1 << 13,
+    P_DUPLICATE = 1 << 14,
 } perm_t;
 
 static struct user_euid {
@@ -361,6 +361,7 @@
 };
 
 typedef enum {
+    TYPE_ANY = 0, // meta type that matches anything
     TYPE_GENERIC = 1,
     TYPE_MASTER_KEY = 2,
     TYPE_KEY_PAIR = 3,
@@ -682,7 +683,7 @@
             upgrade(filename, keyBlob, version, type);
         }
 
-        if (keyBlob->getType() != type) {
+        if (type != TYPE_ANY && keyBlob->getType() != type) {
             ALOGW("key found but type doesn't match: %d vs %d", keyBlob->getType(), type);
             return KEY_NOT_FOUND;
         }
@@ -1585,49 +1586,66 @@
         return static_cast<int64_t>(s.st_mtime);
     }
 
-    int32_t migrate(const String16& name, int32_t targetUid) {
+    int32_t duplicate(const String16& srcKey, int32_t srcUid, const String16& destKey,
+            int32_t destUid) {
         uid_t callingUid = IPCThreadState::self()->getCallingUid();
-        if (!has_permission(callingUid, P_MIGRATE)) {
-            ALOGW("permission denied for %d: migrate", callingUid);
+        if (!has_permission(callingUid, P_DUPLICATE)) {
+            ALOGW("permission denied for %d: duplicate", callingUid);
             return -1L;
         }
 
         State state = mKeyStore->getState();
         if (!isKeystoreUnlocked(state)) {
-            ALOGD("calling migrate in state: %d", state);
+            ALOGD("calling duplicate in state: %d", state);
             return state;
         }
 
-        if (!is_granted_to(callingUid, targetUid)) {
-            ALOGD("migrate not granted: %d -> %d", callingUid, targetUid);
+        if (srcUid == -1 || static_cast<uid_t>(srcUid) == callingUid) {
+            srcUid = callingUid;
+        } else if (!is_granted_to(callingUid, srcUid)) {
+            ALOGD("migrate not granted from source: %d -> %d", callingUid, srcUid);
             return ::PERMISSION_DENIED;
         }
 
-        String8 source8(name);
+        if (destUid == -1) {
+            destUid = callingUid;
+        }
+
+        if (srcUid != destUid) {
+            if (static_cast<uid_t>(srcUid) != callingUid) {
+                ALOGD("can only duplicate from caller to other or to same uid: "
+                      "calling=%d, srcUid=%d, destUid=%d", callingUid, srcUid, destUid);
+                return ::PERMISSION_DENIED;
+            }
+
+            if (!is_granted_to(callingUid, destUid)) {
+                ALOGD("duplicate not granted to dest: %d -> %d", callingUid, destUid);
+                return ::PERMISSION_DENIED;
+            }
+        }
+
+        String8 source8(srcKey);
         char source[NAME_MAX];
 
-        encode_key_for_uid(source, callingUid, source8);
+        encode_key_for_uid(source, srcUid, source8);
 
-        if (access(source, W_OK) == -1) {
-            return (errno != ENOENT) ? ::SYSTEM_ERROR : ::KEY_NOT_FOUND;
-        }
-
-        String8 target8(name);
+        String8 target8(destKey);
         char target[NAME_MAX];
 
-        encode_key_for_uid(target, targetUid, target8);
+        encode_key_for_uid(target, destUid, target8);
 
-        // Make sure the target doesn't exist
-        if (access(target, R_OK) == 0 || errno != ENOENT) {
-            ALOGD("migrate target already exists: %s", strerror(errno));
+        if (access(target, W_OK) != -1 || errno != ENOENT) {
+            ALOGD("destination already exists: %s", target);
             return ::SYSTEM_ERROR;
         }
 
-        if (rename(source, target)) {
-            ALOGD("migrate could not rename: %s", strerror(errno));
-            return ::SYSTEM_ERROR;
+        Blob keyBlob;
+        ResponseCode responseCode = mKeyStore->get(source, &keyBlob, TYPE_ANY);
+        if (responseCode != ::NO_ERROR) {
+            return responseCode;
         }
-        return ::NO_ERROR;
+
+        return mKeyStore->put(target, &keyBlob);
     }
 
 private: