michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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: /* michael@0: * Content policy implementation that prevents all loads of images, michael@0: * subframes, etc from documents loaded as data (eg documents loaded michael@0: * via XMLHttpRequest). michael@0: */ michael@0: michael@0: #include "nsDataDocumentContentPolicy.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsScriptSecurityManager.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsINode.h" michael@0: #include "nsIDOMWindow.h" michael@0: michael@0: NS_IMPL_ISUPPORTS(nsDataDocumentContentPolicy, nsIContentPolicy) michael@0: michael@0: // Helper method for ShouldLoad() michael@0: // Checks a URI for the given flags. Returns true if the URI has the flags, michael@0: // and false if not (or if we weren't able to tell). michael@0: static bool michael@0: HasFlags(nsIURI* aURI, uint32_t aURIFlags) michael@0: { michael@0: bool hasFlags; michael@0: nsresult rv = NS_URIChainHasFlags(aURI, aURIFlags, &hasFlags); michael@0: return NS_SUCCEEDED(rv) && hasFlags; michael@0: } michael@0: michael@0: // If you change DataDocumentContentPolicy, make sure to check that michael@0: // CHECK_PRINCIPAL_AND_DATA in nsContentPolicyUtils is still valid. michael@0: // nsContentPolicyUtils may not pass all the parameters to ShouldLoad. michael@0: NS_IMETHODIMP michael@0: nsDataDocumentContentPolicy::ShouldLoad(uint32_t aContentType, michael@0: nsIURI *aContentLocation, michael@0: nsIURI *aRequestingLocation, michael@0: nsISupports *aRequestingContext, michael@0: const nsACString &aMimeGuess, michael@0: nsISupports *aExtra, michael@0: nsIPrincipal *aRequestPrincipal, michael@0: int16_t *aDecision) michael@0: { michael@0: *aDecision = nsIContentPolicy::ACCEPT; michael@0: // Look for the document. In most cases, aRequestingContext is a node. michael@0: nsCOMPtr doc; michael@0: nsCOMPtr node = do_QueryInterface(aRequestingContext); michael@0: if (node) { michael@0: doc = node->OwnerDoc(); michael@0: } else { michael@0: nsCOMPtr window = do_QueryInterface(aRequestingContext); michael@0: if (window) { michael@0: doc = window->GetDoc(); michael@0: } michael@0: } michael@0: michael@0: // DTDs are always OK to load michael@0: if (!doc || aContentType == nsIContentPolicy::TYPE_DTD) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Nothing else is OK to load for data documents michael@0: if (doc->IsLoadedAsData()) { michael@0: // ...but let static (print/print preview) documents to load fonts. michael@0: if (!doc->IsStaticDocument() || aContentType != nsIContentPolicy::TYPE_FONT) { michael@0: *aDecision = nsIContentPolicy::REJECT_TYPE; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: if (doc->IsBeingUsedAsImage()) { michael@0: // We only allow SVG images to load content from URIs that are local and michael@0: // also satisfy one of the following conditions: michael@0: // - URI inherits security context, e.g. data URIs michael@0: // OR michael@0: // - URI loadable by subsumers, e.g. blob URIs michael@0: // Any URI that doesn't meet these requirements will be rejected below. michael@0: if (!HasFlags(aContentLocation, michael@0: nsIProtocolHandler::URI_IS_LOCAL_RESOURCE) || michael@0: (!HasFlags(aContentLocation, michael@0: nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT) && michael@0: !HasFlags(aContentLocation, michael@0: nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS))) { michael@0: *aDecision = nsIContentPolicy::REJECT_TYPE; michael@0: michael@0: // Report error, if we can. michael@0: if (node) { michael@0: nsIPrincipal* requestingPrincipal = node->NodePrincipal(); michael@0: nsRefPtr principalURI; michael@0: nsresult rv = michael@0: requestingPrincipal->GetURI(getter_AddRefs(principalURI)); michael@0: if (NS_SUCCEEDED(rv) && principalURI) { michael@0: nsScriptSecurityManager::ReportError( michael@0: nullptr, NS_LITERAL_STRING("CheckSameOriginError"), principalURI, michael@0: aContentLocation); michael@0: } michael@0: } michael@0: } else if (aContentType == nsIContentPolicy::TYPE_IMAGE && michael@0: doc->GetDocumentURI()) { michael@0: // Check for (& disallow) recursive image-loads michael@0: bool isRecursiveLoad; michael@0: nsresult rv = aContentLocation->EqualsExceptRef(doc->GetDocumentURI(), michael@0: &isRecursiveLoad); michael@0: if (NS_FAILED(rv) || isRecursiveLoad) { michael@0: NS_WARNING("Refusing to recursively load image"); michael@0: *aDecision = nsIContentPolicy::REJECT_TYPE; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Allow all loads for non-resource documents michael@0: if (!doc->IsResourceDoc()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // For resource documents, blacklist some load types michael@0: if (aContentType == nsIContentPolicy::TYPE_OBJECT || michael@0: aContentType == nsIContentPolicy::TYPE_DOCUMENT || michael@0: aContentType == nsIContentPolicy::TYPE_SUBDOCUMENT || michael@0: aContentType == nsIContentPolicy::TYPE_SCRIPT || michael@0: aContentType == nsIContentPolicy::TYPE_XSLT) { michael@0: *aDecision = nsIContentPolicy::REJECT_TYPE; michael@0: } michael@0: michael@0: // If you add more restrictions here, make sure to check that michael@0: // CHECK_PRINCIPAL_AND_DATA in nsContentPolicyUtils is still valid. michael@0: // nsContentPolicyUtils may not pass all the parameters to ShouldLoad michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsDataDocumentContentPolicy::ShouldProcess(uint32_t aContentType, michael@0: nsIURI *aContentLocation, michael@0: nsIURI *aRequestingLocation, michael@0: nsISupports *aRequestingContext, michael@0: const nsACString &aMimeGuess, michael@0: nsISupports *aExtra, michael@0: nsIPrincipal *aRequestPrincipal, michael@0: int16_t *aDecision) michael@0: { michael@0: return ShouldLoad(aContentType, aContentLocation, aRequestingLocation, michael@0: aRequestingContext, aMimeGuess, aExtra, aRequestPrincipal, michael@0: aDecision); michael@0: }