tenfourfox/security/manager/ssl/PSMContentListener.cpp
Cameron Kaiser c9b2922b70 hello FPR
2017-04-19 00:56:45 -07:00

452 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set sw=2 sts=2 ts=2 et 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 "PSMContentListener.h"
#include "nsIDivertableChannel.h"
#include "nsIStreamListener.h"
#include "nsIX509CertDB.h"
#include "nsIXULAppInfo.h"
#include "mozilla/Casting.h"
#include "mozilla/Services.h"
#include "mozilla/unused.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/net/ChannelDiverterParent.h"
#include "mozilla/net/ChannelDiverterChild.h"
#include "nsCRT.h"
#include "nsNetUtil.h"
#include "nsIChannel.h"
#include "nsIInputStream.h"
#include "nsIURI.h"
#include "nsNSSHelper.h"
#include "mozilla/Logging.h"
extern PRLogModuleInfo* gPIPNSSLog;
namespace mozilla { namespace psm {
namespace {
const int32_t kDefaultCertAllocLength = 2048;
enum {
UNKNOWN_TYPE = 0,
X509_CA_CERT = 1,
X509_USER_CERT = 2,
X509_EMAIL_CERT = 3,
X509_SERVER_CERT = 4
};
/* other mime types that we should handle sometime:
application/x-pkcs7-mime
application/pkcs7-signature
application/pre-encrypted
*/
uint32_t
getPSMContentType(const char* aContentType)
{
// Don't forget to update the registration of content listeners in nsNSSModule.cpp
// for every supported content type.
if (!nsCRT::strcasecmp(aContentType, "application/x-x509-ca-cert"))
return X509_CA_CERT;
if (!nsCRT::strcasecmp(aContentType, "application/x-x509-server-cert"))
return X509_SERVER_CERT;
if (!nsCRT::strcasecmp(aContentType, "application/x-x509-user-cert"))
return X509_USER_CERT;
if (!nsCRT::strcasecmp(aContentType, "application/x-x509-email-cert"))
return X509_EMAIL_CERT;
return UNKNOWN_TYPE;
}
int64_t
ComputeContentLength(nsIRequest* request)
{
nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
if (!channel) {
return -1;
}
int64_t contentLength;
nsresult rv = channel->GetContentLength(&contentLength);
if (NS_FAILED(rv) || contentLength <= 0) {
return kDefaultCertAllocLength;
}
if (contentLength > INT32_MAX) {
return -1;
}
return contentLength;
}
} // unnamed namespace
/* ------------------------
* PSMContentStreamListener
* ------------------------ */
PSMContentStreamListener::PSMContentStreamListener(uint32_t type)
: mType(type)
{
}
PSMContentStreamListener::~PSMContentStreamListener()
{
}
NS_IMPL_ISUPPORTS(PSMContentStreamListener, nsIStreamListener, nsIRequestObserver)
NS_IMETHODIMP
PSMContentStreamListener::OnStartRequest(nsIRequest* request, nsISupports* context)
{
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CertDownloader::OnStartRequest\n"));
int64_t contentLength = ComputeContentLength(request);
if (contentLength < 0) {
return NS_ERROR_FAILURE;
}
mByteData.SetCapacity(contentLength);
return NS_OK;
}
NS_IMETHODIMP
PSMContentStreamListener::OnDataAvailable(nsIRequest* request,
nsISupports* context,
nsIInputStream* aIStream,
uint64_t aSourceOffset,
uint32_t aLength)
{
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CertDownloader::OnDataAvailable\n"));
nsCString chunk;
nsresult rv = NS_ReadInputStreamToString(aIStream, chunk, aLength);
if (NS_FAILED(rv)) {
return rv;
}
mByteData.Append(chunk);
return NS_OK;
}
NS_IMETHODIMP
PSMContentStreamListener::OnStopRequest(nsIRequest* request,
nsISupports* context,
nsresult aStatus)
{
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CertDownloader::OnStopRequest\n"));
// Because importing the cert can spin the event loop (via alerts), we can't
// do it here. Do it off the event loop instead.
nsCOMPtr<nsIRunnable> r =
NS_NewRunnableMethod(this, &PSMContentStreamListener::ImportCertificate);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
return NS_OK;
}
void
PSMContentStreamListener::ImportCertificate()
{
nsCOMPtr<nsIX509CertDB> certdb;
nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
switch (mType) {
case X509_CA_CERT:
case X509_USER_CERT:
case X509_EMAIL_CERT:
certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
break;
default:
break;
}
if (!certdb) {
return;
}
switch (mType) {
case X509_CA_CERT:
certdb->ImportCertificates(reinterpret_cast<uint8_t*>(mByteData.BeginWriting()),
mByteData.Length(), mType, ctx);
break;
case X509_USER_CERT:
certdb->ImportUserCertificate(reinterpret_cast<uint8_t*>(mByteData.BeginWriting()),
mByteData.Length(), ctx);
break;
case X509_EMAIL_CERT:
certdb->ImportEmailCertificate(reinterpret_cast<uint8_t*>(mByteData.BeginWriting()),
mByteData.Length(), ctx);
break;
default:
break;
}
}
/* ------------------------
* PSMContentDownloaderParent
* ------------------------ */
PSMContentDownloaderParent::PSMContentDownloaderParent(uint32_t type)
: PSMContentStreamListener(type)
, mIPCOpen(true)
{
}
PSMContentDownloaderParent::~PSMContentDownloaderParent()
{
}
bool
PSMContentDownloaderParent::RecvOnStartRequest(const uint32_t& contentLength)
{
mByteData.SetCapacity(contentLength);
return true;
}
bool
PSMContentDownloaderParent::RecvOnDataAvailable(const nsCString& data,
const uint64_t& offset,
const uint32_t& count)
{
mByteData.Append(data);
return true;
}
bool
PSMContentDownloaderParent::RecvOnStopRequest(const nsresult& code)
{
if (NS_SUCCEEDED(code)) {
// See also PSMContentStreamListener::OnStopRequest. In this case, we don't
// have to dispatch ImportCertificate off of an event because we don't have
// to worry about Necko sending "clean up" events and destroying us if
// ImportCertificate spins the event loop.
ImportCertificate();
}
if (mIPCOpen) {
mozilla::Unused << Send__delete__(this);
}
return true;
}
NS_IMETHODIMP
PSMContentDownloaderParent::OnStopRequest(nsIRequest* request, nsISupports* context, nsresult code)
{
nsresult rv = PSMContentStreamListener::OnStopRequest(request, context, code);
if (mIPCOpen) {
mozilla::Unused << Send__delete__(this);
}
return rv;
}
bool
PSMContentDownloaderParent::RecvDivertToParentUsing(mozilla::net::PChannelDiverterParent* diverter)
{
MOZ_ASSERT(diverter);
auto p = static_cast<mozilla::net::ChannelDiverterParent*>(diverter);
p->DivertTo(this);
mozilla::Unused << p->Send__delete__(p);
return true;
}
void
PSMContentDownloaderParent::ActorDestroy(ActorDestroyReason why)
{
mIPCOpen = false;
}
/* ------------------------
* PSMContentDownloaderChild
* ------------------------ */
NS_IMPL_ISUPPORTS(PSMContentDownloaderChild, nsIStreamListener)
PSMContentDownloaderChild::PSMContentDownloaderChild()
{
}
PSMContentDownloaderChild::~PSMContentDownloaderChild()
{
}
NS_IMETHODIMP
PSMContentDownloaderChild::OnStartRequest(nsIRequest* request, nsISupports* context)
{
nsCOMPtr<nsIDivertableChannel> divertable = do_QueryInterface(request);
if (divertable) {
mozilla::net::ChannelDiverterChild* diverter = nullptr;
nsresult rv = divertable->DivertToParent(&diverter);
if (NS_FAILED(rv)) {
return rv;
}
MOZ_ASSERT(diverter);
return SendDivertToParentUsing(diverter) ? NS_OK : NS_ERROR_FAILURE;
}
int64_t contentLength = ComputeContentLength(request);
if (contentLength < 0) {
return NS_ERROR_FAILURE;
}
mozilla::Unused << SendOnStartRequest(contentLength);
return NS_OK;
}
NS_IMETHODIMP
PSMContentDownloaderChild::OnDataAvailable(nsIRequest* request,
nsISupports* context,
nsIInputStream* aIStream,
uint64_t aSourceOffset,
uint32_t aLength)
{
nsCString chunk;
nsresult rv = NS_ReadInputStreamToString(aIStream, chunk, aLength);
if (NS_FAILED(rv)) {
return rv;
}
mozilla::Unused << SendOnDataAvailable(chunk, aSourceOffset, aLength);
return NS_OK;
}
NS_IMETHODIMP
PSMContentDownloaderChild::OnStopRequest(nsIRequest* request,
nsISupports* context,
nsresult aStatus)
{
mozilla::Unused << SendOnStopRequest(aStatus);
return NS_OK;
}
/* ------------------------
* PSMContentListener
* ------------------------ */
NS_IMPL_ISUPPORTS(PSMContentListener,
nsIURIContentListener,
nsISupportsWeakReference)
PSMContentListener::PSMContentListener()
{
mLoadCookie = nullptr;
mParentContentListener = nullptr;
}
PSMContentListener::~PSMContentListener()
{
}
nsresult
PSMContentListener::init()
{
return NS_OK;
}
NS_IMETHODIMP
PSMContentListener::OnStartURIOpen(nsIURI* aURI, bool* aAbortOpen)
{
//if we don't want to handle the URI, return true in
//*aAbortOpen
return NS_OK;
}
NS_IMETHODIMP
PSMContentListener::IsPreferred(const char* aContentType,
char** aDesiredContentType,
bool* aCanHandleContent)
{
return CanHandleContent(aContentType, true,
aDesiredContentType, aCanHandleContent);
}
NS_IMETHODIMP
PSMContentListener::CanHandleContent(const char* aContentType,
bool aIsContentPreferred,
char** aDesiredContentType,
bool* aCanHandleContent)
{
uint32_t type = getPSMContentType(aContentType);
*aCanHandleContent = (type != UNKNOWN_TYPE);
return NS_OK;
}
NS_IMETHODIMP
PSMContentListener::DoContent(const nsACString& aContentType,
bool aIsContentPreferred,
nsIRequest* aRequest,
nsIStreamListener** aContentHandler,
bool* aAbortProcess)
{
uint32_t type;
type = getPSMContentType(PromiseFlatCString(aContentType).get());
if (gPIPNSSLog) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("PSMContentListener::DoContent\n"));
}
if (type != UNKNOWN_TYPE) {
nsCOMPtr<nsIStreamListener> downloader;
if (XRE_IsParentProcess()) {
downloader = new PSMContentStreamListener(type);
} else {
downloader = static_cast<PSMContentDownloaderChild*>(
dom::ContentChild::GetSingleton()->SendPPSMContentDownloaderConstructor(type));
}
downloader.forget(aContentHandler);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
PSMContentListener::GetLoadCookie(nsISupports** aLoadCookie)
{
nsCOMPtr<nsISupports> loadCookie(mLoadCookie);
loadCookie.forget(aLoadCookie);
return NS_OK;
}
NS_IMETHODIMP
PSMContentListener::SetLoadCookie(nsISupports* aLoadCookie)
{
mLoadCookie = aLoadCookie;
return NS_OK;
}
NS_IMETHODIMP
PSMContentListener::GetParentContentListener(nsIURIContentListener** aContentListener)
{
nsCOMPtr<nsIURIContentListener> listener(mParentContentListener);
listener.forget(aContentListener);
return NS_OK;
}
NS_IMETHODIMP
PSMContentListener::SetParentContentListener(nsIURIContentListener* aContentListener)
{
mParentContentListener = aContentListener;
return NS_OK;
}
} } // namespace mozilla::psm