Bug 1100024: Don't call Connect if the principal is on a local or remote blocklist (r=mcmanus)

This commit is contained in:
Monica Chew 2015-01-09 13:25:13 -08:00
Родитель 152c9afdc4
Коммит 3023f46c51
8 изменённых файлов: 144 добавлений и 64 удалений

Просмотреть файл

@ -289,15 +289,10 @@ nsBaseChannel::ClassifyURI()
return;
}
nsresult rv;
if (mLoadFlags & LOAD_CLASSIFY_URI) {
nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier();
if (classifier) {
rv = classifier->Start(this);
if (NS_FAILED(rv)) {
Cancel(rv);
}
classifier->Start(this);
} else {
Cancel(NS_ERROR_OUT_OF_MEMORY);
}

Просмотреть файл

@ -49,7 +49,8 @@ NS_IMPL_ISUPPORTS(nsChannelClassifier,
nsIURIClassifierCallback)
nsChannelClassifier::nsChannelClassifier()
: mIsAllowListed(false)
: mIsAllowListed(false),
mSuspendedChannel(false)
{
#if defined(PR_LOGGING)
if (!gChannelClassifierLog)
@ -209,8 +210,20 @@ nsChannelClassifier::NotifyTrackingProtectionDisabled(nsIChannel *aChannel)
return NS_OK;
}
nsresult
void
nsChannelClassifier::Start(nsIChannel *aChannel)
{
mChannel = aChannel;
nsresult rv = StartInternal(aChannel);
if (NS_FAILED(rv)) {
// If we aren't getting a callback for any reason, assume a good verdict and
// make sure we resume the channel if necessary.
OnClassifyComplete(NS_OK);
}
}
nsresult
nsChannelClassifier::StartInternal(nsIChannel *aChannel)
{
// Should only be called in the parent process.
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
@ -220,12 +233,12 @@ nsChannelClassifier::Start(nsIChannel *aChannel)
nsresult status;
aChannel->GetStatus(&status);
if (NS_FAILED(status))
return NS_OK;
return status;
// Don't bother to run the classifier on a cached load that was
// previously classified.
// previously classified as good.
if (HasBeenClassified(aChannel)) {
return NS_OK;
return NS_ERROR_UNEXPECTED;
}
nsCOMPtr<nsIURI> uri;
@ -238,32 +251,32 @@ nsChannelClassifier::Start(nsIChannel *aChannel)
nsIProtocolHandler::URI_DANGEROUS_TO_LOAD,
&hasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (hasFlags) return NS_OK;
if (hasFlags) return NS_ERROR_UNEXPECTED;
rv = NS_URIChainHasFlags(uri,
nsIProtocolHandler::URI_IS_LOCAL_FILE,
&hasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (hasFlags) return NS_OK;
if (hasFlags) return NS_ERROR_UNEXPECTED;
rv = NS_URIChainHasFlags(uri,
nsIProtocolHandler::URI_IS_UI_RESOURCE,
&hasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (hasFlags) return NS_OK;
if (hasFlags) return NS_ERROR_UNEXPECTED;
rv = NS_URIChainHasFlags(uri,
nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
&hasFlags);
NS_ENSURE_SUCCESS(rv, rv);
if (hasFlags) return NS_OK;
if (hasFlags) return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIURIClassifier> uriClassifier =
do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
if (rv == NS_ERROR_FACTORY_NOT_REGISTERED ||
rv == NS_ERROR_NOT_AVAILABLE) {
// no URI classifier, ignore this failure.
return NS_OK;
return NS_ERROR_NOT_AVAILABLE;
}
NS_ENSURE_SUCCESS(rv, rv);
@ -282,7 +295,9 @@ nsChannelClassifier::Start(nsIChannel *aChannel)
rv = uriClassifier->Classify(principal, trackingProtectionEnabled, this,
&expectCallback);
if (NS_FAILED(rv)) return rv;
if (NS_FAILED(rv)) {
return rv;
}
if (expectCallback) {
// Suspend the channel, it will be resumed when we get the classifier
@ -292,14 +307,16 @@ nsChannelClassifier::Start(nsIChannel *aChannel)
// Some channels (including nsJSChannel) fail on Suspend. This
// shouldn't be fatal, but will prevent malware from being
// blocked on these channels.
return NS_OK;
LOG(("nsChannelClassifier[%p]: Couldn't suspend channel", this));
return rv;
}
mSuspendedChannel = aChannel;
#ifdef DEBUG
mSuspendedChannel = true;
LOG(("nsChannelClassifier[%p]: suspended channel %p",
this, mSuspendedChannel.get()));
#endif
this, mChannel.get()));
} else {
LOG(("nsChannelClassifier[%p]: not expecting callback", this));
return NS_ERROR_FAILURE;
}
return NS_OK;
@ -318,8 +335,7 @@ nsChannelClassifier::MarkEntryClassified(nsresult status)
return;
}
nsCOMPtr<nsICachingChannel> cachingChannel =
do_QueryInterface(mSuspendedChannel);
nsCOMPtr<nsICachingChannel> cachingChannel = do_QueryInterface(mChannel);
if (!cachingChannel) {
return;
}
@ -444,17 +460,18 @@ nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode)
// Should only be called in the parent process.
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
LOG(("nsChannelClassifier[%p]:OnClassifyComplete", this));
if (mSuspendedChannel) {
MarkEntryClassified(aErrorCode);
if (NS_FAILED(aErrorCode)) {
#ifdef DEBUG
nsCOMPtr<nsIURI> uri;
mSuspendedChannel->GetURI(getter_AddRefs(uri));
mChannel->GetURI(getter_AddRefs(uri));
nsCString spec;
uri->GetSpec(spec);
LOG(("nsChannelClassifier[%p]: cancelling channel %p for %s "
"with error code: %x", this, mSuspendedChannel.get(),
"with error code: %x", this, mChannel.get(),
spec.get(), aErrorCode));
#endif
@ -462,18 +479,23 @@ nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode)
// Do update the security state of the document and fire a security
// change event.
if (aErrorCode == NS_ERROR_TRACKING_URI) {
SetBlockedTrackingContent(mSuspendedChannel);
SetBlockedTrackingContent(mChannel);
}
mSuspendedChannel->Cancel(aErrorCode);
}
#ifdef DEBUG
mChannel->Cancel(aErrorCode);
}
LOG(("nsChannelClassifier[%p]: resuming channel %p from "
"OnClassifyComplete", this, mSuspendedChannel.get()));
#endif
mSuspendedChannel->Resume();
mSuspendedChannel = nullptr;
"OnClassifyComplete", this, mChannel.get()));
mChannel->Resume();
}
nsresult rv;
nsCOMPtr<nsIHttpChannelInternal> channel = do_QueryInterface(mChannel, &rv);
// Even if we have cancelled the channel, we need to call
// ContinueBeginConnect so that we abort appropriately.
if (NS_SUCCEEDED(rv)) {
channel->ContinueBeginConnect();
}
mChannel = nullptr;
return NS_OK;
}

