diff options
Diffstat (limited to '')
-rw-r--r-- | dom/base/nsDataDocumentContentPolicy.cpp | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/dom/base/nsDataDocumentContentPolicy.cpp b/dom/base/nsDataDocumentContentPolicy.cpp new file mode 100644 index 0000000000..6340cee068 --- /dev/null +++ b/dom/base/nsDataDocumentContentPolicy.cpp @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 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/. */ + +/* + * Content policy implementation that prevents all loads of images, + * subframes, etc from documents loaded as data (eg documents loaded + * via XMLHttpRequest). + */ + +#include "nsContentPolicyUtils.h" +#include "nsContentUtils.h" +#include "nsDataDocumentContentPolicy.h" +#include "nsNetUtil.h" +#include "nsIProtocolHandler.h" +#include "nsScriptSecurityManager.h" +#include "mozilla/dom/Document.h" +#include "mozilla/ScopeExit.h" +#include "nsINode.h" +#include "nsIURI.h" + +using namespace mozilla; + +NS_IMPL_ISUPPORTS(nsDataDocumentContentPolicy, nsIContentPolicy) + +// Helper method for ShouldLoad() +// Checks a URI for the given flags. Returns true if the URI has the flags, +// and false if not (or if we weren't able to tell). +static bool HasFlags(nsIURI* aURI, uint32_t aURIFlags) { + bool hasFlags; + nsresult rv = NS_URIChainHasFlags(aURI, aURIFlags, &hasFlags); + return NS_SUCCEEDED(rv) && hasFlags; +} + +// If you change DataDocumentContentPolicy, make sure to check that +// CHECK_PRINCIPAL_AND_DATA in nsContentPolicyUtils is still valid. +// nsContentPolicyUtils may not pass all the parameters to ShouldLoad. +NS_IMETHODIMP +nsDataDocumentContentPolicy::ShouldLoad(nsIURI* aContentLocation, + nsILoadInfo* aLoadInfo, + int16_t* aDecision) { + auto setBlockingReason = mozilla::MakeScopeExit([&]() { + if (NS_CP_REJECTED(*aDecision)) { + NS_SetRequestBlockingReason( + aLoadInfo, nsILoadInfo::BLOCKING_REASON_CONTENT_POLICY_DATA_DOCUMENT); + } + }); + + ExtContentPolicyType contentType = aLoadInfo->GetExternalContentPolicyType(); + nsCOMPtr<nsISupports> requestingContext = aLoadInfo->GetLoadingContext(); + + *aDecision = nsIContentPolicy::ACCEPT; + // Look for the document. In most cases, requestingContext is a node. + nsCOMPtr<mozilla::dom::Document> doc; + nsCOMPtr<nsINode> node = do_QueryInterface(requestingContext); + if (node) { + doc = node->OwnerDoc(); + } else { + if (nsCOMPtr<nsPIDOMWindowOuter> window = + do_QueryInterface(requestingContext)) { + doc = window->GetDoc(); + } + } + + // DTDs are always OK to load + if (!doc || contentType == ExtContentPolicy::TYPE_DTD) { + return NS_OK; + } + + if (doc->IsLoadedAsData()) { + bool allowed = [&] { + if (!doc->IsStaticDocument()) { + // If not a print/print preview doc, then nothing else is allowed for + // data documents. + return false; + } + // Let static (print/print preview) documents to load fonts and + // images. + switch (contentType) { + case ExtContentPolicy::TYPE_IMAGE: + case ExtContentPolicy::TYPE_IMAGESET: + case ExtContentPolicy::TYPE_FONT: + case ExtContentPolicy::TYPE_UA_FONT: + // This one is a bit sketchy, but nsObjectLoadingContent takes care of + // only getting here if it is an image. + case ExtContentPolicy::TYPE_OBJECT: + return true; + default: + return false; + } + }(); + + if (!allowed) { + *aDecision = nsIContentPolicy::REJECT_TYPE; + return NS_OK; + } + } + + mozilla::dom::Document* docToCheckForImage = doc->GetDisplayDocument(); + if (!docToCheckForImage) { + docToCheckForImage = doc; + } + + if (docToCheckForImage->IsBeingUsedAsImage()) { + // We only allow SVG images to load content from URIs that are local and + // also satisfy one of the following conditions: + // - URI inherits security context, e.g. data URIs + // OR + // - URI loadable by subsumers, e.g. blob URIs + // Any URI that doesn't meet these requirements will be rejected below. + if (!(HasFlags(aContentLocation, + nsIProtocolHandler::URI_IS_LOCAL_RESOURCE) && + (HasFlags(aContentLocation, + nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT) || + HasFlags(aContentLocation, + nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS)))) { + *aDecision = nsIContentPolicy::REJECT_TYPE; + + // Report error, if we can. + if (node) { + nsIPrincipal* requestingPrincipal = node->NodePrincipal(); + nsAutoCString sourceSpec; + requestingPrincipal->GetAsciiSpec(sourceSpec); + nsAutoCString targetSpec; + aContentLocation->GetAsciiSpec(targetSpec); + nsScriptSecurityManager::ReportError( + "ExternalDataError", sourceSpec, targetSpec, + requestingPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0); + } + } else if ((contentType == ExtContentPolicy::TYPE_IMAGE || + contentType == ExtContentPolicy::TYPE_IMAGESET) && + doc->GetDocumentURI()) { + // Check for (& disallow) recursive image-loads + bool isRecursiveLoad; + nsresult rv = aContentLocation->EqualsExceptRef(doc->GetDocumentURI(), + &isRecursiveLoad); + if (NS_FAILED(rv) || isRecursiveLoad) { + NS_WARNING("Refusing to recursively load image"); + *aDecision = nsIContentPolicy::REJECT_TYPE; + } + } + return NS_OK; + } + + // Allow all loads for non-resource documents + if (!doc->IsResourceDoc()) { + return NS_OK; + } + + // For resource documents, blacklist some load types + if (contentType == ExtContentPolicy::TYPE_OBJECT || + contentType == ExtContentPolicy::TYPE_DOCUMENT || + contentType == ExtContentPolicy::TYPE_SUBDOCUMENT || + contentType == ExtContentPolicy::TYPE_SCRIPT || + contentType == ExtContentPolicy::TYPE_XSLT || + contentType == ExtContentPolicy::TYPE_FETCH || + contentType == ExtContentPolicy::TYPE_WEB_MANIFEST) { + *aDecision = nsIContentPolicy::REJECT_TYPE; + } + + // If you add more restrictions here, make sure to check that + // CHECK_PRINCIPAL_AND_DATA in nsContentPolicyUtils is still valid. + // nsContentPolicyUtils may not pass all the parameters to ShouldLoad + + return NS_OK; +} + +NS_IMETHODIMP +nsDataDocumentContentPolicy::ShouldProcess(nsIURI* aContentLocation, + nsILoadInfo* aLoadInfo, + int16_t* aDecision) { + return ShouldLoad(aContentLocation, aLoadInfo, aDecision); +} |