/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* vim: set sw=4 ts=8 et ft=cpp: */ /* 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 #include #undef LOG #if defined(MOZ_WIDGET_GONK) #include #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args) #else #define LOG(args...) printf(args); #endif #include "KeyStore.h" #include "jsfriendapi.h" #include "MainThreadUtils.h" // For NS_IsMainThread. #include "plbase64.h" #include "certdb.h" #include "ScopedNSSTypes.h" using namespace mozilla::ipc; namespace mozilla { namespace ipc { static const char* KEYSTORE_SOCKET_NAME = "keystore"; static const char* KEYSTORE_SOCKET_PATH = "/dev/socket/keystore"; int KeyStoreConnector::Create() { MOZ_ASSERT(!NS_IsMainThread()); int fd; unlink(KEYSTORE_SOCKET_PATH); fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd < 0) { NS_WARNING("Could not open keystore socket!"); return -1; } return fd; } bool KeyStoreConnector::CreateAddr(bool aIsServer, socklen_t& aAddrSize, sockaddr_any& aAddr, const char* aAddress) { // Keystore socket must be server MOZ_ASSERT(aIsServer); aAddr.un.sun_family = AF_LOCAL; if(strlen(KEYSTORE_SOCKET_PATH) > sizeof(aAddr.un.sun_path)) { NS_WARNING("Address too long for socket struct!"); return false; } strcpy((char*)&aAddr.un.sun_path, KEYSTORE_SOCKET_PATH); aAddrSize = strlen(KEYSTORE_SOCKET_PATH) + offsetof(struct sockaddr_un, sun_path) + 1; return true; } bool KeyStoreConnector::SetUp(int aFd) { return true; } 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); return true; } void KeyStoreConnector::GetSocketAddr(const sockaddr_any& aAddr, nsAString& aAddrStr) { // Unused. MOZ_CRASH("This should never be called!"); } 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(); Listen(); } void KeyStore::Shutdown() { mShutdown = true; CloseSocket(); } void KeyStore::Listen() { ListenSocket(new KeyStoreConnector()); 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(UnixSocketRawData *aMessage, size_t aExpectSize) { return (aMessage->mSize - aMessage->mCurrentWriteOffset >= aExpectSize) ? true : false; } bool KeyStore::ReadCommand(UnixSocketRawData *aMessage) { if (mHandlerInfo.state != STATE_IDLE) { NS_WARNING("Wrong state in ReadCommand()!"); return false; } if (!CheckSize(aMessage, 1)) { NS_WARNING("Data size error in ReadCommand()!"); return false; } mHandlerInfo.command = aMessage->mData[aMessage->mCurrentWriteOffset]; aMessage->mCurrentWriteOffset++; // 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 false; } // 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 true; } bool KeyStore::ReadLength(UnixSocketRawData *aMessage) { if (mHandlerInfo.state != STATE_READ_PARAM_LEN) { NS_WARNING("Wrong state in ReadLength()!"); return false; } if (!CheckSize(aMessage, 2)) { NS_WARNING("Data size error in ReadLength()!"); return false; } // Read length of command parameter. unsigned short dataLength; memcpy(&dataLength, &aMessage->mData[aMessage->mCurrentWriteOffset], 2); aMessage->mCurrentWriteOffset += 2; mHandlerInfo.param[mHandlerInfo.paramCount].length = ntohs(dataLength); mHandlerInfo.state = STATE_READ_PARAM_DATA; return true; } bool KeyStore::ReadData(UnixSocketRawData *aMessage) { if (mHandlerInfo.state != STATE_READ_PARAM_DATA) { NS_WARNING("Wrong state in ReadData()!"); return false; } if (!CheckSize(aMessage, mHandlerInfo.param[mHandlerInfo.paramCount].length)) { NS_WARNING("Data size error in ReadData()!"); return false; } // Read command parameter. memcpy(mHandlerInfo.param[mHandlerInfo.paramCount].data, &aMessage->mData[aMessage->mCurrentWriteOffset], mHandlerInfo.param[mHandlerInfo.paramCount].length); aMessage->mCurrentWriteOffset += 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 true; } // 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; UnixSocketRawData* data = new UnixSocketRawData((const void *)&response, 1); SendSocketData(data); } // Data response void KeyStore::SendData(const uint8_t *aData, int aLength) { unsigned short dataLength = htons(aLength); UnixSocketRawData* length = new UnixSocketRawData((const void *)&dataLength, 2); SendSocketData(length); UnixSocketRawData* data = new UnixSocketRawData((const void *)aData, aLength); SendSocketData(data); } void KeyStore::ReceiveSocketData(nsAutoPtr& aMessage) { MOZ_ASSERT(NS_IsMainThread()); bool success = true; while (aMessage->mCurrentWriteOffset < aMessage->mSize || mHandlerInfo.state == STATE_PROCESSING) { switch (mHandlerInfo.state) { case STATE_IDLE: success = ReadCommand(aMessage); break; case STATE_READ_PARAM_LEN: success = ReadLength(aMessage); break; case STATE_READ_PARAM_DATA: success = ReadData(aMessage); break; case STATE_PROCESSING: success = false; if (mHandlerInfo.command == 'g') { // Get CA const uint8_t *certData; int certDataLength; const char *certName = (const char *)mHandlerInfo.param[0].data; // Get cert from NSS by name ScopedCERTCertificate cert(CERT_FindCertByNickname(certdb, certName)); if (!cert) { break; } char *certDER = PL_Base64Encode((const char *)cert->derCert.data, cert->derCert.len, nullptr); if (!certDER) { break; } FormatCaData((const uint8_t *)certDER, strlen(certDER), "CERTIFICATE", &certData, certDataLength); PL_strfree(certDER); SendResponse(SUCCESS); SendData(certData, certDataLength); success = true; free((void *)certData); } ResetHandlerInfo(); break; } if (!success) { SendResponse(PROTOCOL_ERROR); ResetHandlerInfo(); return; } } } void KeyStore::OnConnectSuccess() { mShutdown = false; } void KeyStore::OnConnectError() { if (!mShutdown) { Listen(); } } void KeyStore::OnDisconnect() { if (!mShutdown) { Listen(); } } } // namespace ipc } // namespace mozilla