Files
tubestation/netwerk/ipc/SocketProcessParent.cpp
Dana Keeler a13bccff29 Bug 1712837 - introduce ipcclientcerts to allow client certificates to work with the socket process r=rmf,kershaw,necko-reviewers,ipc-reviewers,nika,jschanck
This patch introduces ipcclientcerts, a PKCS#11 module that the socket process
can load to get access to client certificates and keys managed by the parent
process. This enables client certificate authentication to work with the socket
process (particularly for keys stored outside of NSS, as with osclientcerts or
third-party PKCS#11 modules).

Differential Revision: https://phabricator.services.mozilla.com/D122392
2021-12-01 18:10:34 +00:00

474 lines
16 KiB
C++

/* -*- 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 "SocketProcessParent.h"
#include "SocketProcessLogging.h"
#include "AltServiceParent.h"
#include "CachePushChecker.h"
#include "HttpTransactionParent.h"
#include "SocketProcessHost.h"
#include "mozilla/Components.h"
#include "mozilla/dom/MemoryReportRequest.h"
#include "mozilla/ipc/FileDescriptorSetParent.h"
#include "mozilla/ipc/IPCStreamAlloc.h"
#include "mozilla/ipc/PChildToParentStreamParent.h"
#include "mozilla/ipc/PParentToChildStreamParent.h"
#include "mozilla/net/DNSRequestParent.h"
#include "mozilla/net/ProxyConfigLookupParent.h"
#include "mozilla/RemoteLazyInputStreamParent.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TelemetryIPC.h"
#include "nsIAppStartup.h"
#include "nsIConsoleService.h"
#include "nsIHttpActivityObserver.h"
#include "nsIObserverService.h"
#include "nsNSSComponent.h"
#include "nsNSSIOLayer.h"
#include "nsIOService.h"
#include "nsHttpHandler.h"
#include "nsHttpConnectionInfo.h"
#include "PSMIPCCommon.h"
#include "secerr.h"
#ifdef MOZ_WEBRTC
# include "mozilla/dom/ContentProcessManager.h"
# include "mozilla/dom/BrowserParent.h"
# include "mozilla/net/WebrtcTCPSocketParent.h"
#endif
#if defined(MOZ_WIDGET_ANDROID)
# include "mozilla/java/GeckoProcessManagerWrappers.h"
# include "mozilla/java/GeckoProcessTypeWrappers.h"
#endif // defined(MOZ_WIDGET_ANDROID)
namespace mozilla {
namespace net {
static SocketProcessParent* sSocketProcessParent;
SocketProcessParent::SocketProcessParent(SocketProcessHost* aHost)
: mHost(aHost) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mHost);
MOZ_COUNT_CTOR(SocketProcessParent);
sSocketProcessParent = this;
}
SocketProcessParent::~SocketProcessParent() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_DTOR(SocketProcessParent);
sSocketProcessParent = nullptr;
}
/* static */
SocketProcessParent* SocketProcessParent::GetSingleton() {
MOZ_ASSERT(NS_IsMainThread());
return sSocketProcessParent;
}
void SocketProcessParent::ActorDestroy(ActorDestroyReason aWhy) {
#if defined(MOZ_WIDGET_ANDROID)
nsCOMPtr<nsIEventTarget> launcherThread(ipc::GetIPCLauncher());
MOZ_ASSERT(launcherThread);
auto procType = java::GeckoProcessType::SOCKET();
auto selector =
java::GeckoProcessManager::Selector::New(procType, OtherPid());
launcherThread->Dispatch(NS_NewRunnableFunction(
"SocketProcessParent::ActorDestroy",
[selector = java::GeckoProcessManager::Selector::GlobalRef(selector)]() {
java::GeckoProcessManager::ShutdownProcess(selector);
}));
#endif // defined(MOZ_WIDGET_ANDROID)
if (aWhy == AbnormalShutdown) {
GenerateCrashReport(OtherPid());
if (PR_GetEnv("MOZ_CRASHREPORTER_SHUTDOWN")) {
printf_stderr("Shutting down due to socket process crash.\n");
nsCOMPtr<nsIAppStartup> appService =
do_GetService("@mozilla.org/toolkit/app-startup;1");
if (appService) {
bool userAllowedQuit = true;
appService->Quit(nsIAppStartup::eForceQuit, 1, &userAllowedQuit);
}
}
}
if (mHost) {
mHost->OnChannelClosed();
}
}
bool SocketProcessParent::SendRequestMemoryReport(
const uint32_t& aGeneration, const bool& aAnonymize,
const bool& aMinimizeMemoryUsage,
const Maybe<ipc::FileDescriptor>& aDMDFile) {
mMemoryReportRequest = MakeUnique<dom::MemoryReportRequestHost>(aGeneration);
PSocketProcessParent::SendRequestMemoryReport(
aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile,
[&](const uint32_t& aGeneration2) {
MOZ_ASSERT(gIOService);
if (!gIOService->SocketProcess()) {
return;
}
SocketProcessParent* actor = gIOService->SocketProcess()->GetActor();
if (!actor) {
return;
}
if (actor->mMemoryReportRequest) {
actor->mMemoryReportRequest->Finish(aGeneration2);
actor->mMemoryReportRequest = nullptr;
}
},
[&](mozilla::ipc::ResponseRejectReason) {
MOZ_ASSERT(gIOService);
if (!gIOService->SocketProcess()) {
return;
}
SocketProcessParent* actor = gIOService->SocketProcess()->GetActor();
if (!actor) {
return;
}
actor->mMemoryReportRequest = nullptr;
});
return true;
}
mozilla::ipc::IPCResult SocketProcessParent::RecvAddMemoryReport(
const MemoryReport& aReport) {
if (mMemoryReportRequest) {
mMemoryReportRequest->RecvReport(aReport);
}
return IPC_OK();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvAccumulateChildHistograms(
nsTArray<HistogramAccumulation>&& aAccumulations) {
TelemetryIPC::AccumulateChildHistograms(Telemetry::ProcessID::Socket,
aAccumulations);
return IPC_OK();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvAccumulateChildKeyedHistograms(
nsTArray<KeyedHistogramAccumulation>&& aAccumulations) {
TelemetryIPC::AccumulateChildKeyedHistograms(Telemetry::ProcessID::Socket,
aAccumulations);
return IPC_OK();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvUpdateChildScalars(
nsTArray<ScalarAction>&& aScalarActions) {
TelemetryIPC::UpdateChildScalars(Telemetry::ProcessID::Socket,
aScalarActions);
return IPC_OK();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvUpdateChildKeyedScalars(
nsTArray<KeyedScalarAction>&& aScalarActions) {
TelemetryIPC::UpdateChildKeyedScalars(Telemetry::ProcessID::Socket,
aScalarActions);
return IPC_OK();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvRecordChildEvents(
nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents) {
TelemetryIPC::RecordChildEvents(Telemetry::ProcessID::Socket, aEvents);
return IPC_OK();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvRecordDiscardedData(
const mozilla::Telemetry::DiscardedData& aDiscardedData) {
TelemetryIPC::RecordDiscardedData(Telemetry::ProcessID::Socket,
aDiscardedData);
return IPC_OK();
}
PWebrtcTCPSocketParent* SocketProcessParent::AllocPWebrtcTCPSocketParent(
const Maybe<TabId>& aTabId) {
#ifdef MOZ_WEBRTC
WebrtcTCPSocketParent* parent = new WebrtcTCPSocketParent(aTabId);
parent->AddRef();
return parent;
#else
return nullptr;
#endif
}
bool SocketProcessParent::DeallocPWebrtcTCPSocketParent(
PWebrtcTCPSocketParent* aActor) {
#ifdef MOZ_WEBRTC
WebrtcTCPSocketParent* parent = static_cast<WebrtcTCPSocketParent*>(aActor);
parent->Release();
#endif
return true;
}
already_AddRefed<PDNSRequestParent> SocketProcessParent::AllocPDNSRequestParent(
const nsCString& aHost, const nsCString& aTrrServer, const uint16_t& aType,
const OriginAttributes& aOriginAttributes, const uint32_t& aFlags) {
RefPtr<DNSRequestHandler> handler = new DNSRequestHandler();
RefPtr<DNSRequestParent> actor = new DNSRequestParent(handler);
return actor.forget();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvPDNSRequestConstructor(
PDNSRequestParent* aActor, const nsCString& aHost,
const nsCString& aTrrServer, const uint16_t& aType,
const OriginAttributes& aOriginAttributes, const uint32_t& aFlags) {
RefPtr<DNSRequestParent> actor = static_cast<DNSRequestParent*>(aActor);
RefPtr<DNSRequestHandler> handler =
actor->GetDNSRequest()->AsDNSRequestHandler();
handler->DoAsyncResolve(aHost, aTrrServer, aType, aOriginAttributes, aFlags);
return IPC_OK();
}
mozilla::ipc::PFileDescriptorSetParent*
SocketProcessParent::AllocPFileDescriptorSetParent(const FileDescriptor& aFD) {
return new mozilla::ipc::FileDescriptorSetParent(aFD);
}
bool SocketProcessParent::DeallocPFileDescriptorSetParent(
PFileDescriptorSetParent* aActor) {
delete static_cast<mozilla::ipc::FileDescriptorSetParent*>(aActor);
return true;
}
mozilla::ipc::PChildToParentStreamParent*
SocketProcessParent::AllocPChildToParentStreamParent() {
return mozilla::ipc::AllocPChildToParentStreamParent();
}
bool SocketProcessParent::DeallocPChildToParentStreamParent(
PChildToParentStreamParent* aActor) {
delete aActor;
return true;
}
mozilla::ipc::PParentToChildStreamParent*
SocketProcessParent::AllocPParentToChildStreamParent() {
MOZ_CRASH("PParentToChildStreamChild actors should be manually constructed!");
}
bool SocketProcessParent::DeallocPParentToChildStreamParent(
PParentToChildStreamParent* aActor) {
delete aActor;
return true;
}
mozilla::ipc::PParentToChildStreamParent*
SocketProcessParent::SendPParentToChildStreamConstructor(
PParentToChildStreamParent* aActor) {
MOZ_ASSERT(NS_IsMainThread());
return PSocketProcessParent::SendPParentToChildStreamConstructor(aActor);
}
mozilla::ipc::PFileDescriptorSetParent*
SocketProcessParent::SendPFileDescriptorSetConstructor(
const FileDescriptor& aFD) {
MOZ_ASSERT(NS_IsMainThread());
return PSocketProcessParent::SendPFileDescriptorSetConstructor(aFD);
}
mozilla::ipc::IPCResult SocketProcessParent::RecvObserveHttpActivity(
const HttpActivityArgs& aArgs, const uint32_t& aActivityType,
const uint32_t& aActivitySubtype, const PRTime& aTimestamp,
const uint64_t& aExtraSizeData, const nsCString& aExtraStringData) {
nsCOMPtr<nsIHttpActivityDistributor> activityDistributor =
components::HttpActivityDistributor::Service();
MOZ_ASSERT(activityDistributor);
Unused << activityDistributor->ObserveActivityWithArgs(
aArgs, aActivityType, aActivitySubtype, aTimestamp, aExtraSizeData,
aExtraStringData);
return IPC_OK();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvInitBackground(
Endpoint<PBackgroundParent>&& aEndpoint) {
LOG(("SocketProcessParent::RecvInitBackground\n"));
if (!ipc::BackgroundParent::Alloc(nullptr, std::move(aEndpoint))) {
return IPC_FAIL(this, "BackgroundParent::Alloc failed");
}
return IPC_OK();
}
already_AddRefed<PAltServiceParent>
SocketProcessParent::AllocPAltServiceParent() {
RefPtr<AltServiceParent> actor = new AltServiceParent();
return actor.forget();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvGetTLSClientCert(
const nsCString& aHostName, const OriginAttributes& aOriginAttributes,
const int32_t& aPort, const uint32_t& aProviderFlags,
const uint32_t& aProviderTlsFlags, const ByteArray& aServerCert,
Maybe<ByteArray>&& aClientCert, nsTArray<ByteArray>&& aCollectedCANames,
bool* aSucceeded, ByteArray* aOutCert, nsTArray<ByteArray>* aBuiltChain) {
*aSucceeded = false;
SECItem serverCertItem = {
siBuffer, const_cast<uint8_t*>(aServerCert.data().Elements()),
static_cast<unsigned int>(aServerCert.data().Length())};
UniqueCERTCertificate serverCert(CERT_NewTempCertificate(
CERT_GetDefaultCertDB(), &serverCertItem, nullptr, false, true));
if (!serverCert) {
return IPC_OK();
}
RefPtr<nsIX509Cert> clientCert;
if (aClientCert) {
clientCert = nsNSSCertificate::ConstructFromDER(
BitwiseCast<char*, uint8_t*>(aClientCert->data().Elements()),
aClientCert->data().Length());
if (!clientCert) {
return IPC_OK();
}
}
ClientAuthInfo info(aHostName, aOriginAttributes, aPort, aProviderFlags,
aProviderTlsFlags, clientCert);
nsTArray<nsTArray<uint8_t>> collectedCANames;
for (auto& name : aCollectedCANames) {
collectedCANames.AppendElement(std::move(name.data()));
}
UniqueCERTCertificate cert;
UniqueCERTCertList builtChain;
SECStatus status =
DoGetClientAuthData(std::move(info), serverCert,
std::move(collectedCANames), cert, builtChain);
if (status != SECSuccess) {
return IPC_OK();
}
aOutCert->data().AppendElements(cert->derCert.data, cert->derCert.len);
if (builtChain) {
for (CERTCertListNode* n = CERT_LIST_HEAD(builtChain);
!CERT_LIST_END(n, builtChain); n = CERT_LIST_NEXT(n)) {
ByteArray array;
array.data().AppendElements(n->cert->derCert.data, n->cert->derCert.len);
aBuiltChain->AppendElement(std::move(array));
}
}
*aSucceeded = true;
return IPC_OK();
}
already_AddRefed<PProxyConfigLookupParent>
SocketProcessParent::AllocPProxyConfigLookupParent(
nsIURI* aURI, const uint32_t& aProxyResolveFlags) {
RefPtr<ProxyConfigLookupParent> actor =
new ProxyConfigLookupParent(aURI, aProxyResolveFlags);
return actor.forget();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvPProxyConfigLookupConstructor(
PProxyConfigLookupParent* aActor, nsIURI* aURI,
const uint32_t& aProxyResolveFlags) {
static_cast<ProxyConfigLookupParent*>(aActor)->DoProxyLookup();
return IPC_OK();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvCachePushCheck(
nsIURI* aPushedURL, OriginAttributes&& aOriginAttributes,
nsCString&& aRequestString, CachePushCheckResolver&& aResolver) {
RefPtr<CachePushChecker> checker = new CachePushChecker(
aPushedURL, aOriginAttributes, aRequestString, aResolver);
if (NS_FAILED(checker->DoCheck())) {
aResolver(false);
}
return IPC_OK();
}
// To ensure that IPDL is finished before SocketParent gets deleted.
class DeferredDeleteSocketProcessParent : public Runnable {
public:
explicit DeferredDeleteSocketProcessParent(
UniquePtr<SocketProcessParent>&& aParent)
: Runnable("net::DeferredDeleteSocketProcessParent"),
mParent(std::move(aParent)) {}
NS_IMETHODIMP Run() override { return NS_OK; }
private:
UniquePtr<SocketProcessParent> mParent;
};
/* static */
void SocketProcessParent::Destroy(UniquePtr<SocketProcessParent>&& aParent) {
NS_DispatchToMainThread(
new DeferredDeleteSocketProcessParent(std::move(aParent)));
}
already_AddRefed<PRemoteLazyInputStreamParent>
SocketProcessParent::AllocPRemoteLazyInputStreamParent(const nsID& aID,
const uint64_t& aSize) {
RefPtr<RemoteLazyInputStreamParent> actor =
RemoteLazyInputStreamParent::Create(aID, aSize, this);
return actor.forget();
}
mozilla::ipc::IPCResult
SocketProcessParent::RecvPRemoteLazyInputStreamConstructor(
PRemoteLazyInputStreamParent* aActor, const nsID& aID,
const uint64_t& aSize) {
if (!static_cast<RemoteLazyInputStreamParent*>(aActor)->HasValidStream()) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvODoHServiceActivated(
const bool& aActivated) {
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
observerService->NotifyObservers(nullptr, "odoh-service-activated",
aActivated ? u"true" : u"false");
}
return IPC_OK();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvExcludeHttp2OrHttp3(
const HttpConnectionInfoCloneArgs& aArgs) {
RefPtr<nsHttpConnectionInfo> cinfo =
nsHttpConnectionInfo::DeserializeHttpConnectionInfoCloneArgs(aArgs);
if (!cinfo) {
MOZ_ASSERT(false, "failed to deserizlize http connection info");
return IPC_OK();
}
if (cinfo->IsHttp3()) {
gHttpHandler->ExcludeHttp3(cinfo);
} else {
gHttpHandler->ExcludeHttp2(cinfo);
}
return IPC_OK();
}
mozilla::ipc::IPCResult SocketProcessParent::RecvOnConsoleMessage(
const nsString& aMessage) {
nsCOMPtr<nsIConsoleService> consoleService =
do_GetService(NS_CONSOLESERVICE_CONTRACTID);
if (consoleService) {
consoleService->LogStringMessage(aMessage.get());
}
return IPC_OK();
}
} // namespace net
} // namespace mozilla