Bug 1020212 - Support keystore binder. r=qdot, a=2.0+
authorChuck Lee <chulee@mozilla.com>
Thu, 03 Jul 2014 15:08:01 +0800
changeset 207686 3b3b7dd57218036beefcf3ac2aa78b5274b8808a
parent 207685 d8dd1845628e0d82caea40cfa7707ac4d8f56b4f
child 207687 967f80f8409ef871331806dba871bc5fa026cbd3
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot, 2
bugs1020212
milestone32.0a2
Bug 1020212 - Support keystore binder. r=qdot, a=2.0+
ipc/keystore/KeyStore.cpp
ipc/keystore/KeyStore.h
--- a/ipc/keystore/KeyStore.cpp
+++ b/ipc/keystore/KeyStore.cpp
@@ -22,16 +22,165 @@
 #include "jsfriendapi.h"
 #include "MainThreadUtils.h" // For NS_IsMainThread.
 
 #include "plbase64.h"
 #include "certdb.h"
 #include "ScopedNSSTypes.h"
 
 using namespace mozilla::ipc;
+#if ANDROID_VERSION >= 18
+// After Android 4.3, it uses binder to access keystore instead of unix socket.
+#include <android/log.h>
+#include <binder/BinderService.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <security/keystore/include/keystore/IKeystoreService.h>
+#include <security/keystore/include/keystore/keystore.h>
+
+using namespace android;
+
+namespace android {
+// This class is used to make compiler happy.
+class BpKeystoreService : public BpInterface<IKeystoreService>
+{
+public:
+  BpKeystoreService(const sp<IBinder>& impl)
+    : BpInterface<IKeystoreService>(impl)
+  {
+  }
+
+  virtual int32_t get(const String16& name, uint8_t** item, size_t* itemLength) {return 0;}
+  virtual int32_t test() {return 0;}
+  virtual int32_t insert(const String16& name, const uint8_t* item, size_t itemLength, int uid, int32_t flags) {return 0;}
+  virtual int32_t del(const String16& name, int uid) {return 0;}
+  virtual int32_t exist(const String16& name, int uid) {return 0;}
+  virtual int32_t saw(const String16& name, int uid, Vector<String16>* matches) {return 0;}
+  virtual int32_t reset() {return 0;}
+  virtual int32_t password(const String16& password) {return 0;}
+  virtual int32_t lock() {return 0;}
+  virtual int32_t unlock(const String16& password) {return 0;}
+  virtual int32_t zero() {return 0;}
+  virtual int32_t import(const String16& name, const uint8_t* data, size_t length, int uid, int32_t flags) {return 0;}
+  virtual int32_t sign(const String16& name, const uint8_t* data, size_t length, uint8_t** out, size_t* outLength) {return 0;}
+  virtual int32_t verify(const String16& name, const uint8_t* data, size_t dataLength, const uint8_t* signature, size_t signatureLength) {return 0;}
+  virtual int32_t get_pubkey(const String16& name, uint8_t** pubkey, size_t* pubkeyLength) {return 0;}
+  virtual int32_t del_key(const String16& name, int uid) {return 0;}
+  virtual int32_t grant(const String16& name, int32_t granteeUid) {return 0;}
+  virtual int32_t ungrant(const String16& name, int32_t granteeUid) {return 0;}
+  virtual int64_t getmtime(const String16& name) {return 0;}
+  virtual int32_t duplicate(const String16& srcKey, int32_t srcUid, const String16& destKey, int32_t destUid) {return 0;}
+  virtual int32_t clear_uid(int64_t uid) {return 0;}
+#if ANDROID_VERSION == 18
+  virtual int32_t generate(const String16& name, int uid, int32_t flags) {return 0;}
+  virtual int32_t is_hardware_backed() {return 0;}
+#else
+  virtual int32_t generate(const String16& name, int32_t uid, int32_t keyType, int32_t keySize, int32_t flags, Vector<sp<KeystoreArg> >* args) {return 0;}
+  virtual int32_t is_hardware_backed(const String16& keyType) {return 0;}
+#endif
+};
+
+IMPLEMENT_META_INTERFACE(KeystoreService, "android.security.keystore");
+
+// Here comes binder requests.
+status_t BnKeystoreService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+  switch(code) {
+    case TEST: {
+      CHECK_INTERFACE(IKeystoreService, data, reply);
+      reply->writeNoException();
+      reply->writeInt32(test());
+      return NO_ERROR;
+    } break;
+    case GET: {
+      CHECK_INTERFACE(IKeystoreService, data, reply);
+      String16 name = data.readString16();
+      String8 tmp(name);
+      uint8_t* data = NULL;
+      size_t dataLength = 0;
+      int32_t ret = get(name, &data, &dataLength);
+
+      reply->writeNoException();
+      if (ret == 1) {
+        reply->writeInt32(dataLength);
+        void* buf = reply->writeInplace(dataLength);
+        memcpy(buf, data, dataLength);
+        free(data);
+      } else {
+        reply->writeInt32(-1);
+      }
+      return NO_ERROR;
+    } break;
+    default:
+      return NO_ERROR;
+  }
+}
+
+// Provide service for binder.
+class KeyStoreService: public BnKeystoreService
+{
+public:
+  int32_t test() {
+    uid_t callingUid = IPCThreadState::self()->getCallingUid();
+    if (!mozilla::ipc::checkPermission(callingUid)) {
+      return ::PERMISSION_DENIED;
+    }
+
+    return ::NO_ERROR;
+  }
+
+  int32_t get(const String16& name, uint8_t** item, size_t* itemLength) {
+    uid_t callingUid = IPCThreadState::self()->getCallingUid();
+    if (!mozilla::ipc::checkPermission(callingUid)) {
+      return ::PERMISSION_DENIED;
+    }
+
+    String8 certName(name);
+    return mozilla::ipc::getCertificate(certName.string(), (const uint8_t **)item, (int *)itemLength);
+  }
+
+  int32_t insert(const String16& name, const uint8_t* item, size_t itemLength, int uid, int32_t flags) {return ::UNDEFINED_ACTION;}
+  int32_t del(const String16& name, int uid) {return ::UNDEFINED_ACTION;}
+  int32_t exist(const String16& name, int uid) {return ::UNDEFINED_ACTION;}
+  int32_t saw(const String16& name, int uid, Vector<String16>* matches) {return ::UNDEFINED_ACTION;}
+  int32_t reset() {return ::UNDEFINED_ACTION;}
+  int32_t password(const String16& password) {return ::UNDEFINED_ACTION;}
+  int32_t lock() {return ::UNDEFINED_ACTION;}
+  int32_t unlock(const String16& password) {return ::UNDEFINED_ACTION;}
+  int32_t zero() {return ::UNDEFINED_ACTION;}
+  int32_t import(const String16& name, const uint8_t* data, size_t length, int uid, int32_t flags) {return ::UNDEFINED_ACTION;}
+  int32_t sign(const String16& name, const uint8_t* data, size_t length, uint8_t** out, size_t* outLength) {return ::UNDEFINED_ACTION;}
+  int32_t verify(const String16& name, const uint8_t* data, size_t dataLength, const uint8_t* signature, size_t signatureLength) {return ::UNDEFINED_ACTION;}
+  int32_t get_pubkey(const String16& name, uint8_t** pubkey, size_t* pubkeyLength) {return ::UNDEFINED_ACTION;}
+  int32_t del_key(const String16& name, int uid) {return ::UNDEFINED_ACTION;}
+  int32_t grant(const String16& name, int32_t granteeUid) {return ::UNDEFINED_ACTION;}
+  int32_t ungrant(const String16& name, int32_t granteeUid) {return ::UNDEFINED_ACTION;}
+  int64_t getmtime(const String16& name) {return ::UNDEFINED_ACTION;}
+  int32_t duplicate(const String16& srcKey, int32_t srcUid, const String16& destKey, int32_t destUid) {return ::UNDEFINED_ACTION;}
+  int32_t clear_uid(int64_t uid) {return ::UNDEFINED_ACTION;}
+#if ANDROID_VERSION == 18
+  virtual int32_t generate(const String16& name, int uid, int32_t flags) {return ::UNDEFINED_ACTION;}
+  virtual int32_t is_hardware_backed() {return ::UNDEFINED_ACTION;}
+#else
+  virtual int32_t generate(const String16& name, int32_t uid, int32_t keyType, int32_t keySize, int32_t flags, Vector<sp<KeystoreArg> >* args) {return ::UNDEFINED_ACTION;}
+  virtual int32_t is_hardware_backed(const String16& keyType) {return ::UNDEFINED_ACTION;}
+#endif
+};
+
+} // namespace android
+
+void startKeyStoreService()
+{
+  android::sp<android::IServiceManager> sm = android::defaultServiceManager();
+  android::sp<android::KeyStoreService> keyStoreService = new android::KeyStoreService();
+  sm->addService(String16("android.security.keystore"), keyStoreService);
+}
+#else
+void startKeyStoreService() { return; }
+#endif
 
 namespace mozilla {
 namespace ipc {
 
 static const char* KEYSTORE_SOCKET_NAME = "keystore";
 static const char* KEYSTORE_SOCKET_PATH = "/dev/socket/keystore";
 static const char* KEYSTORE_ALLOWED_USERS[] = {
   "root",
@@ -40,16 +189,109 @@ static const char* KEYSTORE_ALLOWED_USER
 };
 static const char* KEYSTORE_ALLOWED_PREFIXES[] = {
   "WIFI_SERVERCERT_",
   "WIFI_USERCERT_",
   "WIFI_USERKEY_",
   NULL
 };
 
+// Transform base64 certification data into DER format
+void
+FormatCaData(const uint8_t *aCaData, int aCaDataLength,
+             const char *aName, const uint8_t **aFormatData,
+             int *aFormatDataLength)
+{
+  int bufSize = strlen(CA_BEGIN) + strlen(CA_END) + strlen(CA_TAILER) * 2 +
+                strlen(aName) * 2 + aCaDataLength + aCaDataLength/CA_LINE_SIZE + 2;
+  char *buf = (char *)malloc(bufSize);
+
+  *aFormatDataLength = bufSize;
+  *aFormatData = (const uint8_t *)buf;
+
+  char *ptr = buf;
+  int len;
+
+  // Create DER header.
+  len = snprintf(ptr, bufSize, "%s%s%s", CA_BEGIN, aName, CA_TAILER);
+  ptr += len;
+  bufSize -= len;
+
+  // Split base64 data in lines.
+  int copySize;
+  while (aCaDataLength > 0) {
+    copySize = (aCaDataLength > CA_LINE_SIZE) ? CA_LINE_SIZE : aCaDataLength;
+
+    memcpy(ptr, aCaData, copySize);
+    ptr += copySize;
+    aCaData += copySize;
+    aCaDataLength -= copySize;
+    bufSize -= copySize;
+
+    *ptr = '\n';
+    ptr++;
+    bufSize--;
+  }
+
+  // Create DEA tailer.
+  snprintf(ptr, bufSize, "%s%s%s", CA_END, aName, CA_TAILER);
+}
+
+ResponseCode
+getCertificate(const char *aCertName, const uint8_t **aCertData, int *aCertDataLength)
+{
+  // certificate name prefix check.
+  if (!aCertName) {
+    return KEY_NOT_FOUND;
+  }
+
+  const char **prefix = KEYSTORE_ALLOWED_PREFIXES;
+  for (; *prefix; prefix++ ) {
+    if (!strncmp(*prefix, aCertName, strlen(*prefix))) {
+      break;
+    }
+  }
+  if (!(*prefix)) {
+    return KEY_NOT_FOUND;
+  }
+
+  // Get cert from NSS by name
+  ScopedCERTCertificate cert(CERT_FindCertByNickname(CERT_GetDefaultCertDB(),
+                                                     aCertName));
+
+  if (!cert) {
+    return KEY_NOT_FOUND;
+  }
+
+  char *certDER = PL_Base64Encode((const char *)cert->derCert.data,
+                                  cert->derCert.len, nullptr);
+  if (!certDER) {
+    return SYSTEM_ERROR;
+  }
+
+  FormatCaData((const uint8_t *)certDER, strlen(certDER), "CERTIFICATE",
+               aCertData, aCertDataLength);
+  PL_strfree(certDER);
+
+  return SUCCESS;
+}
+
+bool
+checkPermission(uid_t uid)
+{
+  struct passwd *userInfo = getpwuid(uid);
+  for (const char **user = KEYSTORE_ALLOWED_USERS; *user; user++ ) {
+    if (!strcmp(*user, userInfo->pw_name)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 int
 KeyStoreConnector::Create()
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   int fd;
 
   unlink(KEYSTORE_SOCKET_PATH);
@@ -90,24 +332,17 @@ KeyStoreConnector::SetUp(int aFd)
   // Socket permission check.
   struct ucred userCred;
   socklen_t len = sizeof(struct ucred);
 
   if (getsockopt(aFd, SOL_SOCKET, SO_PEERCRED, &userCred, &len)) {
     return false;
   }
 
-  struct passwd *userInfo = getpwuid(userCred.uid);
-  for (const char **user = KEYSTORE_ALLOWED_USERS; *user; user++ ) {
-    if (!strcmp(*user, userInfo->pw_name)) {
-      return true;
-    }
-  }
-
-  return false;
+  return ::checkPermission(userCred.uid);
 }
 
 bool
 KeyStoreConnector::SetUpListenSocket(int aFd)
 {
   // Allow access of wpa_supplicant(different user, differnt group)
   chmod(KEYSTORE_SOCKET_PATH, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
 
@@ -126,19 +361,17 @@ static char *
 get_cert_db_filename(void *arg, int vers)
 {
   static char keystoreDbPath[] = "/data/misc/wifi/keystore";
   return keystoreDbPath;
 }
 
 KeyStore::KeyStore()
 {
-  // Initial NSS
-  certdb = CERT_GetDefaultCertDB();
-
+  ::startKeyStoreService();
   Listen();
 }
 
 void
 KeyStore::Shutdown()
 {
   mShutdown = true;
   CloseSocket();
@@ -259,57 +492,16 @@ KeyStore::ReadData(UnixSocketRawData *aM
     mHandlerInfo.state = STATE_PROCESSING;
   } else {
     mHandlerInfo.state = STATE_READ_PARAM_LEN;
   }
 
   return SUCCESS;
 }
 
-// Transform base64 certification data into DER format
-void
-KeyStore::FormatCaData(const uint8_t *aCaData, int aCaDataLength,
-                       const char *aName, const uint8_t **aFormatData,
-                       int &aFormatDataLength)
-{
-  int bufSize = strlen(CA_BEGIN) + strlen(CA_END) + strlen(CA_TAILER) * 2 +
-                strlen(aName) * 2 + aCaDataLength + aCaDataLength/CA_LINE_SIZE + 2;
-  char *buf = (char *)malloc(bufSize);
-
-  aFormatDataLength = bufSize;
-  *aFormatData = (const uint8_t *)buf;
-
-  char *ptr = buf;
-  int len;
-
-  // Create DER header.
-  len = snprintf(ptr, bufSize, "%s%s%s", CA_BEGIN, aName, CA_TAILER);
-  ptr += len;
-  bufSize -= len;
-
-  // Split base64 data in lines.
-  int copySize;
-  while (aCaDataLength > 0) {
-    copySize = (aCaDataLength > CA_LINE_SIZE) ? CA_LINE_SIZE : aCaDataLength;
-
-    memcpy(ptr, aCaData, copySize);
-    ptr += copySize;
-    aCaData += copySize;
-    aCaDataLength -= copySize;
-    bufSize -= copySize;
-
-    *ptr = '\n';
-    ptr++;
-    bufSize--;
-  }
-
-  // Create DEA tailer.
-  snprintf(ptr, bufSize, "%s%s%s", CA_END, aName, CA_TAILER);
-}
-
 // Status response
 void
 KeyStore::SendResponse(ResponseCode aResponse)
 {
   if (aResponse == NO_RESPONSE)
     return;
 
   uint8_t response = (uint8_t)aResponse;
@@ -351,50 +543,21 @@ KeyStore::ReceiveSocketData(nsAutoPtr<Un
         break;
       case STATE_PROCESSING:
         if (mHandlerInfo.command == 'g') {
           // Get CA
           const uint8_t *certData;
           int certDataLength;
           const char *certName = (const char *)mHandlerInfo.param[0].data;
 
-          // certificate name prefix check.
-          if (!certName) {
-            result = KEY_NOT_FOUND;
-            break;
-          }
-          const char **prefix = KEYSTORE_ALLOWED_PREFIXES;
-          for (; *prefix; prefix++ ) {
-            if (!strncmp(*prefix, certName, strlen(*prefix))) {
-              break;
-            }
-          }
-          if (!(*prefix)) {
-            result = KEY_NOT_FOUND;
+          result = getCertificate(certName, &certData, &certDataLength);
+          if (result != SUCCESS) {
             break;
           }
 
-          // Get cert from NSS by name
-          ScopedCERTCertificate cert(CERT_FindCertByNickname(certdb, certName));
-          if (!cert) {
-            result = KEY_NOT_FOUND;
-            break;
-          }
-
-          char *certDER = PL_Base64Encode((const char *)cert->derCert.data,
-                                          cert->derCert.len, nullptr);
-          if (!certDER) {
-            result = SYSTEM_ERROR;
-            break;
-          }
-
-          FormatCaData((const uint8_t *)certDER, strlen(certDER), "CERTIFICATE",
-                       &certData, certDataLength);
-          PL_strfree(certDER);
-
           SendResponse(SUCCESS);
           SendData(certData, certDataLength);
 
           free((void *)certData);
         }
 
         ResetHandlerInfo();
         break;
--- a/ipc/keystore/KeyStore.h
+++ b/ipc/keystore/KeyStore.h
@@ -28,16 +28,25 @@ enum ResponseCode {
   UNDEFINED_ACTION  =  9,
   WRONG_PASSWORD_0  = 10,
   WRONG_PASSWORD_1  = 11,
   WRONG_PASSWORD_2  = 12,
   WRONG_PASSWORD_3  = 13, // MAX_RETRY = 4
   NO_RESPONSE
 };
 
+void FormatCaData(const uint8_t *aCaData, int aCaDataLength,
+                  const char *aName, const uint8_t **aFormatData,
+                  int *aFormatDataLength);
+
+ResponseCode getCertificate(const char *aCertName, const uint8_t **aCertData,
+                            int *aCertDataLength);
+
+bool checkPermission(uid_t uid);
+
 static const int MAX_PARAM = 2;
 static const int KEY_SIZE = ((NAME_MAX - 15) / 2);
 static const int VALUE_SIZE = 32768;
 static const int PASSWORD_SIZE = VALUE_SIZE;
 
 static const char *CA_BEGIN = "-----BEGIN ",
                   *CA_END   = "-----END ",
                   *CA_TAILER = "-----\n";
@@ -105,27 +114,22 @@ private:
     uint8_t                       command;
     struct ProtocolParam          param[MAX_PARAM];
     int                           paramCount;
     const struct ProtocolCommand  *commandPattern;
   } mHandlerInfo;
   void ResetHandlerInfo();
   void Listen();
 
-  void FormatCaData(const uint8_t *aCaData, int aCaDataLength, const char *aName,
-                    const uint8_t **aFormatData, int &aFormatDataLength);
-
   bool CheckSize(UnixSocketRawData *aMessage, size_t aExpectSize);
   ResponseCode ReadCommand(UnixSocketRawData *aMessage);
   ResponseCode ReadLength(UnixSocketRawData *aMessage);
   ResponseCode ReadData(UnixSocketRawData *aMessage);
   void SendResponse(ResponseCode response);
   void SendData(const uint8_t *data, int length);
 
   bool mShutdown;
-
-  CERTCertDBHandle *certdb;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_KeyStore_h