diff --git a/dom/wifi/WifiCertService.cpp b/dom/wifi/WifiCertService.cpp new file mode 100644 index 000000000000..4dbfc64ce1d8 --- /dev/null +++ b/dom/wifi/WifiCertService.cpp @@ -0,0 +1,276 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "WifiCertService.h" + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/ModuleUtils.h" +#include "mozilla/RefPtr.h" +#include "cert.h" +#include "certdb.h" +#include "CryptoTask.h" +#include "nsCxPusher.h" +#include "nsIDOMFile.h" +#include "nsIWifiService.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "nsXULAppAPI.h" +#include "ScopedNSSTypes.h" + +#define NS_WIFICERTSERVICE_CID \ + { 0x83585afd, 0x0e11, 0x43aa, {0x83, 0x46, 0xf3, 0x4d, 0x97, 0x5e, 0x46, 0x77} } + +using namespace mozilla; +using namespace mozilla::dom; + +namespace mozilla { + +// The singleton Wifi Cert service, to be used on the main thread. +StaticRefPtr gWifiCertService; + +class ImportCertTask MOZ_FINAL: public CryptoTask +{ +public: + ImportCertTask(int32_t aId, nsIDOMBlob* aCertBlob, + const nsAString& aCertPassword, + const nsAString& aCertNickname) + : mBlob(aCertBlob) + , mPassword(aCertPassword) + { + MOZ_ASSERT(NS_IsMainThread()); + + mResult.mId = aId; + mResult.mStatus = 0; + mResult.mUsageFlag = 0; + mResult.mNickname = aCertNickname; + } + +private: + virtual void ReleaseNSSResources() {} + + virtual nsresult CalculateResult() MOZ_OVERRIDE + { + MOZ_ASSERT(!NS_IsMainThread()); + + // read data from blob. + nsCString blobBuf; + nsresult rv = ReadBlob(blobBuf); + if (NS_FAILED(rv)) { + return rv; + } + + char* buf; + uint32_t size = blobBuf.GetMutableData(&buf); + if (size == 0) { + return NS_ERROR_OUT_OF_MEMORY; + } + + // Only support DER format now. + return ImportDERBlob(buf, size, mResult.mNickname, + &mResult.mUsageFlag); + } + + virtual void CallCallback(nsresult rv) + { + if (NS_FAILED(rv)) { + mResult.mStatus = -1; + } + gWifiCertService->DispatchResult(mResult); + } + + nsresult ImportDERBlob(char* buf, uint32_t size, + const nsAString& aNickname, + /*out*/ uint16_t* aUsageFlag) + { + NS_ENSURE_ARG_POINTER(aUsageFlag); + + // Create certificate object. + ScopedCERTCertificate cert(CERT_DecodeCertFromPackage(buf, size)); + if (!cert) { + return MapSECStatus(SECFailure); + } + + // Import certificate with nickname. + return ImportCert(cert, aNickname, aUsageFlag); + } + + nsresult ReadBlob(/*out*/ nsCString& aBuf) + { + NS_ENSURE_ARG_POINTER(mBlob); + + static const uint64_t MAX_FILE_SIZE = 16384; + uint64_t size; + nsresult rv = mBlob->GetSize(&size); + if (NS_FAILED(rv)) { + return rv; + } + if (size > MAX_FILE_SIZE) { + return NS_ERROR_FILE_TOO_BIG; + } + + nsCOMPtr inputStream; + rv = mBlob->GetInternalStream(getter_AddRefs(inputStream)); + if (NS_FAILED(rv)) { + return rv; + } + + rv = NS_ReadInputStreamToString(inputStream, aBuf, (uint32_t)size); + if (NS_FAILED(rv)) { + return rv; + } + + return NS_OK; + } + + nsresult ImportCert(CERTCertificate* aCert, const nsAString& aNickname, + /*out*/ uint16_t* aUsageFlag) + { + NS_ENSURE_ARG_POINTER(aUsageFlag); + + nsCString userNickname, fullNickname; + + CopyUTF16toUTF8(aNickname, userNickname); + // Determine certificate nickname by adding prefix according to its type. + if (aCert->isRoot && (aCert->nsCertType & NS_CERT_TYPE_SSL_CA)) { + // Accept self-signed SSL CA as server certificate. + fullNickname.AssignLiteral("WIFI_SERVERCERT_"); + fullNickname += userNickname; + *aUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_SERVER; + } else { + return NS_ERROR_ABORT; + } + + char* nickname; + uint32_t length; + length = fullNickname.GetMutableData(&nickname); + if (length == 0) { + return NS_ERROR_UNEXPECTED; + } + + // Import certificate, duplicated nickname will cause error. + SECStatus srv = CERT_AddTempCertToPerm(aCert, nickname, NULL); + if (srv != SECSuccess) { + return MapSECStatus(srv); + } + + return NS_OK; + } + + nsCOMPtr mBlob; + nsString mPassword; + WifiCertServiceResultOptions mResult; +}; + +NS_IMPL_ISUPPORTS(WifiCertService, nsIWifiCertService) + +NS_IMETHODIMP +WifiCertService::Start(nsIWifiEventListener* aListener) +{ + MOZ_ASSERT(aListener); + + nsresult rv = NS_NewThread(getter_AddRefs(mRequestThread)); + if (NS_FAILED(rv)) { + NS_WARNING("Certn't create wifi control thread"); + Shutdown(); + return NS_ERROR_FAILURE; + } + + mListener = aListener; + + return NS_OK; +} + +NS_IMETHODIMP +WifiCertService::Shutdown() +{ + MOZ_ASSERT(NS_IsMainThread()); + if (mRequestThread) { + mRequestThread->Shutdown(); + mRequestThread = nullptr; + } + return NS_OK; +} + +void +WifiCertService::DispatchResult(const WifiCertServiceResultOptions& aOptions) +{ + MOZ_ASSERT(NS_IsMainThread()); + + mozilla::AutoSafeJSContext cx; + JS::RootedValue val(cx); + nsCString dummyInterface; + + if (!aOptions.ToObject(cx, &val)) { + return; + } + + // Certll the listener with a JS value. + mListener->OnCommand(val, dummyInterface); +} + +WifiCertService::WifiCertService() +{ + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(!gWifiCertService); +} + +WifiCertService::~WifiCertService() +{ + MOZ_ASSERT(!gWifiCertService); +} + +already_AddRefed +WifiCertService::FactoryCreate() +{ + if (XRE_GetProcessType() != GeckoProcessType_Default) { + return nullptr; + } + + MOZ_ASSERT(NS_IsMainThread()); + + if (!gWifiCertService) { + gWifiCertService = new WifiCertService(); + ClearOnShutdown(&gWifiCertService); + } + + nsRefPtr service = gWifiCertService.get(); + return service.forget(); +} + +NS_IMETHODIMP +WifiCertService::ImportCert(int32_t aId, nsIDOMBlob* aCertBlob, + const nsAString& aCertPassword, + const nsAString& aCertNickname) +{ + RefPtr task = new ImportCertTask(aId, aCertBlob, aCertPassword, + aCertNickname); + return task->Dispatch("WifiImportCert"); +} + +NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(WifiCertService, + WifiCertService::FactoryCreate) + +NS_DEFINE_NAMED_CID(NS_WIFICERTSERVICE_CID); + +static const mozilla::Module::CIDEntry kWifiCertServiceCIDs[] = { + { &kNS_WIFICERTSERVICE_CID, false, nullptr, WifiCertServiceConstructor }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kWifiCertServiceContracts[] = { + { "@mozilla.org/wifi/certservice;1", &kNS_WIFICERTSERVICE_CID }, + { nullptr } +}; + +static const mozilla::Module kWifiCertServiceModule = { + mozilla::Module::kVersion, + kWifiCertServiceCIDs, + kWifiCertServiceContracts, + nullptr +}; + +} // namespace mozilla + +NSMODULE_DEFN(WifiCertServiceModule) = &kWifiCertServiceModule; diff --git a/dom/wifi/WifiCertService.h b/dom/wifi/WifiCertService.h new file mode 100644 index 000000000000..219764c57e39 --- /dev/null +++ b/dom/wifi/WifiCertService.h @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 WifiCertService_h +#define WifiCertService_h + +#include "nsIWifiCertService.h" +#include "nsCOMPtr.h" +#include "nsThread.h" +#include "mozilla/dom/WifiOptionsBinding.h" + +namespace mozilla { +namespace dom { + +class WifiCertService MOZ_FINAL : public nsIWifiCertService +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIWIFICERTSERVICE + + static already_AddRefed + FactoryCreate(); + void DispatchResult(const mozilla::dom::WifiCertServiceResultOptions& aOptions); + +private: + WifiCertService(); + ~WifiCertService(); + nsCOMPtr mRequestThread; + nsCOMPtr mListener; +}; + +} // namespce dom +} // namespace mozilla + +#endif // WifiCertService_h diff --git a/dom/wifi/moz.build b/dom/wifi/moz.build index a361f4ff69de..00551a9636c1 100644 --- a/dom/wifi/moz.build +++ b/dom/wifi/moz.build @@ -34,6 +34,7 @@ EXTRA_JS_MODULES += [ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': SOURCES = [ + 'WifiCertService.cpp', 'WifiProxyService.cpp', 'WifiUtils.cpp', ] diff --git a/toolkit/library/nsStaticXULComponents.cpp b/toolkit/library/nsStaticXULComponents.cpp index e47d3d7dcf2a..6378db2946bf 100644 --- a/toolkit/library/nsStaticXULComponents.cpp +++ b/toolkit/library/nsStaticXULComponents.cpp @@ -253,6 +253,7 @@ XUL_MODULES #ifdef MOZ_WIDGET_GONK +MODULE(WifiCertServiceModule) MODULE(WifiProxyServiceModule) MODULE(NetworkWorkerModule) #endif @@ -265,6 +266,7 @@ MODULE(NetworkWorkerModule) extern const mozilla::Module *const *const kPStaticModules[] = { XUL_MODULES #ifdef MOZ_WIDGET_GONK +MODULE(WifiCertServiceModule) MODULE(WifiProxyServiceModule) MODULE(NetworkWorkerModule) #endif