Просмотреть файл

@ -20,16 +20,27 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIURICLASSIFIERCALLBACK
nsresult Start(nsIChannel *aChannel);
// Calls nsIURIClassifier.Classify with the principal of the given channel,
// and cancels the channel on a bad verdict. If aChannel is
// nsIHttpChannelInternal, nsChannelClassifier must call
// ContinueBeginConnect once Start has successfully returned.
void Start(nsIChannel *aChannel);
private:
nsCOMPtr<nsIChannel> mSuspendedChannel;
// Set true if the channel is on the allow list.
// True if the channel is on the allow list.
bool mIsAllowListed;
// True if the channel has been suspended.
bool mSuspendedChannel;
nsCOMPtr<nsIChannel> mChannel;
~nsChannelClassifier() {}
// Caches good classifications for the channel principal.
void MarkEntryClassified(nsresult status);
bool HasBeenClassified(nsIChannel *aChannel);
// Helper function so that we ensure we call ContinueBeginConnect once
// Start is called. Returns NS_OK if and only if we will get a callback
// from the classifier service.
nsresult StartInternal(nsIChannel *aChannel);
// Whether or not tracking protection should be enabled on this channel.
nsresult ShouldEnableTrackingProtection(nsIChannel *aChannel, bool *result);

Просмотреть файл

