summaryrefslogtreecommitdiffstats
path: root/ipc/keystore
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /ipc/keystore
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'ipc/keystore')
-rw-r--r--ipc/keystore/KeyStore.cpp986
-rw-r--r--ipc/keystore/KeyStore.h141
-rw-r--r--ipc/keystore/KeyStoreConnector.cpp239
-rw-r--r--ipc/keystore/KeyStoreConnector.h57
-rw-r--r--ipc/keystore/moz.build18
5 files changed, 1441 insertions, 0 deletions
diff --git a/ipc/keystore/KeyStore.cpp b/ipc/keystore/KeyStore.cpp
new file mode 100644
index 000000000..992bc9075
--- /dev/null
+++ b/ipc/keystore/KeyStore.cpp
@@ -0,0 +1,986 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et ft=cpp: tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <fcntl.h>
+#include <limits.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#if defined(MOZ_WIDGET_GONK)
+#include <android/log.h>
+#define KEYSTORE_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
+#else
+#define KEYSTORE_LOG(args...) printf(args);
+#endif
+
+#include "KeyStore.h"
+#include "jsfriendapi.h"
+#include "KeyStoreConnector.h"
+#include "MainThreadUtils.h" // For NS_IsMainThread.
+#include "nsICryptoHash.h"
+
+#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 >= 21
+ 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;}
+ virtual int32_t reset_uid(int32_t uid) {return 0;}
+ virtual int32_t sync_uid(int32_t sourceUid, int32_t targetUid) {return 0;}
+ virtual int32_t password_uid(const String16& password, int32_t uid) {return 0;}
+#elif 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;
+ case GET_PUBKEY: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ uint8_t* data = nullptr;
+ size_t dataLength = 0;
+ int32_t ret = get_pubkey(name, &data, &dataLength);
+
+ reply->writeNoException();
+ if (dataLength > 0 && data != nullptr) {
+ reply->writeInt32(dataLength);
+ void* buf = reply->writeInplace(dataLength);
+ memcpy(buf, data, dataLength);
+ free(data);
+ } else {
+ reply->writeInt32(-1);
+ }
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ case SIGN: {
+ CHECK_INTERFACE(IKeystoreService, data, reply);
+ String16 name = data.readString16();
+ ssize_t signDataSize = data.readInt32();
+ const uint8_t *signData = nullptr;
+ if (signDataSize >= 0 && (size_t)signDataSize <= data.dataAvail()) {
+ signData = (const uint8_t *)data.readInplace(signDataSize);
+ }
+
+ uint8_t *signResult = nullptr;
+ size_t signResultSize;
+ int32_t ret = sign(name, signData, (size_t)signDataSize, &signResult,
+ &signResultSize);
+
+ reply->writeNoException();
+ if (signResultSize > 0 && signResult != nullptr) {
+ reply->writeInt32(signResultSize);
+ void* buf = reply->writeInplace(signResultSize);
+ memcpy(buf, signResult, signResultSize);
+ free(signResult);
+ } else {
+ reply->writeInt32(-1);
+ }
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ } break;
+ default:
+ return NO_ERROR;
+ }
+}
+
+// Provide service for binder.
+class KeyStoreService : public BnKeystoreService
+ , public nsNSSShutDownObject
+{
+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) {
+ nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+ return ::SYSTEM_ERROR;
+ }
+
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ if (!mozilla::ipc::checkPermission(callingUid)) {
+ return ::PERMISSION_DENIED;
+ }
+
+ String8 certName(name);
+ if (!strncmp(certName.string(), "WIFI_USERKEY_", 13)) {
+ return getPrivateKey(certName.string(), (const uint8_t**)item, itemLength);
+ }
+
+ return getCertificate(certName.string(), (const uint8_t**)item, 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)
+ {
+ nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+ return ::SYSTEM_ERROR;
+ }
+
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ if (!mozilla::ipc::checkPermission(callingUid)) {
+ return ::PERMISSION_DENIED;
+ }
+
+ if (data == nullptr) {
+ return ::SYSTEM_ERROR;
+ }
+
+ String8 keyName(name);
+ if (!strncmp(keyName.string(), "WIFI_USERKEY_", 13)) {
+ return signData(keyName.string(), data, length, out, 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) {
+ nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+ return ::SYSTEM_ERROR;
+ }
+
+ uid_t callingUid = IPCThreadState::self()->getCallingUid();
+ if (!mozilla::ipc::checkPermission(callingUid)) {
+ return ::PERMISSION_DENIED;
+ }
+
+ String8 keyName(name);
+ if (!strncmp(keyName.string(), "WIFI_USERKEY_", 13)) {
+ return getPublicKey(keyName.string(), (const uint8_t**)pubkey, 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 >= 21
+ 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;}
+ virtual int32_t reset_uid(int32_t uid) {return ::UNDEFINED_ACTION;;}
+ virtual int32_t sync_uid(int32_t sourceUid, int32_t targetUid) {return ::UNDEFINED_ACTION;}
+ virtual int32_t password_uid(const String16& password, int32_t uid) {return ::UNDEFINED_ACTION;}
+#elif 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
+
+protected:
+ virtual void virtualDestroyNSSReference() {}
+
+private:
+ ~KeyStoreService() {
+ nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+ return;
+ }
+ shutdown(ShutdownCalledFrom::Object);
+ }
+};
+
+} // 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
+
+static const char *CA_BEGIN = "-----BEGIN ",
+ *CA_END = "-----END ",
+ *CA_TAILER = "-----\n";
+
+namespace mozilla {
+namespace ipc {
+
+static const char* KEYSTORE_ALLOWED_USERS[] = {
+ "root",
+ "wifi",
+ NULL
+};
+static const char* KEYSTORE_ALLOWED_PREFIXES[] = {
+ "WIFI_SERVERCERT_",
+ "WIFI_USERCERT_",
+ "WIFI_USERKEY_",
+ NULL
+};
+
+// Transform base64 certification data into DER format
+void
+FormatCaData(const char *aCaData, int aCaDataLength,
+ const char *aName, const uint8_t **aFormatData,
+ size_t *aFormatDataLength)
+{
+ size_t 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);
+ if (!buf) {
+ *aFormatData = nullptr;
+ return;
+ }
+
+ *aFormatDataLength = bufSize;
+ *aFormatData = (const uint8_t *)buf;
+
+ char *ptr = buf;
+ size_t 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,
+ size_t *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(certDER, strlen(certDER), "CERTIFICATE", aCertData,
+ aCertDataLength);
+ PL_strfree(certDER);
+
+ if (!(*aCertData)) {
+ return SYSTEM_ERROR;
+ }
+
+ return SUCCESS;
+}
+
+ResponseCode getPrivateKey(const char *aKeyName, const uint8_t **aKeyData,
+ size_t *aKeyDataLength)
+{
+ *aKeyData = nullptr;
+ // Get corresponding user certificate nickname
+ char userCertName[128] = {0};
+ snprintf(userCertName, sizeof(userCertName) - 1, "WIFI_USERCERT_%s", aKeyName + 13);
+
+ // Get private key from user certificate.
+ ScopedCERTCertificate userCert(
+ CERT_FindCertByNickname(CERT_GetDefaultCertDB(), userCertName));
+ if (!userCert) {
+ return KEY_NOT_FOUND;
+ }
+
+ ScopedSECKEYPrivateKey privateKey(
+ PK11_FindKeyByAnyCert(userCert.get(), nullptr));
+ if (!privateKey) {
+ return KEY_NOT_FOUND;
+ }
+
+ // Export private key in PKCS#12 encrypted format, no password.
+ unsigned char pwstr[] = {0, 0};
+ SECItem password = {siBuffer, pwstr, sizeof(pwstr)};
+ ScopedSECKEYEncryptedPrivateKeyInfo encryptedPrivateKey(
+ PK11_ExportEncryptedPrivKeyInfo(privateKey->pkcs11Slot,
+ SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4, &password, privateKey, 1,
+ privateKey->wincx));
+
+ if (!encryptedPrivateKey) {
+ return KEY_NOT_FOUND;
+ }
+
+ // Decrypt into RSA private key.
+ //
+ // Generate key for PKCS#12 encryption, we use SHA1 with 1 iteration, as the
+ // parameters used in PK11_ExportEncryptedPrivKeyInfo() above.
+ // see: PKCS#12 v1.0, B.2.
+ //
+ uint8_t DSP[192] = {0};
+ memset(DSP, 0x01, 64); // Diversifier part, ID = 1 for decryption.
+ memset(DSP + 128, 0x00, 64); // Password part, no password.
+
+ uint8_t *S = &DSP[64]; // Salt part.
+ uint8_t *salt = encryptedPrivateKey->algorithm.parameters.data + 4;
+ int saltLength = (int)encryptedPrivateKey->algorithm.parameters.data[3];
+ if (saltLength <= 0) {
+ return SYSTEM_ERROR;
+ }
+ for (int i = 0; i < 64; i++) {
+ S[i] = salt[i % saltLength];
+ }
+
+ // Generate key by SHA-1
+ nsresult rv;
+ nsCOMPtr<nsICryptoHash> hash =
+ do_CreateInstance("@mozilla.org/security/hash;1", &rv);
+ if (NS_FAILED(rv)) {
+ return SYSTEM_ERROR;
+ }
+
+ rv = hash->Init(nsICryptoHash::SHA1);
+ if (NS_FAILED(rv)) {
+ return SYSTEM_ERROR;
+ }
+
+ rv = hash->Update(DSP, sizeof(DSP));
+ if (NS_FAILED(rv)) {
+ return SYSTEM_ERROR;
+ }
+
+ nsCString hashResult;
+ rv = hash->Finish(false, hashResult);
+ if (NS_FAILED(rv)) {
+ return SYSTEM_ERROR;
+ }
+
+ // First 40-bit as key for RC4.
+ uint8_t key[5];
+ memcpy(key, hashResult.get(), sizeof(key));
+
+ ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+ if (!slot) {
+ return SYSTEM_ERROR;
+ }
+
+ SECItem keyItem = {siBuffer, key, sizeof(key)};
+ ScopedPK11SymKey symKey(PK11_ImportSymKey(slot, CKM_RC4, PK11_OriginUnwrap,
+ CKA_DECRYPT, &keyItem, nullptr));
+ if (!symKey) {
+ return SYSTEM_ERROR;
+ }
+
+ // Get expected decrypted data size then allocate memory.
+ uint8_t *encryptedData = (uint8_t *)encryptedPrivateKey->encryptedData.data;
+ unsigned int encryptedDataLen = encryptedPrivateKey->encryptedData.len;
+ unsigned int decryptedDataLen = encryptedDataLen;
+ SECStatus srv = PK11_Decrypt(symKey, CKM_RC4, &keyItem, nullptr,
+ &decryptedDataLen, encryptedDataLen,
+ encryptedData, encryptedDataLen);
+ if (srv != SECSuccess) {
+ return SYSTEM_ERROR;
+ }
+
+ ScopedSECItem decryptedData(::SECITEM_AllocItem(nullptr, nullptr,
+ decryptedDataLen));
+ if (!decryptedData) {
+ return SYSTEM_ERROR;
+ }
+
+ // Decrypt by RC4.
+ srv = PK11_Decrypt(symKey, CKM_RC4, &keyItem, decryptedData->data,
+ &decryptedDataLen, decryptedData->len, encryptedData,
+ encryptedDataLen);
+ if (srv != SECSuccess) {
+ return SYSTEM_ERROR;
+ }
+
+ // Export key in PEM format.
+ char *keyPEM = PL_Base64Encode((const char *)decryptedData->data,
+ decryptedDataLen, nullptr);
+
+ if (!keyPEM) {
+ return SYSTEM_ERROR;
+ }
+
+ FormatCaData(keyPEM, strlen(keyPEM), "PRIVATE KEY", aKeyData, aKeyDataLength);
+ PL_strfree(keyPEM);
+
+ if (!(*aKeyData)) {
+ return SYSTEM_ERROR;
+ }
+
+ return SUCCESS;
+}
+
+ResponseCode getPublicKey(const char *aKeyName, const uint8_t **aKeyData,
+ size_t *aKeyDataLength)
+{
+ *aKeyData = nullptr;
+
+ // Get corresponding user certificate nickname
+ char userCertName[128] = {0};
+ snprintf(userCertName, sizeof(userCertName) - 1, "WIFI_USERCERT_%s", aKeyName + 13);
+
+ // Get public key from user certificate.
+ ScopedCERTCertificate userCert(
+ CERT_FindCertByNickname(CERT_GetDefaultCertDB(), userCertName));
+ if (!userCert) {
+ return KEY_NOT_FOUND;
+ }
+
+ // Get public key.
+ ScopedSECKEYPublicKey publicKey(CERT_ExtractPublicKey(userCert));
+ if (!publicKey) {
+ return KEY_NOT_FOUND;
+ }
+
+ ScopedSECItem keyItem(PK11_DEREncodePublicKey(publicKey));
+ if (!keyItem) {
+ return KEY_NOT_FOUND;
+ }
+
+ size_t bufSize = keyItem->len;
+ char *buf = (char *)malloc(bufSize);
+ if (!buf) {
+ return SYSTEM_ERROR;
+ }
+
+ memcpy(buf, keyItem->data, bufSize);
+ *aKeyData = (const uint8_t *)buf;
+ *aKeyDataLength = bufSize;
+
+ return SUCCESS;
+}
+
+ResponseCode signData(const char *aKeyName, const uint8_t *data, size_t length,
+ uint8_t **out, size_t *outLength)
+{
+ *out = nullptr;
+ // Get corresponding user certificate nickname
+ char userCertName[128] = {0};
+ snprintf(userCertName, sizeof(userCertName) - 1, "WIFI_USERCERT_%s", aKeyName + 13);
+
+ // Get private key from user certificate.
+ ScopedCERTCertificate userCert(
+ CERT_FindCertByNickname(CERT_GetDefaultCertDB(), userCertName));
+ if (!userCert) {
+ return KEY_NOT_FOUND;
+ }
+
+ ScopedSECKEYPrivateKey privateKey(
+ PK11_FindKeyByAnyCert(userCert.get(), nullptr));
+ if (!privateKey) {
+ return KEY_NOT_FOUND;
+ }
+
+ //
+ // Find hash data from incoming data.
+ //
+ // Incoming data might be padded by PKCS-1 format:
+ // 00 01 FF FF ... FF 00 || Hash of length 36
+ // If the padding part exists, we have to ignore them.
+ //
+ uint8_t *hash = (uint8_t *)data;
+ const size_t HASH_LENGTH = 36;
+ if (length < HASH_LENGTH) {
+ return VALUE_CORRUPTED;
+ }
+ if (hash[0] == 0x00 && hash[1] == 0x01 && hash[2] == 0xFF && hash[3] == 0xFF) {
+ hash += 4;
+ while (*hash == 0xFF) {
+ if (hash + HASH_LENGTH > data + length) {
+ return VALUE_CORRUPTED;
+ }
+ hash++;
+ }
+ if (*hash != 0x00) {
+ return VALUE_CORRUPTED;
+ }
+ hash++;
+ }
+ if (hash + HASH_LENGTH != data + length) {
+ return VALUE_CORRUPTED;
+ }
+ SECItem hashItem = {siBuffer, hash, HASH_LENGTH};
+
+ // Sign hash.
+ ScopedSECItem signItem(::SECITEM_AllocItem(nullptr, nullptr,
+ PK11_SignatureLen(privateKey)));
+ if (!signItem) {
+ return SYSTEM_ERROR;
+ }
+
+ SECStatus srv;
+ srv = PK11_Sign(privateKey, signItem.get(), &hashItem);
+ if (srv != SECSuccess) {
+ return SYSTEM_ERROR;
+ }
+
+ uint8_t *buf = (uint8_t *)malloc(signItem->len);
+ if (!buf) {
+ return SYSTEM_ERROR;
+ }
+
+ memcpy(buf, signItem->data, signItem->len);
+ *out = buf;
+ *outLength = signItem->len;
+
+ 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;
+}
+
+//
+// KeyStore
+//
+
+KeyStore::KeyStore()
+: mShutdown(false)
+{
+ MOZ_COUNT_CTOR(KeyStore);
+ ::startKeyStoreService();
+ Listen();
+}
+
+KeyStore::~KeyStore()
+{
+ nsNSSShutDownPreventionLock locker;
+ MOZ_COUNT_DTOR(KeyStore);
+
+ if (isAlreadyShutDown()) {
+ return;
+ }
+
+ shutdown(ShutdownCalledFrom::Object);
+
+ MOZ_ASSERT(!mListenSocket);
+ MOZ_ASSERT(!mStreamSocket);
+}
+
+void
+KeyStore::Shutdown()
+{
+ // We set mShutdown first, so that |OnDisconnect| won't try to reconnect.
+ mShutdown = true;
+
+ if (mStreamSocket) {
+ mStreamSocket->Close();
+ mStreamSocket = nullptr;
+ }
+ if (mListenSocket) {
+ mListenSocket->Close();
+ mListenSocket = nullptr;
+ }
+}
+
+void
+KeyStore::Listen()
+{
+ // We only allocate one |StreamSocket|, but re-use it for every connection.
+ if (mStreamSocket) {
+ mStreamSocket->Close();
+ } else {
+ mStreamSocket = new StreamSocket(this, STREAM_SOCKET);
+ }
+
+ if (!mListenSocket) {
+ // We only ever allocate one |ListenSocket|...
+ mListenSocket = new ListenSocket(this, LISTEN_SOCKET);
+ mListenSocket->Listen(new KeyStoreConnector(KEYSTORE_ALLOWED_USERS),
+ mStreamSocket);
+ } else {
+ // ... but keep it open.
+ mListenSocket->Listen(mStreamSocket);
+ }
+
+ ResetHandlerInfo();
+}
+
+void
+KeyStore::ResetHandlerInfo()
+{
+ mHandlerInfo.state = STATE_IDLE;
+ mHandlerInfo.command = 0;
+ mHandlerInfo.paramCount = 0;
+ mHandlerInfo.commandPattern = nullptr;
+ for (int i = 0; i < MAX_PARAM; i++) {
+ mHandlerInfo.param[i].length = 0;
+ memset(mHandlerInfo.param[i].data, 0, VALUE_SIZE);
+ }
+}
+
+bool
+KeyStore::CheckSize(UnixSocketBuffer *aMessage, size_t aExpectSize)
+{
+ return (aMessage->GetSize() >= aExpectSize);
+}
+
+ResponseCode
+KeyStore::ReadCommand(UnixSocketBuffer *aMessage)
+{
+ if (mHandlerInfo.state != STATE_IDLE) {
+ NS_WARNING("Wrong state in ReadCommand()!");
+ return SYSTEM_ERROR;
+ }
+
+ if (!CheckSize(aMessage, 1)) {
+ NS_WARNING("Data size error in ReadCommand()!");
+ return PROTOCOL_ERROR;
+ }
+
+ mHandlerInfo.command = *aMessage->GetData();
+ aMessage->Consume(1);
+
+ // Find corrsponding command pattern
+ const struct ProtocolCommand *command = commands;
+ while (command->command && command->command != mHandlerInfo.command) {
+ command++;
+ }
+
+ if (!command->command) {
+ NS_WARNING("Unsupported command!");
+ return PROTOCOL_ERROR;
+ }
+
+ // Get command pattern.
+ mHandlerInfo.commandPattern = command;
+ if (command->paramNum) {
+ // Read command parameter if needed.
+ mHandlerInfo.state = STATE_READ_PARAM_LEN;
+ } else {
+ mHandlerInfo.state = STATE_PROCESSING;
+ }
+
+ return SUCCESS;
+}
+
+ResponseCode
+KeyStore::ReadLength(UnixSocketBuffer *aMessage)
+{
+ if (mHandlerInfo.state != STATE_READ_PARAM_LEN) {
+ NS_WARNING("Wrong state in ReadLength()!");
+ return SYSTEM_ERROR;
+ }
+
+ if (!CheckSize(aMessage, 2)) {
+ NS_WARNING("Data size error in ReadLength()!");
+ return PROTOCOL_ERROR;
+ }
+
+ // Read length of command parameter.
+ // FIXME: Depends on endianess and (sizeof(unsigned short) == 2)
+ unsigned short dataLength;
+ memcpy(&dataLength, aMessage->GetData(), 2);
+ aMessage->Consume(2);
+ mHandlerInfo.param[mHandlerInfo.paramCount].length = ntohs(dataLength);
+
+ mHandlerInfo.state = STATE_READ_PARAM_DATA;
+
+ return SUCCESS;
+}
+
+ResponseCode
+KeyStore::ReadData(UnixSocketBuffer *aMessage)
+{
+ if (mHandlerInfo.state != STATE_READ_PARAM_DATA) {
+ NS_WARNING("Wrong state in ReadData()!");
+ return SYSTEM_ERROR;
+ }
+
+ if (!CheckSize(aMessage, mHandlerInfo.param[mHandlerInfo.paramCount].length)) {
+ NS_WARNING("Data size error in ReadData()!");
+ return PROTOCOL_ERROR;
+ }
+
+ // Read command parameter.
+ memcpy(mHandlerInfo.param[mHandlerInfo.paramCount].data,
+ aMessage->GetData(),
+ mHandlerInfo.param[mHandlerInfo.paramCount].length);
+ aMessage->Consume(mHandlerInfo.param[mHandlerInfo.paramCount].length);
+ mHandlerInfo.paramCount++;
+
+ if (mHandlerInfo.paramCount == mHandlerInfo.commandPattern->paramNum) {
+ mHandlerInfo.state = STATE_PROCESSING;
+ } else {
+ mHandlerInfo.state = STATE_READ_PARAM_LEN;
+ }
+
+ return SUCCESS;
+}
+
+// Status response
+void
+KeyStore::SendResponse(ResponseCode aResponse)
+{
+ MOZ_ASSERT(mStreamSocket);
+
+ if (aResponse == NO_RESPONSE)
+ return;
+
+ uint8_t response = (uint8_t)aResponse;
+ UnixSocketRawData* data = new UnixSocketRawData((const void *)&response, 1);
+ mStreamSocket->SendSocketData(data);
+}
+
+// Data response
+void
+KeyStore::SendData(const uint8_t *aData, int aLength)
+{
+ MOZ_ASSERT(mStreamSocket);
+
+ unsigned short dataLength = htons(aLength);
+
+ UnixSocketRawData* length = new UnixSocketRawData((const void *)&dataLength, 2);
+ mStreamSocket->SendSocketData(length);
+
+ UnixSocketRawData* data = new UnixSocketRawData((const void *)aData, aLength);
+ mStreamSocket->SendSocketData(data);
+}
+
+// |StreamSocketConsumer|, |ListenSocketConsumer|
+
+void
+KeyStore::ReceiveSocketData(int aIndex, UniquePtr<UnixSocketBuffer>& aMessage)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Handle request.
+ ResponseCode result = SUCCESS;
+ while (aMessage->GetSize() ||
+ mHandlerInfo.state == STATE_PROCESSING) {
+ switch (mHandlerInfo.state) {
+ case STATE_IDLE:
+ result = ReadCommand(aMessage.get());
+ break;
+ case STATE_READ_PARAM_LEN:
+ result = ReadLength(aMessage.get());
+ break;
+ case STATE_READ_PARAM_DATA:
+ result = ReadData(aMessage.get());
+ break;
+ case STATE_PROCESSING:
+ if (mHandlerInfo.command == 'g') {
+ result = SYSTEM_ERROR;
+
+ nsNSSShutDownPreventionLock locker;
+ if (isAlreadyShutDown()) {
+ break;
+ }
+
+ // Get CA
+ const uint8_t *data;
+ size_t dataLength;
+ const char *name = (const char *)mHandlerInfo.param[0].data;
+
+ if (!strncmp(name, "WIFI_USERKEY_", 13)) {
+ result = getPrivateKey(name, &data, &dataLength);
+ } else {
+ result = getCertificate(name, &data, &dataLength);
+ }
+ if (result != SUCCESS) {
+ break;
+ }
+
+ SendResponse(SUCCESS);
+ SendData(data, (int)dataLength);
+
+ free((void *)data);
+ }
+
+ ResetHandlerInfo();
+ break;
+ }
+
+ if (result != SUCCESS) {
+ SendResponse(result);
+ ResetHandlerInfo();
+ return;
+ }
+ }
+}
+
+void
+KeyStore::OnConnectSuccess(int aIndex)
+{
+ if (aIndex == STREAM_SOCKET) {
+ mShutdown = false;
+ }
+}
+
+void
+KeyStore::OnConnectError(int aIndex)
+{
+ if (mShutdown) {
+ return;
+ }
+
+ if (aIndex == STREAM_SOCKET) {
+ // Stream socket error; start listening again
+ Listen();
+ }
+}
+
+void
+KeyStore::OnDisconnect(int aIndex)
+{
+ if (mShutdown) {
+ return;
+ }
+
+ switch (aIndex) {
+ case LISTEN_SOCKET:
+ // Listen socket disconnected; start anew.
+ mListenSocket = nullptr;
+ Listen();
+ break;
+ case STREAM_SOCKET:
+ // Stream socket disconnected; start listening again.
+ Listen();
+ break;
+ }
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/keystore/KeyStore.h b/ipc/keystore/KeyStore.h
new file mode 100644
index 000000000..c6bb09023
--- /dev/null
+++ b/ipc/keystore/KeyStore.h
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et ft=cpp: tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_KeyStore_h
+#define mozilla_ipc_KeyStore_h 1
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "cert.h"
+#include "mozilla/ipc/ListenSocket.h"
+#include "mozilla/ipc/ListenSocketConsumer.h"
+#include "mozilla/ipc/StreamSocket.h"
+#include "mozilla/ipc/StreamSocketConsumer.h"
+#include "nsNSSShutDown.h"
+
+namespace mozilla {
+namespace ipc {
+
+enum ResponseCode {
+ SUCCESS = 1,
+ LOCKED = 2,
+ UNINITIALIZED = 3,
+ SYSTEM_ERROR = 4,
+ PROTOCOL_ERROR = 5,
+ PERMISSION_DENIED = 6,
+ KEY_NOT_FOUND = 7,
+ VALUE_CORRUPTED = 8,
+ 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,
+ size_t *aFormatDataLength);
+
+ResponseCode getCertificate(const char *aCertName, const uint8_t **aCertData,
+ size_t *aCertDataLength);
+ResponseCode getPrivateKey(const char *aKeyName, const uint8_t **aKeyData,
+ size_t *aKeyDataLength);
+ResponseCode getPublicKey(const char *aKeyName, const uint8_t **aKeyData,
+ size_t *aKeyDataLength);
+ResponseCode signData(const char *aKeyName, const uint8_t *data, size_t length,
+ uint8_t **out, size_t *outLength);
+
+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 int CA_LINE_SIZE = 64;
+
+struct ProtocolCommand {
+ int8_t command;
+ int paramNum;
+};
+
+static const struct ProtocolCommand commands[] = {
+ {'g', 1}, // Get CA, command "g CERT_NAME"
+ { 0, 0}
+};
+
+struct ProtocolParam{
+ uint length;
+ int8_t data[VALUE_SIZE];
+};
+
+typedef enum {
+ STATE_IDLE,
+ STATE_READ_PARAM_LEN,
+ STATE_READ_PARAM_DATA,
+ STATE_PROCESSING
+} ProtocolHandlerState;
+
+class KeyStore final
+ : public StreamSocketConsumer
+ , public ListenSocketConsumer
+ , public nsNSSShutDownObject
+{
+public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(KeyStore)
+
+ KeyStore();
+
+ void Shutdown();
+
+protected:
+ virtual void virtualDestroyNSSReference() {}
+
+private:
+ enum SocketType {
+ LISTEN_SOCKET,
+ STREAM_SOCKET
+ };
+
+ ~KeyStore();
+
+ struct {
+ ProtocolHandlerState state;
+ uint8_t command;
+ struct ProtocolParam param[MAX_PARAM];
+ int paramCount;
+ const struct ProtocolCommand *commandPattern;
+ } mHandlerInfo;
+ void ResetHandlerInfo();
+ void Listen();
+
+ bool CheckSize(UnixSocketBuffer *aMessage, size_t aExpectSize);
+ ResponseCode ReadCommand(UnixSocketBuffer *aMessage);
+ ResponseCode ReadLength(UnixSocketBuffer *aMessage);
+ ResponseCode ReadData(UnixSocketBuffer *aMessage);
+ void SendResponse(ResponseCode response);
+ void SendData(const uint8_t *data, int length);
+
+ // Methods for |StreamSocketConsumer|
+ //
+
+ void ReceiveSocketData(int aIndex,
+ UniquePtr<UnixSocketBuffer>& aMessage) override;
+ void OnConnectSuccess(int aIndex) override;
+ void OnConnectError(int aIndex) override;
+ void OnDisconnect(int aIndex) override;
+
+ bool mShutdown;
+
+ RefPtr<ListenSocket> mListenSocket;
+ RefPtr<StreamSocket> mStreamSocket;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_KeyStore_h
diff --git a/ipc/keystore/KeyStoreConnector.cpp b/ipc/keystore/KeyStoreConnector.cpp
new file mode 100644
index 000000000..0e11fcec6
--- /dev/null
+++ b/ipc/keystore/KeyStoreConnector.cpp
@@ -0,0 +1,239 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et ft=cpp: tw=80: */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "KeyStoreConnector.h"
+#include <fcntl.h>
+#include <pwd.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
+#include "nsThreadUtils.h" // For NS_IsMainThread.
+
+#ifdef MOZ_WIDGET_GONK
+#include <android/log.h>
+#define KEYSTORE_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
+#else
+#define KEYSTORE_LOG(args...) printf(args);
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+static const char KEYSTORE_SOCKET_PATH[] = "/dev/socket/keystore";
+
+KeyStoreConnector::KeyStoreConnector(const char** const aAllowedUsers)
+ : mAllowedUsers(aAllowedUsers)
+{
+ MOZ_COUNT_CTOR_INHERITED(KeyStoreConnector, UnixSocketConnector);
+}
+
+KeyStoreConnector::~KeyStoreConnector()
+{
+ MOZ_COUNT_DTOR_INHERITED(KeyStoreConnector, UnixSocketConnector);
+}
+
+nsresult
+KeyStoreConnector::CreateSocket(int& aFd) const
+{
+ unlink(KEYSTORE_SOCKET_PATH);
+
+ aFd = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (aFd < 0) {
+ KEYSTORE_LOG("Could not open KeyStore socket!");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+KeyStoreConnector::SetSocketFlags(int aFd) const
+{
+ static const int sReuseAddress = 1;
+
+ // Set close-on-exec bit.
+ int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFD));
+ if (flags < 0) {
+ return NS_ERROR_FAILURE;
+ }
+ flags |= FD_CLOEXEC;
+ int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFD, flags));
+ if (res < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Set non-blocking status flag.
+ flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
+ if (flags < 0) {
+ return NS_ERROR_FAILURE;
+ }
+ flags |= O_NONBLOCK;
+ res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags));
+ if (res < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Set socket addr to be reused even if kernel is still waiting to close.
+ res = setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, &sReuseAddress,
+ sizeof(sReuseAddress));
+ if (res < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+KeyStoreConnector::CheckPermission(int aFd) const
+{
+ struct ucred userCred;
+ socklen_t len = sizeof(userCred);
+
+ if (getsockopt(aFd, SOL_SOCKET, SO_PEERCRED, &userCred, &len)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ const struct passwd* userInfo = getpwuid(userCred.uid);
+ if (!userInfo) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mAllowedUsers) {
+ return NS_ERROR_FAILURE;
+ }
+
+ for (const char** user = mAllowedUsers; *user; ++user) {
+ if (!strcmp(*user, userInfo->pw_name)) {
+ return NS_OK;
+ }
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+KeyStoreConnector::CreateAddress(struct sockaddr& aAddress,
+ socklen_t& aAddressLength) const
+{
+ struct sockaddr_un* address =
+ reinterpret_cast<struct sockaddr_un*>(&aAddress);
+
+ size_t namesiz = strlen(KEYSTORE_SOCKET_PATH) + 1; // include trailing '\0'
+
+ if (namesiz > sizeof(address->sun_path)) {
+ KEYSTORE_LOG("Address too long for socket struct!");
+ return NS_ERROR_FAILURE;
+ }
+
+ address->sun_family = AF_UNIX;
+ memcpy(address->sun_path, KEYSTORE_SOCKET_PATH, namesiz);
+
+ aAddressLength = offsetof(struct sockaddr_un, sun_path) + namesiz;
+
+ return NS_OK;
+}
+
+// |UnixSocketConnector|
+
+nsresult
+KeyStoreConnector::ConvertAddressToString(const struct sockaddr& aAddress,
+ socklen_t aAddressLength,
+ nsACString& aAddressString)
+{
+ MOZ_ASSERT(aAddress.sa_family == AF_UNIX);
+
+ const struct sockaddr_un* un =
+ reinterpret_cast<const struct sockaddr_un*>(&aAddress);
+
+ size_t len = aAddressLength - offsetof(struct sockaddr_un, sun_path);
+
+ aAddressString.Assign(un->sun_path, len);
+
+ return NS_OK;
+}
+
+nsresult
+KeyStoreConnector::CreateListenSocket(struct sockaddr* aAddress,
+ socklen_t* aAddressLength,
+ int& aListenFd)
+{
+ ScopedClose fd;
+
+ nsresult rv = CreateSocket(fd.rwget());
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = SetSocketFlags(fd);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (aAddress && aAddressLength) {
+ rv = CreateAddress(*aAddress, *aAddressLength);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ // Allow access for wpa_supplicant (different user, different group)
+ //
+ // TODO: Improve this by setting specific user/group for
+ // wpa_supplicant by calling |fchmod| and |fchown|.
+ //
+ chmod(KEYSTORE_SOCKET_PATH, S_IRUSR|S_IWUSR|
+ S_IRGRP|S_IWGRP|
+ S_IROTH|S_IWOTH);
+
+ aListenFd = fd.forget();
+
+ return NS_OK;
+}
+
+nsresult
+KeyStoreConnector::AcceptStreamSocket(int aListenFd,
+ struct sockaddr* aAddress,
+ socklen_t* aAddressLength,
+ int& aStreamFd)
+{
+ ScopedClose fd(
+ TEMP_FAILURE_RETRY(accept(aListenFd, aAddress, aAddressLength)));
+ if (fd < 0) {
+ NS_WARNING("Cannot accept file descriptor!");
+ return NS_ERROR_FAILURE;
+ }
+ nsresult rv = SetSocketFlags(fd);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = CheckPermission(fd);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ aStreamFd = fd.forget();
+
+ return NS_OK;
+}
+
+nsresult
+KeyStoreConnector::CreateStreamSocket(struct sockaddr* aAddress,
+ socklen_t* aAddressLength,
+ int& aStreamFd)
+{
+ MOZ_CRASH("|KeyStoreConnector| does not support creating stream sockets.");
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+KeyStoreConnector::Duplicate(UnixSocketConnector*& aConnector)
+{
+ aConnector = new KeyStoreConnector(*this);
+
+ return NS_OK;
+}
+
+}
+}
diff --git a/ipc/keystore/KeyStoreConnector.h b/ipc/keystore/KeyStoreConnector.h
new file mode 100644
index 000000000..349b030de
--- /dev/null
+++ b/ipc/keystore/KeyStoreConnector.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 et ft=cpp: tw=80: */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ipc_KeyStoreConnector_h
+#define mozilla_ipc_KeyStoreConnector_h
+
+#include "mozilla/ipc/UnixSocketConnector.h"
+
+namespace mozilla {
+namespace ipc {
+
+class KeyStoreConnector final : public UnixSocketConnector
+{
+public:
+ KeyStoreConnector(const char** const aAllowedUsers);
+ ~KeyStoreConnector();
+
+ // Methods for |UnixSocketConnector|
+ //
+
+ nsresult ConvertAddressToString(const struct sockaddr& aAddress,
+ socklen_t aAddressLength,
+ nsACString& aAddressString) override;
+
+ nsresult CreateListenSocket(struct sockaddr* aAddress,
+ socklen_t* aAddressLength,
+ int& aListenFd) override;
+
+ nsresult AcceptStreamSocket(int aListenFd,
+ struct sockaddr* aAddress,
+ socklen_t* aAddressLength,
+ int& aStreamFd) override;
+
+ nsresult CreateStreamSocket(struct sockaddr* aAddress,
+ socklen_t* aAddressLength,
+ int& aStreamFd) override;
+
+ nsresult Duplicate(UnixSocketConnector*& aConnector) override;
+
+private:
+ nsresult CreateSocket(int& aFd) const;
+ nsresult SetSocketFlags(int aFd) const;
+ nsresult CheckPermission(int aFd) const;
+ nsresult CreateAddress(struct sockaddr& aAddress,
+ socklen_t& aAddressLength) const;
+
+ const char** const mAllowedUsers;
+};
+
+}
+}
+
+#endif
diff --git a/ipc/keystore/moz.build b/ipc/keystore/moz.build
new file mode 100644
index 000000000..e5df10a30
--- /dev/null
+++ b/ipc/keystore/moz.build
@@ -0,0 +1,18 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS.mozilla.ipc += [
+ 'KeyStore.h'
+]
+
+SOURCES += [
+ 'KeyStore.cpp',
+ 'KeyStoreConnector.cpp'
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'