michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsChannelClassifier.h" michael@0: michael@0: #include "nsNetUtil.h" michael@0: #include "nsIChannel.h" michael@0: #include "nsIProtocolHandler.h" michael@0: #include "nsICachingChannel.h" michael@0: #include "nsICacheEntryDescriptor.h" michael@0: #include "prlog.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: michael@0: #if defined(PR_LOGGING) michael@0: // michael@0: // NSPR_LOG_MODULES=nsChannelClassifier:5 michael@0: // michael@0: static PRLogModuleInfo *gChannelClassifierLog; michael@0: #endif michael@0: #undef LOG michael@0: #define LOG(args) PR_LOG(gChannelClassifierLog, PR_LOG_DEBUG, args) michael@0: michael@0: NS_IMPL_ISUPPORTS(nsChannelClassifier, michael@0: nsIURIClassifierCallback) michael@0: michael@0: nsChannelClassifier::nsChannelClassifier() michael@0: { michael@0: #if defined(PR_LOGGING) michael@0: if (!gChannelClassifierLog) michael@0: gChannelClassifierLog = PR_NewLogModule("nsChannelClassifier"); michael@0: #endif michael@0: } michael@0: michael@0: nsresult michael@0: nsChannelClassifier::Start(nsIChannel *aChannel) michael@0: { michael@0: // Don't bother to run the classifier on a load that has already failed. michael@0: // (this might happen after a redirect) michael@0: nsresult status; michael@0: aChannel->GetStatus(&status); michael@0: if (NS_FAILED(status)) michael@0: return NS_OK; michael@0: michael@0: // Don't bother to run the classifier on a cached load that was michael@0: // previously classified. michael@0: if (HasBeenClassified(aChannel)) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr uri; michael@0: nsresult rv = aChannel->GetURI(getter_AddRefs(uri)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Don't bother checking certain types of URIs. michael@0: bool hasFlags; michael@0: rv = NS_URIChainHasFlags(uri, michael@0: nsIProtocolHandler::URI_DANGEROUS_TO_LOAD, michael@0: &hasFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (hasFlags) return NS_OK; michael@0: michael@0: rv = NS_URIChainHasFlags(uri, michael@0: nsIProtocolHandler::URI_IS_LOCAL_FILE, michael@0: &hasFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (hasFlags) return NS_OK; michael@0: michael@0: rv = NS_URIChainHasFlags(uri, michael@0: nsIProtocolHandler::URI_IS_UI_RESOURCE, michael@0: &hasFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (hasFlags) return NS_OK; michael@0: michael@0: rv = NS_URIChainHasFlags(uri, michael@0: nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, michael@0: &hasFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (hasFlags) return NS_OK; michael@0: michael@0: nsCOMPtr uriClassifier = michael@0: do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv); michael@0: if (rv == NS_ERROR_FACTORY_NOT_REGISTERED || michael@0: rv == NS_ERROR_NOT_AVAILABLE) { michael@0: // no URI classifier, ignore this failure. michael@0: return NS_OK; michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr securityManager = michael@0: do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr principal; michael@0: rv = securityManager->GetChannelPrincipal(aChannel, michael@0: getter_AddRefs(principal)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: bool expectCallback; michael@0: rv = uriClassifier->Classify(principal, this, &expectCallback); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (expectCallback) { michael@0: // Suspend the channel, it will be resumed when we get the classifier michael@0: // callback. michael@0: rv = aChannel->Suspend(); michael@0: if (NS_FAILED(rv)) { michael@0: // Some channels (including nsJSChannel) fail on Suspend. This michael@0: // shouldn't be fatal, but will prevent malware from being michael@0: // blocked on these channels. michael@0: return NS_OK; michael@0: } michael@0: michael@0: mSuspendedChannel = aChannel; michael@0: #ifdef DEBUG michael@0: LOG(("nsChannelClassifier[%p]: suspended channel %p", michael@0: this, mSuspendedChannel.get())); michael@0: #endif michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Note in the cache entry that this URL was classified, so that future michael@0: // cached loads don't need to be checked. michael@0: void michael@0: nsChannelClassifier::MarkEntryClassified(nsresult status) michael@0: { michael@0: nsCOMPtr cachingChannel = michael@0: do_QueryInterface(mSuspendedChannel); michael@0: if (!cachingChannel) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr cacheToken; michael@0: cachingChannel->GetCacheToken(getter_AddRefs(cacheToken)); michael@0: if (!cacheToken) { michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr cacheEntry = michael@0: do_QueryInterface(cacheToken); michael@0: if (!cacheEntry) { michael@0: return; michael@0: } michael@0: michael@0: cacheEntry->SetMetaDataElement("necko:classified", michael@0: NS_SUCCEEDED(status) ? "1" : nullptr); michael@0: } michael@0: michael@0: bool michael@0: nsChannelClassifier::HasBeenClassified(nsIChannel *aChannel) michael@0: { michael@0: nsCOMPtr cachingChannel = michael@0: do_QueryInterface(aChannel); michael@0: if (!cachingChannel) { michael@0: return false; michael@0: } michael@0: michael@0: // Only check the tag if we are loading from the cache without michael@0: // validation. michael@0: bool fromCache; michael@0: if (NS_FAILED(cachingChannel->IsFromCache(&fromCache)) || !fromCache) { michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr cacheToken; michael@0: cachingChannel->GetCacheToken(getter_AddRefs(cacheToken)); michael@0: if (!cacheToken) { michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr cacheEntry = michael@0: do_QueryInterface(cacheToken); michael@0: if (!cacheEntry) { michael@0: return false; michael@0: } michael@0: michael@0: nsXPIDLCString tag; michael@0: cacheEntry->GetMetaDataElement("necko:classified", getter_Copies(tag)); michael@0: return tag.EqualsLiteral("1"); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsChannelClassifier::OnClassifyComplete(nsresult aErrorCode) michael@0: { michael@0: if (mSuspendedChannel) { michael@0: MarkEntryClassified(aErrorCode); michael@0: michael@0: if (NS_FAILED(aErrorCode)) { michael@0: #ifdef DEBUG michael@0: LOG(("nsChannelClassifier[%p]: cancelling channel %p with error " michael@0: "code: %x", this, mSuspendedChannel.get(), aErrorCode)); michael@0: #endif michael@0: mSuspendedChannel->Cancel(aErrorCode); michael@0: } michael@0: #ifdef DEBUG michael@0: LOG(("nsChannelClassifier[%p]: resuming channel %p from " michael@0: "OnClassifyComplete", this, mSuspendedChannel.get())); michael@0: #endif michael@0: mSuspendedChannel->Resume(); michael@0: mSuspendedChannel = nullptr; michael@0: } michael@0: michael@0: return NS_OK; michael@0: }