@ -1409,6 +1409,15 @@ HttpBaseChannel::RedirectTo(nsIURI *newURI)
// HttpBaseChannel::nsIHttpChannelInternal
//-----------------------------------------------------------------------------
NS_IMETHODIMP
HttpBaseChannel::ContinueBeginConnect()
{
MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default,
"The parent overrides this");
MOZ_ASSERT(false, "This method must be overridden");
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
HttpBaseChannel::GetTopWindowURI(nsIURI **aTopWindowURI)
{

Просмотреть файл

@ -188,6 +188,7 @@ public:
NS_IMETHOD GetLastModifiedTime(PRTime* lastModifiedTime) MOZ_OVERRIDE;
NS_IMETHOD ForceNoIntercept() MOZ_OVERRIDE;
NS_IMETHOD GetTopWindowURI(nsIURI **aTopWindowURI) MOZ_OVERRIDE;
NS_IMETHOD ContinueBeginConnect();
inline void CleanRedirectCacheChainIfNecessary()
{

Просмотреть файл

@ -22,6 +22,7 @@
#include "nsISeekableStream.h"
#include "nsILoadGroupChild.h"
#include "nsIProtocolProxyService2.h"
#include "nsIURIClassifier.h"
#include "nsMimeTypes.h"
#include "nsNetUtil.h"
#include "prprf.h"
@ -39,6 +40,7 @@
#include "GeckoProfiler.h"
#include "nsIConsoleService.h"
#include "mozilla/Attributes.h"
#include "mozilla/Preferences.h"
#include "mozilla/VisualEventTracer.h"
#include "nsISSLSocketControl.h"
#include "sslt.h"
@ -240,6 +242,7 @@ nsHttpChannel::nsHttpChannel()
, mIsPartialRequest(0)
, mHasAutoRedirectVetoNotifier(0)
, mPushedStream(nullptr)
, mLocalBlocklist(false)
, mDidReval(false)
{
LOG(("Creating nsHttpChannel [this=%p]\n", this));
@ -326,7 +329,7 @@ nsHttpChannel::Connect()
mConnectionInfo->SetPrivate(mPrivateBrowsing);
mConnectionInfo->SetNoSpdy(mCaps & NS_HTTP_DISALLOW_SPDY);
// Consider opening a TCP connection right away
// Consider opening a TCP connection right away.
SpeculativeConnect();
// Don't allow resuming when cache must be used
@ -433,11 +436,11 @@ nsHttpChannel::SpeculativeConnect()
// Before we take the latency hit of dealing with the cache, try and
// get the TCP (and SSL) handshakes going so they can overlap.
// don't speculate on uses of the offline application cache,
// if we are offline, when doing http upgrade (i.e. websockets bootstrap),
// or if we can't do keep-alive (because then we couldn't reuse
// the speculative connection anyhow).
if (mApplicationCache || gIOService->IsOffline() ||
// don't speculate if we are on a local blocklist, on uses of the offline
// application cache, if we are offline, when doing http upgrade (i.e.
// websockets bootstrap), or if we can't do keep-alive (because then we
// couldn't reuse the speculative connection anyhow).
if (mLocalBlocklist || mApplicationCache || gIOService->IsOffline() ||
mUpgradeProtocolCallback || !(mCaps & NS_HTTP_ALLOW_KEEPALIVE))
return;
@ -4873,6 +4876,21 @@ nsHttpChannel::BeginConnect()
if (mAPIRedirectToURI) {
return AsyncCall(&nsHttpChannel::HandleAsyncAPIRedirect);
}
// Check to see if this principal exists on local blocklists.
if (mLoadFlags & LOAD_CLASSIFY_URI) {
nsCOMPtr<nsIURIClassifier> classifier = do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID);
if (classifier) {
nsCOMPtr<nsIPrincipal> principal = GetPrincipal(false);
bool tp = Preferences::GetBool("privacy.trackingprotection.enabled",
false);
nsresult response = NS_OK;
classifier->ClassifyLocal(principal, tp, &response);
if (NS_FAILED(response)) {
LOG(("nsHttpChannel::Found principal on local blocklist [this=%p]", this));
mLocalBlocklist = true;
}
}
}
// If mTimingEnabled flag is not set after OnModifyRequest() then
// clear the already recorded AsyncOpen value for consistency.
@ -4892,7 +4910,7 @@ nsHttpChannel::BeginConnect()
if (mLoadFlags & VALIDATE_ALWAYS || BYPASS_LOCAL_CACHE(mLoadFlags))
mCaps |= NS_HTTP_REFRESH_DNS;
if (!mConnectionInfo->UsingHttpProxy() &&
if (!mLocalBlocklist && !mConnectionInfo->UsingHttpProxy() &&
!(mLoadFlags & (LOAD_NO_NETWORK_IO | LOAD_ONLY_FROM_CACHE))) {
// Start a DNS lookup very early in case the real open is queued the DNS can
// happen in parallel. Do not do so in the presence of an HTTP proxy as
@ -4936,27 +4954,17 @@ nsHttpChannel::BeginConnect()
}
mCaps &= ~NS_HTTP_ALLOW_PIPELINING;
}
// We may have been cancelled already, either by on-modify-request
// listeners or by load group observers; in that case, we should
// not send the request to the server
if (mCanceled)
rv = mStatus;
else
rv = Connect();
if (NS_FAILED(rv)) {
LOG(("Calling AsyncAbort [rv=%x mCanceled=%i]\n", rv, mCanceled));
CloseCacheEntry(true);
AsyncAbort(rv);
} else if (mLoadFlags & LOAD_CLASSIFY_URI) {
nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier();
rv = classifier->Start(this);
if (NS_FAILED(rv)) {
Cancel(rv);
return rv;
}
if (mCanceled || !mLocalBlocklist) {
return ContinueBeginConnect();
}
MOZ_ASSERT(!mCanceled && mLocalBlocklist);
// nsChannelClassifier must call ContinueBeginConnect after optionally
// cancelling the channel once we have a remote verdict. We call a concrete
// class instead of an nsI* that might be overridden.
nsRefPtr<nsChannelClassifier> classifier = new nsChannelClassifier();
LOG(("nsHttpChannel::Starting nsChannelClassifier %p [this=%p]",
classifier.get(), this));
classifier->Start(this);
return NS_OK;
}
@ -4993,6 +5001,30 @@ nsHttpChannel::SetPriority(int32_t value)
return NS_OK;
}
//-----------------------------------------------------------------------------
// nsHttpChannel::nsIHttpChannelInternal
//-----------------------------------------------------------------------------
NS_IMETHODIMP
nsHttpChannel::ContinueBeginConnect()
{
LOG(("nsHttpChannel::ContinueBeginConnect [this=%p]", this));
nsresult rv;
// We may have been cancelled already, either by on-modify-request
// listeners or load group observers or nsChannelClassifier; in that case,
// we should not send the request to the server
if (mCanceled) {
rv = mStatus;
} else {
rv = Connect();
}
if (NS_FAILED(rv)) {
LOG(("Calling AsyncAbort [rv=%x mCanceled=%i]\n", rv, mCanceled));
CloseCacheEntry(true);
AsyncAbort(rv);
}
return rv;
}
//-----------------------------------------------------------------------------
// HttpChannel::nsIClassOfService
//-----------------------------------------------------------------------------

Просмотреть файл

@ -119,6 +119,7 @@ public:
NS_IMETHOD AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) MOZ_OVERRIDE;
// nsIHttpChannelInternal
NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey) MOZ_OVERRIDE;
NS_IMETHOD ContinueBeginConnect();
// nsISupportsPriority
NS_IMETHOD SetPriority(int32_t value) MOZ_OVERRIDE;
// nsIClassOfService
@ -465,6 +466,9 @@ private:
nsRefPtr<nsDNSPrefetch> mDNSPrefetch;
Http2PushedStream *mPushedStream;
// True if the channel's principal was found on a phishing, malware, or
// tracking (if tracking protection is enabled) blocklist
bool mLocalBlocklist;
nsresult WaitForRedirectCallback();
void PushRedirectAsyncFunc(nsContinueRedirectionFunc func);

Просмотреть файл

@ -224,4 +224,10 @@ interface nsIHttpChannelInternal : nsISupports
* The URI of the top-level window that's associated with this channel.
*/
readonly attribute nsIURI topWindowURI;
/**
* Used only by nsChannelClassifier to resume connecting or abort the
* channel after a remote classification verdict.
*/
void continueBeginConnect();
};