Bug 1329234 - Remove ipc/keystore/. r=mrbkap

This folder only contains B2G specific code, which we can now remove from m-c.
Moreover, it is the last consumer of the Scoped.h NSS types provided by
ScopedNSSTypes.h, so removing the folder makes getting rid of Scoped.h easier.

MozReview-Commit-ID: CawbEv1d4vQ

--HG--
extra : rebase_source : b546e4682583aeffcb1b8609f017f58511473ecb
This commit is contained in:
Cykesiopka 2017-01-12 00:56:25 +08:00
parent 6d2f73025d
commit 501d857ec4
6 changed files with 1 additions and 1442 deletions

View File

@ -1,986 +0,0 @@
/* -*- 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

View File

@ -1,141 +0,0 @@
/* -*- 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

View File

@ -1,239 +0,0 @@
/* -*- 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;
}
}
}

View File

@ -1,57 +0,0 @@
/* -*- 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

View File

@ -1,18 +0,0 @@
# -*- 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'

View File

@ -18,7 +18,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
DIRS += ['unixfd', 'unixsocket']
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
DIRS += ['hal', 'keystore', 'netd']
DIRS += ['hal', 'netd']
if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
DIRS += ['contentproc']