michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set sw=4 ts=8 et ft=cpp: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #undef CHROMIUM_LOG michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: #include michael@0: #define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args) michael@0: #else michael@0: #define CHROMIUM_LOG(args...) printf(args); michael@0: #endif michael@0: michael@0: #include "KeyStore.h" michael@0: #include "jsfriendapi.h" michael@0: #include "MainThreadUtils.h" // For NS_IsMainThread. michael@0: michael@0: #include "plbase64.h" michael@0: #include "certdb.h" michael@0: #include "ScopedNSSTypes.h" michael@0: michael@0: using namespace mozilla::ipc; michael@0: michael@0: namespace mozilla { michael@0: namespace ipc { michael@0: michael@0: static const char* KEYSTORE_SOCKET_NAME = "keystore"; michael@0: static const char* KEYSTORE_SOCKET_PATH = "/dev/socket/keystore"; michael@0: michael@0: int michael@0: KeyStoreConnector::Create() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: int fd; michael@0: michael@0: unlink(KEYSTORE_SOCKET_PATH); michael@0: michael@0: fd = socket(AF_LOCAL, SOCK_STREAM, 0); michael@0: michael@0: if (fd < 0) { michael@0: NS_WARNING("Could not open keystore socket!"); michael@0: return -1; michael@0: } michael@0: michael@0: return fd; michael@0: } michael@0: michael@0: bool michael@0: KeyStoreConnector::CreateAddr(bool aIsServer, michael@0: socklen_t& aAddrSize, michael@0: sockaddr_any& aAddr, michael@0: const char* aAddress) michael@0: { michael@0: // Keystore socket must be server michael@0: MOZ_ASSERT(aIsServer); michael@0: michael@0: aAddr.un.sun_family = AF_LOCAL; michael@0: if(strlen(KEYSTORE_SOCKET_PATH) > sizeof(aAddr.un.sun_path)) { michael@0: NS_WARNING("Address too long for socket struct!"); michael@0: return false; michael@0: } michael@0: strcpy((char*)&aAddr.un.sun_path, KEYSTORE_SOCKET_PATH); michael@0: aAddrSize = strlen(KEYSTORE_SOCKET_PATH) + offsetof(struct sockaddr_un, sun_path) + 1; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: KeyStoreConnector::SetUp(int aFd) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: KeyStoreConnector::SetUpListenSocket(int aFd) michael@0: { michael@0: // Allow access of wpa_supplicant(different user, differnt group) michael@0: chmod(KEYSTORE_SOCKET_PATH, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: KeyStoreConnector::GetSocketAddr(const sockaddr_any& aAddr, michael@0: nsAString& aAddrStr) michael@0: { michael@0: // Unused. michael@0: MOZ_CRASH("This should never be called!"); michael@0: } michael@0: michael@0: static char * michael@0: get_cert_db_filename(void *arg, int vers) michael@0: { michael@0: static char keystoreDbPath[] = "/data/misc/wifi/keystore"; michael@0: return keystoreDbPath; michael@0: } michael@0: michael@0: KeyStore::KeyStore() michael@0: { michael@0: // Initial NSS michael@0: certdb = CERT_GetDefaultCertDB(); michael@0: michael@0: Listen(); michael@0: } michael@0: michael@0: void michael@0: KeyStore::Shutdown() michael@0: { michael@0: mShutdown = true; michael@0: CloseSocket(); michael@0: } michael@0: michael@0: void michael@0: KeyStore::Listen() michael@0: { michael@0: ListenSocket(new KeyStoreConnector()); michael@0: michael@0: ResetHandlerInfo(); michael@0: } michael@0: michael@0: void michael@0: KeyStore::ResetHandlerInfo() michael@0: { michael@0: mHandlerInfo.state = STATE_IDLE; michael@0: mHandlerInfo.command = 0; michael@0: mHandlerInfo.paramCount = 0; michael@0: mHandlerInfo.commandPattern = nullptr; michael@0: for (int i = 0; i < MAX_PARAM; i++) { michael@0: mHandlerInfo.param[i].length = 0; michael@0: memset(mHandlerInfo.param[i].data, 0, VALUE_SIZE); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: KeyStore::CheckSize(UnixSocketRawData *aMessage, size_t aExpectSize) michael@0: { michael@0: return (aMessage->mSize - aMessage->mCurrentWriteOffset >= aExpectSize) ? michael@0: true : false; michael@0: } michael@0: michael@0: bool michael@0: KeyStore::ReadCommand(UnixSocketRawData *aMessage) michael@0: { michael@0: if (mHandlerInfo.state != STATE_IDLE) { michael@0: NS_WARNING("Wrong state in ReadCommand()!"); michael@0: return false; michael@0: } michael@0: michael@0: if (!CheckSize(aMessage, 1)) { michael@0: NS_WARNING("Data size error in ReadCommand()!"); michael@0: return false; michael@0: } michael@0: michael@0: mHandlerInfo.command = aMessage->mData[aMessage->mCurrentWriteOffset]; michael@0: aMessage->mCurrentWriteOffset++; michael@0: michael@0: // Find corrsponding command pattern michael@0: const struct ProtocolCommand *command = commands; michael@0: while (command->command && command->command != mHandlerInfo.command) { michael@0: command++; michael@0: } michael@0: michael@0: if (!command->command) { michael@0: NS_WARNING("Unsupported command!"); michael@0: return false; michael@0: } michael@0: michael@0: // Get command pattern. michael@0: mHandlerInfo.commandPattern = command; michael@0: if (command->paramNum) { michael@0: // Read command parameter if needed. michael@0: mHandlerInfo.state = STATE_READ_PARAM_LEN; michael@0: } else { michael@0: mHandlerInfo.state = STATE_PROCESSING; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: KeyStore::ReadLength(UnixSocketRawData *aMessage) michael@0: { michael@0: if (mHandlerInfo.state != STATE_READ_PARAM_LEN) { michael@0: NS_WARNING("Wrong state in ReadLength()!"); michael@0: return false; michael@0: } michael@0: michael@0: if (!CheckSize(aMessage, 2)) { michael@0: NS_WARNING("Data size error in ReadLength()!"); michael@0: return false; michael@0: } michael@0: michael@0: // Read length of command parameter. michael@0: unsigned short dataLength; michael@0: memcpy(&dataLength, &aMessage->mData[aMessage->mCurrentWriteOffset], 2); michael@0: aMessage->mCurrentWriteOffset += 2; michael@0: mHandlerInfo.param[mHandlerInfo.paramCount].length = ntohs(dataLength); michael@0: michael@0: mHandlerInfo.state = STATE_READ_PARAM_DATA; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: KeyStore::ReadData(UnixSocketRawData *aMessage) michael@0: { michael@0: if (mHandlerInfo.state != STATE_READ_PARAM_DATA) { michael@0: NS_WARNING("Wrong state in ReadData()!"); michael@0: return false; michael@0: } michael@0: michael@0: if (!CheckSize(aMessage, mHandlerInfo.param[mHandlerInfo.paramCount].length)) { michael@0: NS_WARNING("Data size error in ReadData()!"); michael@0: return false; michael@0: } michael@0: michael@0: // Read command parameter. michael@0: memcpy(mHandlerInfo.param[mHandlerInfo.paramCount].data, michael@0: &aMessage->mData[aMessage->mCurrentWriteOffset], michael@0: mHandlerInfo.param[mHandlerInfo.paramCount].length); michael@0: aMessage->mCurrentWriteOffset += mHandlerInfo.param[mHandlerInfo.paramCount].length; michael@0: mHandlerInfo.paramCount++; michael@0: michael@0: if (mHandlerInfo.paramCount == mHandlerInfo.commandPattern->paramNum) { michael@0: mHandlerInfo.state = STATE_PROCESSING; michael@0: } else { michael@0: mHandlerInfo.state = STATE_READ_PARAM_LEN; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Transform base64 certification data into DER format michael@0: void michael@0: KeyStore::FormatCaData(const uint8_t *aCaData, int aCaDataLength, michael@0: const char *aName, const uint8_t **aFormatData, michael@0: int &aFormatDataLength) michael@0: { michael@0: int bufSize = strlen(CA_BEGIN) + strlen(CA_END) + strlen(CA_TAILER) * 2 + michael@0: strlen(aName) * 2 + aCaDataLength + aCaDataLength/CA_LINE_SIZE + 2; michael@0: char *buf = (char *)malloc(bufSize); michael@0: michael@0: aFormatDataLength = bufSize; michael@0: *aFormatData = (const uint8_t *)buf; michael@0: michael@0: char *ptr = buf; michael@0: int len; michael@0: michael@0: // Create DER header. michael@0: len = snprintf(ptr, bufSize, "%s%s%s", CA_BEGIN, aName, CA_TAILER); michael@0: ptr += len; michael@0: bufSize -= len; michael@0: michael@0: // Split base64 data in lines. michael@0: int copySize; michael@0: while (aCaDataLength > 0) { michael@0: copySize = (aCaDataLength > CA_LINE_SIZE) ? CA_LINE_SIZE : aCaDataLength; michael@0: michael@0: memcpy(ptr, aCaData, copySize); michael@0: ptr += copySize; michael@0: aCaData += copySize; michael@0: aCaDataLength -= copySize; michael@0: bufSize -= copySize; michael@0: michael@0: *ptr = '\n'; michael@0: ptr++; michael@0: bufSize--; michael@0: } michael@0: michael@0: // Create DEA tailer. michael@0: snprintf(ptr, bufSize, "%s%s%s", CA_END, aName, CA_TAILER); michael@0: } michael@0: michael@0: // Status response michael@0: void michael@0: KeyStore::SendResponse(ResponseCode aResponse) michael@0: { michael@0: if (aResponse == NO_RESPONSE) michael@0: return; michael@0: michael@0: uint8_t response = (uint8_t)aResponse; michael@0: UnixSocketRawData* data = new UnixSocketRawData((const void *)&response, 1); michael@0: SendSocketData(data); michael@0: } michael@0: michael@0: // Data response michael@0: void michael@0: KeyStore::SendData(const uint8_t *aData, int aLength) michael@0: { michael@0: unsigned short dataLength = htons(aLength); michael@0: michael@0: UnixSocketRawData* length = new UnixSocketRawData((const void *)&dataLength, 2); michael@0: SendSocketData(length); michael@0: michael@0: UnixSocketRawData* data = new UnixSocketRawData((const void *)aData, aLength); michael@0: SendSocketData(data); michael@0: } michael@0: michael@0: void michael@0: KeyStore::ReceiveSocketData(nsAutoPtr& aMessage) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: bool success = true; michael@0: while (aMessage->mCurrentWriteOffset < aMessage->mSize || michael@0: mHandlerInfo.state == STATE_PROCESSING) { michael@0: switch (mHandlerInfo.state) { michael@0: case STATE_IDLE: michael@0: success = ReadCommand(aMessage); michael@0: break; michael@0: case STATE_READ_PARAM_LEN: michael@0: success = ReadLength(aMessage); michael@0: break; michael@0: case STATE_READ_PARAM_DATA: michael@0: success = ReadData(aMessage); michael@0: break; michael@0: case STATE_PROCESSING: michael@0: success = false; michael@0: if (mHandlerInfo.command == 'g') { michael@0: // Get CA michael@0: const uint8_t *certData; michael@0: int certDataLength; michael@0: const char *certName = (const char *)mHandlerInfo.param[0].data; michael@0: michael@0: // Get cert from NSS by name michael@0: ScopedCERTCertificate cert(CERT_FindCertByNickname(certdb, certName)); michael@0: if (!cert) { michael@0: break; michael@0: } michael@0: michael@0: char *certDER = PL_Base64Encode((const char *)cert->derCert.data, michael@0: cert->derCert.len, nullptr); michael@0: if (!certDER) { michael@0: break; michael@0: } michael@0: michael@0: FormatCaData((const uint8_t *)certDER, strlen(certDER), "CERTIFICATE", michael@0: &certData, certDataLength); michael@0: PL_strfree(certDER); michael@0: michael@0: SendResponse(SUCCESS); michael@0: SendData(certData, certDataLength); michael@0: success = true; michael@0: michael@0: free((void *)certData); michael@0: } michael@0: michael@0: ResetHandlerInfo(); michael@0: break; michael@0: } michael@0: michael@0: if (!success) { michael@0: SendResponse(PROTOCOL_ERROR); michael@0: ResetHandlerInfo(); michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: KeyStore::OnConnectSuccess() michael@0: { michael@0: mShutdown = false; michael@0: } michael@0: michael@0: void michael@0: KeyStore::OnConnectError() michael@0: { michael@0: if (!mShutdown) { michael@0: Listen(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: KeyStore::OnDisconnect() michael@0: { michael@0: if (!mShutdown) { michael@0: Listen(); michael@0: } michael@0: } michael@0: michael@0: } // namespace ipc michael@0: } // namespace mozilla