mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 08:15:31 +00:00
Bug 1599131: Remove carve outs for downloads within x-frame-options when fission enabled. r=smaug,mattwoodrow
Differential Revision: https://phabricator.services.mozilla.com/D65949 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
8753fc5a42
commit
5ea63e55c7
@ -112,7 +112,6 @@
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/FeaturePolicy.h"
|
||||
#include "mozilla/dom/FeaturePolicyUtils.h"
|
||||
#include "mozilla/dom/FramingChecker.h"
|
||||
#include "mozilla/dom/HTMLAllCollection.h"
|
||||
#include "mozilla/dom/HTMLMetaElement.h"
|
||||
#include "mozilla/dom/HTMLSharedElement.h"
|
||||
@ -3170,58 +3169,6 @@ nsresult Document::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
|
||||
rv = InitCSP(aChannel);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Bug 1574372: Download should be fully done in the parent process.
|
||||
// Unfortunately we currently can not determine whether a load will
|
||||
// result in a download in the parent process. Hence, if running in
|
||||
// non-fission-mode then we will have to enforce checks for
|
||||
// frame-ancestors and x-frame-options here in the content process
|
||||
// but if we run in fission-mode then we do those two security
|
||||
// checks within DOMSecurityManager::Observe in the parent.
|
||||
bool fissionEnabled =
|
||||
docShell && nsDocShell::Cast(docShell)->UseRemoteSubframes();
|
||||
if (!fissionEnabled) {
|
||||
nsContentPolicyType contentType = loadInfo->GetExternalContentPolicyType();
|
||||
// frame-ancestor check only makes sense for subdocument and object loads,
|
||||
// if this is not a load of such type, there is nothing to do here.
|
||||
if (contentType == nsIContentPolicy::TYPE_SUBDOCUMENT ||
|
||||
contentType == nsIContentPolicy::TYPE_OBJECT) {
|
||||
// we only enforce frame-ancestors if the load is an actual http
|
||||
// channel, otherwise we block dynamic iframes since about:blank
|
||||
// inherits the CSP.
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel;
|
||||
nsContentSecurityUtils::GetHttpChannelFromPotentialMultiPart(
|
||||
aChannel, getter_AddRefs(httpChannel));
|
||||
if (httpChannel && mCSP) {
|
||||
bool safeAncestry = false;
|
||||
// PermitsAncestry sends violation reports when necessary
|
||||
rv = mCSP->PermitsAncestry(loadInfo, &safeAncestry);
|
||||
if (NS_FAILED(rv) || !safeAncestry) {
|
||||
// stop! ERROR page!
|
||||
aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the checks for type_subdoc or type_object happen within
|
||||
// CheckFrameOptions.
|
||||
if (!FramingChecker::CheckFrameOptions(aChannel, mCSP)) {
|
||||
// stop! ERROR page!
|
||||
// But before we have to reset the principal of the document
|
||||
// because the onload() event fires before the error page
|
||||
// is displayed and we do not want the enclosing document
|
||||
// to access the contentDocument.
|
||||
RefPtr<NullPrincipal> nullPrincipal =
|
||||
NullPrincipal::CreateWithInheritedAttributes(NodePrincipal());
|
||||
// Before calling SetPrincipals() we should ensure that mFontFaceSet
|
||||
// and also GetInnerWindow() is still null at this point, before
|
||||
// we can fix Bug 1614735: Evaluate calls to SetPrincipal
|
||||
// within Document.cpp
|
||||
MOZ_ASSERT(!mFontFaceSet && !GetInnerWindow());
|
||||
SetPrincipals(nullPrincipal, nullPrincipal);
|
||||
aChannel->Cancel(NS_ERROR_XFO_VIOLATION);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize FeaturePolicy
|
||||
rv = InitFeaturePolicy(aChannel);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
@ -67,15 +67,10 @@ add_task(async function test() {
|
||||
SpecialPowers.registerObservers("xfo-on-violate-policy");
|
||||
|
||||
function examiner() {
|
||||
// Depending on whether xfo checks are performed in the parent
|
||||
// or in the content process we have to use the specialpowers version
|
||||
// for observing XFO violations. After Bug 1574372 we should be able
|
||||
// to remove the "xfo-on-violate-policy" here.
|
||||
SpecialPowers.addObserver(
|
||||
this,
|
||||
"specialpowers-xfo-on-violate-policy"
|
||||
);
|
||||
SpecialPowers.addObserver(this, "xfo-on-violate-policy");
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe(subject, topic, data) {
|
||||
@ -92,7 +87,6 @@ add_task(async function test() {
|
||||
this,
|
||||
"specialpowers-xfo-on-violate-policy"
|
||||
);
|
||||
SpecialPowers.removeObserver(this, "xfo-on-violate-policy");
|
||||
},
|
||||
};
|
||||
let myExaminer = new examiner();
|
||||
|
@ -1,213 +0,0 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#include "DOMSecurityManager.h"
|
||||
#include "nsCSPContext.h"
|
||||
#include "nsContentSecurityUtils.h"
|
||||
#include "mozilla/dom/FramingChecker.h"
|
||||
#include "mozilla/dom/WindowGlobalParent.h"
|
||||
#include "mozilla/StaticPrefs_fission.h"
|
||||
|
||||
#include "nsIMultiPartChannel.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIHttpProtocolHandler.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
namespace {
|
||||
StaticRefPtr<DOMSecurityManager> gDOMSecurityManager;
|
||||
} // namespace
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(DOMSecurityManager)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_ADDREF(DOMSecurityManager)
|
||||
NS_IMPL_RELEASE(DOMSecurityManager)
|
||||
|
||||
/* static */
|
||||
void DOMSecurityManager::Initialize() {
|
||||
MOZ_ASSERT(!gDOMSecurityManager);
|
||||
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<DOMSecurityManager> service = new DOMSecurityManager();
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (NS_WARN_IF(!obs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
obs->AddObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC, false);
|
||||
obs->AddObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
gDOMSecurityManager = std::move(service);
|
||||
}
|
||||
|
||||
/* static */
|
||||
void DOMSecurityManager::Shutdown() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!gDOMSecurityManager) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<DOMSecurityManager> service = gDOMSecurityManager.forget();
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (NS_WARN_IF(!obs)) {
|
||||
return;
|
||||
}
|
||||
|
||||
obs->RemoveObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC);
|
||||
obs->RemoveObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DOMSecurityManager::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData) {
|
||||
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
||||
Shutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!strcmp(aTopic, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC));
|
||||
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aSubject);
|
||||
if (NS_WARN_IF(!channel)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Bug 1574372: Download should be fully done in the parent process.
|
||||
// Unfortunately we currently can not determine whether a load will
|
||||
// result in a download in the parent process. Hence, if running in
|
||||
// fission-mode, then we will enforce the security checks for
|
||||
// frame-ancestors and x-frame-options here in the parent using some
|
||||
// additional carveouts for downloads but if we run in
|
||||
// non-fission-mode then we do those two security checks within
|
||||
// Document::StartDocumentLoad in the content process.
|
||||
nsCOMPtr<nsILoadContext> loadContext;
|
||||
NS_QueryNotificationCallbacks(channel, loadContext);
|
||||
bool fissionEnabled = loadContext && loadContext->UseRemoteSubframes();
|
||||
if (fissionEnabled) {
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
nsresult rv =
|
||||
ParseCSPAndEnforceFrameAncestorCheck(channel, getter_AddRefs(csp));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
// X-Frame-Options needs to be enforced after CSP frame-ancestors
|
||||
// checks because if frame-ancestors is present, then x-frame-options
|
||||
// will be discarded
|
||||
EnforceXFrameOptionsCheck(channel, csp);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult DOMSecurityManager::ParseCSPAndEnforceFrameAncestorCheck(
|
||||
nsIChannel* aChannel, nsIContentSecurityPolicy** aOutCSP) {
|
||||
MOZ_ASSERT(aChannel);
|
||||
|
||||
// CSP can only hang off an http channel, if this channel is not
|
||||
// an http channel then there is nothing to do here.
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel;
|
||||
nsresult rv = nsContentSecurityUtils::GetHttpChannelFromPotentialMultiPart(
|
||||
aChannel, getter_AddRefs(httpChannel));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!httpChannel) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
|
||||
nsContentPolicyType contentType = loadInfo->GetExternalContentPolicyType();
|
||||
// frame-ancestor check only makes sense for subdocument and object loads,
|
||||
// if this is not a load of such type, there is nothing to do here.
|
||||
if (contentType != nsIContentPolicy::TYPE_SUBDOCUMENT &&
|
||||
contentType != nsIContentPolicy::TYPE_OBJECT) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString tCspHeaderValue, tCspROHeaderValue;
|
||||
|
||||
Unused << httpChannel->GetResponseHeader(
|
||||
NS_LITERAL_CSTRING("content-security-policy"), tCspHeaderValue);
|
||||
|
||||
Unused << httpChannel->GetResponseHeader(
|
||||
NS_LITERAL_CSTRING("content-security-policy-report-only"),
|
||||
tCspROHeaderValue);
|
||||
|
||||
// if there are no CSP values, then there is nothing to do here.
|
||||
if (tCspHeaderValue.IsEmpty() && tCspROHeaderValue.IsEmpty()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
|
||||
NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
|
||||
|
||||
RefPtr<nsCSPContext> csp = new nsCSPContext();
|
||||
nsCOMPtr<nsIPrincipal> resultPrincipal;
|
||||
rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
|
||||
aChannel, getter_AddRefs(resultPrincipal));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIURI> selfURI;
|
||||
aChannel->GetURI(getter_AddRefs(selfURI));
|
||||
|
||||
nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
|
||||
nsAutoString referrerSpec;
|
||||
if (referrerInfo) {
|
||||
referrerInfo->GetComputedReferrerSpec(referrerSpec);
|
||||
}
|
||||
uint64_t innerWindowID = loadInfo->GetInnerWindowID();
|
||||
|
||||
rv = csp->SetRequestContextWithPrincipal(resultPrincipal, selfURI,
|
||||
referrerSpec, innerWindowID);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// ----- if there's a full-strength CSP header, apply it.
|
||||
if (!cspHeaderValue.IsEmpty()) {
|
||||
rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// ----- if there's a report-only CSP header, apply it.
|
||||
if (!cspROHeaderValue.IsEmpty()) {
|
||||
rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// ----- Enforce frame-ancestor policy on any applied policies
|
||||
bool safeAncestry = false;
|
||||
// PermitsAncestry sends violation reports when necessary
|
||||
rv = csp->PermitsAncestry(loadInfo, &safeAncestry);
|
||||
|
||||
if (NS_FAILED(rv) || !safeAncestry) {
|
||||
// stop! ERROR page!
|
||||
aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
|
||||
}
|
||||
|
||||
// return the CSP for x-frame-options check
|
||||
csp.forget(aOutCSP);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void DOMSecurityManager::EnforceXFrameOptionsCheck(
|
||||
nsIChannel* aChannel, nsIContentSecurityPolicy* aCsp) {
|
||||
MOZ_ASSERT(aChannel);
|
||||
if (!FramingChecker::CheckFrameOptions(aChannel, aCsp)) {
|
||||
// stop! ERROR page!
|
||||
aChannel->Cancel(NS_ERROR_XFO_VIOLATION);
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#ifndef mozilla_dom_DOMSecurityManager_h
|
||||
#define mozilla_dom_DOMSecurityManager_h
|
||||
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
|
||||
class nsIChannel;
|
||||
|
||||
class DOMSecurityManager final : public nsIObserver {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static void Initialize();
|
||||
|
||||
private:
|
||||
DOMSecurityManager() = default;
|
||||
~DOMSecurityManager() = default;
|
||||
|
||||
// Only enforces the frame-ancestor check which needs to happen in
|
||||
// the parent because we can only access the window global in the
|
||||
// parent. The actual CSP gets parsed and applied in content.
|
||||
nsresult ParseCSPAndEnforceFrameAncestorCheck(
|
||||
nsIChannel* aChannel, nsIContentSecurityPolicy** aOutCSP);
|
||||
|
||||
// XFO checks are ignored in case CSP frame-ancestors is present,
|
||||
void EnforceXFrameOptionsCheck(nsIChannel* aChannel,
|
||||
nsIContentSecurityPolicy* aCsp);
|
||||
|
||||
static void Shutdown();
|
||||
};
|
||||
|
||||
#endif /* mozilla_dom_DOMSecurityManager_h */
|
@ -18,6 +18,7 @@
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/dom/nsCSPUtils.h"
|
||||
#include "mozilla/dom/LoadURIOptionsBinding.h"
|
||||
#include "mozilla/dom/WindowGlobalParent.h"
|
||||
#include "mozilla/NullPrincipal.h"
|
||||
#include "nsIStringBundle.h"
|
||||
|
||||
@ -98,17 +99,10 @@ void FramingChecker::ReportError(const char* aMessageTag,
|
||||
nsCOMPtr<nsIURI> parentURI;
|
||||
if (aParentContext) {
|
||||
BrowsingContext* topContext = aParentContext->Top();
|
||||
// If fission is enabled, then ReportError is called in the parent
|
||||
// process, otherwise in the content process. After Bug 1574372 we
|
||||
// should be able to remove that branching code for querying parentURI.
|
||||
if (XRE_IsParentProcess()) {
|
||||
WindowGlobalParent* window =
|
||||
topContext->Canonical()->GetCurrentWindowGlobal();
|
||||
if (window) {
|
||||
parentURI = window->GetDocumentURI();
|
||||
}
|
||||
} else if (nsPIDOMWindowOuter* windowOuter = topContext->GetDOMWindow()) {
|
||||
parentURI = windowOuter->GetDocumentURI();
|
||||
WindowGlobalParent* window =
|
||||
topContext->Canonical()->GetCurrentWindowGlobal();
|
||||
if (window) {
|
||||
parentURI = window->GetDocumentURI();
|
||||
}
|
||||
ReportError(aMessageTag, parentURI, aChildURI, aPolicy, aInnerWindowID);
|
||||
}
|
||||
@ -138,19 +132,12 @@ bool FramingChecker::CheckOneFrameOptionsPolicy(nsIHttpChannel* aHttpChannel,
|
||||
|
||||
while (ctx) {
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
// If fission is enabled, then CheckOneFrameOptionsPolicy is called in the
|
||||
// parent process, otherwise in the content process. After Bug 1574372 we
|
||||
// should be able to remove that branching code for querying principal.
|
||||
if (XRE_IsParentProcess()) {
|
||||
WindowGlobalParent* window = ctx->Canonical()->GetCurrentWindowGlobal();
|
||||
if (window) {
|
||||
// Using the URI of the Principal and not the document because e.g.
|
||||
// window.open inherits the principal and hence the URI of the
|
||||
// opening context needed for same origin checks.
|
||||
principal = window->DocumentPrincipal();
|
||||
}
|
||||
} else if (nsPIDOMWindowOuter* windowOuter = ctx->GetDOMWindow()) {
|
||||
principal = nsGlobalWindowOuter::Cast(windowOuter)->GetPrincipal();
|
||||
WindowGlobalParent* window = ctx->Canonical()->GetCurrentWindowGlobal();
|
||||
if (window) {
|
||||
// Using the URI of the Principal and not the document because e.g.
|
||||
// window.open inherits the principal and hence the URI of the
|
||||
// opening context needed for same origin checks.
|
||||
principal = window->DocumentPrincipal();
|
||||
}
|
||||
|
||||
if (principal && principal->IsSystemPrincipal()) {
|
||||
@ -227,6 +214,8 @@ static bool ShouldIgnoreFrameOptions(nsIChannel* aChannel,
|
||||
/* static */
|
||||
bool FramingChecker::CheckFrameOptions(nsIChannel* aChannel,
|
||||
nsIContentSecurityPolicy* aCsp) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess(), "check frame options only in parent");
|
||||
|
||||
if (!aChannel) {
|
||||
return true;
|
||||
}
|
||||
@ -253,22 +242,6 @@ bool FramingChecker::CheckFrameOptions(nsIChannel* aChannel,
|
||||
return true;
|
||||
}
|
||||
|
||||
// Bug 1574372: Download should be fully done in the parent process.
|
||||
// Unfortunately we currently can not determine whether a load will
|
||||
// result in a download in the parent process. As an interim hotfix
|
||||
// for Bug 1593832, we are going to allow loads using a content-type
|
||||
// of 'application/octet-stream' which will definitley result in
|
||||
// a download.
|
||||
if (XRE_IsParentProcess()) {
|
||||
// Bug 1599131: Remove carve outs for downloads within x-frame-options
|
||||
// when fission enabled
|
||||
nsAutoCString type;
|
||||
aChannel->GetContentType(type);
|
||||
if (type.LowerCaseEqualsLiteral("application/octet-stream")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel;
|
||||
nsresult rv = nsContentSecurityUtils::GetHttpChannelFromPotentialMultiPart(
|
||||
aChannel, getter_AddRefs(httpChannel));
|
||||
|
@ -13,7 +13,6 @@ DIRS += [ 'featurepolicy' ]
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'CSPEvalChecker.h',
|
||||
'DOMSecurityManager.h',
|
||||
'DOMSecurityMonitor.h',
|
||||
'FramingChecker.h',
|
||||
'nsContentSecurityManager.h',
|
||||
@ -38,7 +37,6 @@ EXPORTS += [
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'CSPEvalChecker.cpp',
|
||||
'DOMSecurityManager.cpp',
|
||||
'DOMSecurityMonitor.cpp',
|
||||
'FramingChecker.cpp',
|
||||
'nsContentSecurityManager.cpp',
|
||||
|
@ -1529,6 +1529,7 @@ nsresult nsCSPContext::AsyncReportViolation(
|
||||
NS_IMETHODIMP
|
||||
nsCSPContext::PermitsAncestry(nsILoadInfo* aLoadInfo,
|
||||
bool* outPermitsAncestry) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess(), "frame-ancestor check only in parent");
|
||||
nsresult rv;
|
||||
|
||||
*outPermitsAncestry = true;
|
||||
@ -1542,16 +1543,9 @@ nsCSPContext::PermitsAncestry(nsILoadInfo* aLoadInfo,
|
||||
|
||||
while (ctx) {
|
||||
nsCOMPtr<nsIURI> currentURI;
|
||||
// If fission is enabled, then permitsAncestry is called in the parent
|
||||
// process, otherwise in the content process. After Bug 1574372 we should
|
||||
// be able to remove that branching code for querying currentURI.
|
||||
if (XRE_IsParentProcess()) {
|
||||
WindowGlobalParent* window = ctx->Canonical()->GetCurrentWindowGlobal();
|
||||
if (window) {
|
||||
currentURI = window->GetDocumentURI();
|
||||
}
|
||||
} else if (nsPIDOMWindowOuter* windowOuter = ctx->GetDOMWindow()) {
|
||||
currentURI = windowOuter->GetDocumentURI();
|
||||
WindowGlobalParent* window = ctx->Canonical()->GetCurrentWindowGlobal();
|
||||
if (window) {
|
||||
currentURI = window->GetDocumentURI();
|
||||
}
|
||||
|
||||
if (currentURI) {
|
||||
|
@ -606,6 +606,124 @@ nsresult nsContentSecurityUtils::GetHttpChannelFromPotentialMultiPart(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult ParseCSPAndEnforceFrameAncestorCheck(
|
||||
nsIChannel* aChannel, nsIContentSecurityPolicy** aOutCSP) {
|
||||
MOZ_ASSERT(aChannel);
|
||||
|
||||
// CSP can only hang off an http channel, if this channel is not
|
||||
// an http channel then there is nothing to do here.
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel;
|
||||
nsresult rv = nsContentSecurityUtils::GetHttpChannelFromPotentialMultiPart(
|
||||
aChannel, getter_AddRefs(httpChannel));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!httpChannel) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
|
||||
nsContentPolicyType contentType = loadInfo->GetExternalContentPolicyType();
|
||||
// frame-ancestor check only makes sense for subdocument and object loads,
|
||||
// if this is not a load of such type, there is nothing to do here.
|
||||
if (contentType != nsIContentPolicy::TYPE_SUBDOCUMENT &&
|
||||
contentType != nsIContentPolicy::TYPE_OBJECT) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoCString tCspHeaderValue, tCspROHeaderValue;
|
||||
|
||||
Unused << httpChannel->GetResponseHeader(
|
||||
NS_LITERAL_CSTRING("content-security-policy"), tCspHeaderValue);
|
||||
|
||||
Unused << httpChannel->GetResponseHeader(
|
||||
NS_LITERAL_CSTRING("content-security-policy-report-only"),
|
||||
tCspROHeaderValue);
|
||||
|
||||
// if there are no CSP values, then there is nothing to do here.
|
||||
if (tCspHeaderValue.IsEmpty() && tCspROHeaderValue.IsEmpty()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
|
||||
NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
|
||||
|
||||
RefPtr<nsCSPContext> csp = new nsCSPContext();
|
||||
nsCOMPtr<nsIPrincipal> resultPrincipal;
|
||||
rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
|
||||
aChannel, getter_AddRefs(resultPrincipal));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIURI> selfURI;
|
||||
aChannel->GetURI(getter_AddRefs(selfURI));
|
||||
|
||||
nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
|
||||
nsAutoString referrerSpec;
|
||||
if (referrerInfo) {
|
||||
referrerInfo->GetComputedReferrerSpec(referrerSpec);
|
||||
}
|
||||
uint64_t innerWindowID = loadInfo->GetInnerWindowID();
|
||||
|
||||
rv = csp->SetRequestContextWithPrincipal(resultPrincipal, selfURI,
|
||||
referrerSpec, innerWindowID);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// ----- if there's a full-strength CSP header, apply it.
|
||||
if (!cspHeaderValue.IsEmpty()) {
|
||||
rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// ----- if there's a report-only CSP header, apply it.
|
||||
if (!cspROHeaderValue.IsEmpty()) {
|
||||
rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// ----- Enforce frame-ancestor policy on any applied policies
|
||||
bool safeAncestry = false;
|
||||
// PermitsAncestry sends violation reports when necessary
|
||||
rv = csp->PermitsAncestry(loadInfo, &safeAncestry);
|
||||
|
||||
if (NS_FAILED(rv) || !safeAncestry) {
|
||||
// stop! ERROR page!
|
||||
aChannel->Cancel(NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION);
|
||||
return NS_ERROR_CSP_FRAME_ANCESTOR_VIOLATION;
|
||||
}
|
||||
|
||||
// return the CSP for x-frame-options check
|
||||
csp.forget(aOutCSP);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void EnforceXFrameOptionsCheck(nsIChannel* aChannel,
|
||||
nsIContentSecurityPolicy* aCsp) {
|
||||
MOZ_ASSERT(aChannel);
|
||||
if (!FramingChecker::CheckFrameOptions(aChannel, aCsp)) {
|
||||
// stop! ERROR page!
|
||||
aChannel->Cancel(NS_ERROR_XFO_VIOLATION);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(
|
||||
nsIChannel* aChannel) {
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
nsresult rv =
|
||||
ParseCSPAndEnforceFrameAncestorCheck(aChannel, getter_AddRefs(csp));
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// X-Frame-Options needs to be enforced after CSP frame-ancestors
|
||||
// checks because if frame-ancestors is present, then x-frame-options
|
||||
// will be discarded
|
||||
EnforceXFrameOptionsCheck(aChannel, csp);
|
||||
}
|
||||
|
||||
#if defined(DEBUG)
|
||||
/* static */
|
||||
void nsContentSecurityUtils::AssertAboutPageHasCSP(Document* aDocument) {
|
||||
|
@ -37,6 +37,12 @@ class nsContentSecurityUtils {
|
||||
static nsresult GetHttpChannelFromPotentialMultiPart(
|
||||
nsIChannel* aChannel, nsIHttpChannel** aHttpChannel);
|
||||
|
||||
// Helper function which performs the following framing checks
|
||||
// * CSP frame-ancestors
|
||||
// * x-frame-options
|
||||
// If any of the two disallows framing, the channel will be cancelled.
|
||||
static void PerformCSPFrameAncestorAndXFOCheck(nsIChannel* aChannel);
|
||||
|
||||
#if defined(DEBUG)
|
||||
static void AssertAboutPageHasCSP(mozilla::dom::Document* aDocument);
|
||||
#endif
|
||||
|
@ -58,12 +58,7 @@ SpecialPowers.registerObservers("csp-on-violate-policy");
|
||||
// This is used to watch the blocked data bounce off CSP and allowed data
|
||||
// get sent out to the wire.
|
||||
function examiner() {
|
||||
// Depending on whether frame-ancestor checks are performed in the parent
|
||||
// or in the content process we have to use the specialpowers version
|
||||
// for observing CSP violations. After Bug 1574372 we should be able
|
||||
// to remove the "csp-on-violate-policy" here.
|
||||
SpecialPowers.addObserver(this, "specialpowers-csp-on-violate-policy");
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy");
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe(subject, topic, data) {
|
||||
@ -89,8 +84,7 @@ examiner.prototype = {
|
||||
// was not an nsIURI, so it was probably a cross-origin report.
|
||||
}
|
||||
|
||||
if (topic === "specialpowers-csp-on-violate-policy" ||
|
||||
topic === "csp-on-violate-policy") {
|
||||
if (topic === "specialpowers-csp-on-violate-policy") {
|
||||
//these were blocked... record that they were blocked
|
||||
window.frameBlocked(asciiSpec, data);
|
||||
}
|
||||
@ -100,7 +94,6 @@ examiner.prototype = {
|
||||
// or mochitests might get borked.
|
||||
remove() {
|
||||
SpecialPowers.removeObserver(this, "specialpowers-csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,12 +29,7 @@ SpecialPowers.registerObservers("csp-on-violate-policy");
|
||||
// This is used to watch the blocked data bounce off CSP and allowed data
|
||||
// get sent out to the wire.
|
||||
function examiner() {
|
||||
// Depending on whether frame-ancestor checks are performed in the parent
|
||||
// or in the content process we have to use the specialpowers version
|
||||
// for observing CSP violations. After Bug 1574372 we should be able
|
||||
// to remove the "csp-on-violate-policy" here.
|
||||
SpecialPowers.addObserver(this, "specialpowers-csp-on-violate-policy");
|
||||
SpecialPowers.addObserver(this, "csp-on-violate-policy");
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe(subject, topic, data) {
|
||||
@ -60,8 +55,7 @@ examiner.prototype = {
|
||||
// was not an nsIURI, so it was probably a cross-origin report.
|
||||
}
|
||||
|
||||
if (topic === "specialpowers-csp-on-violate-policy" ||
|
||||
topic === "csp-on-violate-policy") {
|
||||
if (topic === "specialpowers-csp-on-violate-policy") {
|
||||
//these were blocked... record that they were blocked
|
||||
window.frameBlocked(asciiSpec, data);
|
||||
}
|
||||
@ -71,7 +65,6 @@ examiner.prototype = {
|
||||
// or mochitests might get borked.
|
||||
remove() {
|
||||
SpecialPowers.removeObserver(this, "specialpowers-csp-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "csp-on-violate-policy");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,12 +37,7 @@ function checkFinished() {
|
||||
SpecialPowers.registerObservers("xfo-on-violate-policy");
|
||||
|
||||
function examiner() {
|
||||
// Depending on whether xfo checks are performed in the parent
|
||||
// or in the content process we have to use the specialpowers version
|
||||
// for observing XFO violations. After Bug 1574372 we should be able
|
||||
// to remove the "xfo-on-violate-policy" here.
|
||||
SpecialPowers.addObserver(this, "specialpowers-xfo-on-violate-policy");
|
||||
SpecialPowers.addObserver(this, "xfo-on-violate-policy");
|
||||
}
|
||||
examiner.prototype = {
|
||||
observe(subject, topic, data) {
|
||||
@ -55,7 +50,6 @@ examiner.prototype = {
|
||||
},
|
||||
remove() {
|
||||
SpecialPowers.removeObserver(this, "specialpowers-xfo-on-violate-policy");
|
||||
SpecialPowers.removeObserver(this, "xfo-on-violate-policy");
|
||||
}
|
||||
}
|
||||
window.examiner = new examiner();
|
||||
|
@ -105,7 +105,6 @@
|
||||
#include "DecoderDoctorLogger.h"
|
||||
#include "MediaDecoder.h"
|
||||
#include "mozilla/ClearSiteData.h"
|
||||
#include "mozilla/dom/DOMSecurityManager.h"
|
||||
#include "mozilla/EditorController.h"
|
||||
#include "mozilla/Fuzzyfox.h"
|
||||
#include "mozilla/HTMLEditorController.h"
|
||||
@ -294,8 +293,6 @@ nsresult nsLayoutStatics::Initialize() {
|
||||
|
||||
ClearSiteData::Initialize();
|
||||
|
||||
DOMSecurityManager::Initialize();
|
||||
|
||||
// Reporting API.
|
||||
ReportingHeader::Initialize();
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "mozilla/net/RedirectChannelRegistrar.h"
|
||||
#include "nsDocShell.h"
|
||||
#include "nsDocShellLoadState.h"
|
||||
#include "nsContentSecurityUtils.h"
|
||||
#include "nsHttpChannel.h"
|
||||
#include "nsISecureBrowserUI.h"
|
||||
#include "nsRedirectHistoryEntry.h"
|
||||
@ -956,6 +957,10 @@ DocumentLoadListener::OnStartRequest(nsIRequest* aRequest) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
// Enforce CSP frame-ancestors and x-frame-options checks which
|
||||
// might cancel the channel.
|
||||
nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(mChannel);
|
||||
|
||||
// Once we initiate a process switch, we ask the child to notify the
|
||||
// listeners that we have completed. If the switch promise then gets
|
||||
// rejected we also cancel the parent, which results in this being called.
|
||||
|
@ -1,5 +0,0 @@
|
||||
[frame-ancestors-from-serviceworker.https.html]
|
||||
[A 'frame-ancestors' CSP directive set from a serviceworker response with a value 'none' should block rendering.]
|
||||
expected:
|
||||
if fission: FAIL
|
||||
|
Loading…
Reference in New Issue
Block a user