Bug 1226909 part 1: Do security checks in a redirect handler rather than when opening the redirected channel. r=ckerschb

This commit is contained in:
Jonas Sicking 2015-12-05 01:46:20 -08:00
parent 785d5def22
commit 224b3b9b00
11 changed files with 109 additions and 106 deletions

View File

@ -431,7 +431,6 @@ nsScriptSecurityManager::IsSystemPrincipal(nsIPrincipal* aPrincipal,
////////////////////////////////////
NS_IMPL_ISUPPORTS(nsScriptSecurityManager,
nsIScriptSecurityManager,
nsIChannelEventSink,
nsIObserver)
///////////////////////////////////////////////////
@ -1236,41 +1235,6 @@ nsScriptSecurityManager::CanGetService(JSContext *cx,
return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
}
/////////////////////////////////////////////
// Method implementing nsIChannelEventSink //
/////////////////////////////////////////////
NS_IMETHODIMP
nsScriptSecurityManager::AsyncOnChannelRedirect(nsIChannel* oldChannel,
nsIChannel* newChannel,
uint32_t redirFlags,
nsIAsyncVerifyRedirectCallback *cb)
{
nsCOMPtr<nsIPrincipal> oldPrincipal;
GetChannelResultPrincipal(oldChannel, getter_AddRefs(oldPrincipal));
nsCOMPtr<nsIURI> newURI;
newChannel->GetURI(getter_AddRefs(newURI));
nsCOMPtr<nsIURI> newOriginalURI;
newChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
const uint32_t flags =
nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
nsIScriptSecurityManager::DISALLOW_SCRIPT;
nsresult rv = CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
rv = CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
}
if (NS_FAILED(rv))
return rv;
cb->OnRedirectVerifyCallback(NS_OK);
return NS_OK;
}
/////////////////////////////////////
// Method implementing nsIObserver //
/////////////////////////////////////

View File

@ -14,7 +14,6 @@
#include "nsIAddonPolicyService.h"
#include "nsIPrincipal.h"
#include "nsCOMPtr.h"
#include "nsIChannelEventSink.h"
#include "nsIObserver.h"
#include "nsServiceManagerUtils.h"
#include "plstr.h"
@ -39,7 +38,6 @@ class PrincipalOriginAttributes;
{ 0xba, 0x18, 0x00, 0x60, 0xb0, 0xf1, 0x99, 0xa2 }}
class nsScriptSecurityManager final : public nsIScriptSecurityManager,
public nsIChannelEventSink,
public nsIObserver
{
public:
@ -49,7 +47,6 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSISCRIPTSECURITYMANAGER
NS_DECL_NSICHANNELEVENTSINK
NS_DECL_NSIOBSERVER
static nsScriptSecurityManager*

View File

@ -8,7 +8,9 @@
#include "mozilla/dom/Element.h"
NS_IMPL_ISUPPORTS(nsContentSecurityManager, nsIContentSecurityManager)
NS_IMPL_ISUPPORTS(nsContentSecurityManager,
nsIContentSecurityManager,
nsIChannelEventSink)
static nsresult
ValidateSecurityFlags(nsILoadInfo* aLoadInfo)
@ -342,22 +344,17 @@ nsContentSecurityManager::doContentSecurityCheck(nsIChannel* aChannel,
return NS_ERROR_UNEXPECTED;
}
// if dealing with a redirected channel then we have already installed
// streamlistener and redirect proxies and so we are done.
if (loadInfo->GetInitialSecurityCheckDone()) {
return NS_OK;
}
// make sure that only one of the five security flags is set in the loadinfo
// e.g. do not require same origin and allow cross origin at the same time
nsresult rv = ValidateSecurityFlags(loadInfo);
NS_ENSURE_SUCCESS(rv, rv);
// lets store the initialSecurityCheckDone flag which indicates whether the channel
// was initialy evaluated by the contentSecurityManager. Once the inital
// asyncOpen() of the channel went through the contentSecurityManager then
// redirects do not have perform all the security checks, e.g. no reason
// to setup CORS again.
bool initialSecurityCheckDone = loadInfo->GetInitialSecurityCheckDone();
// now lets set the initalSecurityFlag for subsequent calls
rv = loadInfo->SetInitialSecurityCheckDone(true);
NS_ENSURE_SUCCESS(rv, rv);
// since aChannel was openend using asyncOpen2() we have to make sure
// that redirects of that channel also get openend using asyncOpen2()
// please note that some implementations of ::AsyncOpen2 might already
@ -366,49 +363,116 @@ nsContentSecurityManager::doContentSecurityCheck(nsIChannel* aChannel,
rv = loadInfo->SetEnforceSecurity(true);
NS_ENSURE_SUCCESS(rv, rv);
if (loadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
rv = CheckChannel(aChannel);
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIURI> finalChannelURI;
rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI));
NS_ENSURE_SUCCESS(rv, rv);
// Perform all ContentPolicy checks (MixedContent, CSP, ...)
rv = DoContentSecurityChecks(finalChannelURI, loadInfo);
NS_ENSURE_SUCCESS(rv, rv);
// now lets set the initalSecurityFlag for subsequent calls
rv = loadInfo->SetInitialSecurityCheckDone(true);
NS_ENSURE_SUCCESS(rv, rv);
// all security checks passed - lets allow the load
return NS_OK;
}
NS_IMETHODIMP
nsContentSecurityManager::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
nsIChannel* aNewChannel,
uint32_t aRedirFlags,
nsIAsyncVerifyRedirectCallback *aCb)
{
nsCOMPtr<nsILoadInfo> loadInfo = aOldChannel->GetLoadInfo();
// Are we enforcing security using LoadInfo?
if (loadInfo && loadInfo->GetEnforceSecurity()) {
nsresult rv = CheckChannel(aNewChannel);
if (NS_FAILED(rv)) {
aOldChannel->Cancel(rv);
return rv;
}
}
// Also verify that the redirecting server is allowed to redirect to the
// given URI
nsCOMPtr<nsIPrincipal> oldPrincipal;
nsContentUtils::GetSecurityManager()->
GetChannelResultPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
nsCOMPtr<nsIURI> newURI;
aNewChannel->GetURI(getter_AddRefs(newURI));
nsCOMPtr<nsIURI> newOriginalURI;
aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
const uint32_t flags =
nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
nsIScriptSecurityManager::DISALLOW_SCRIPT;
nsresult rv = nsContentUtils::GetSecurityManager()->
CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
rv = nsContentUtils::GetSecurityManager()->
CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
}
NS_ENSURE_SUCCESS(rv, rv);
aCb->OnRedirectVerifyCallback(NS_OK);
return NS_OK;
}
/*
* Check that this channel passes all security checks. Returns an error code
* if this requesst should not be permitted.
*/
nsresult
nsContentSecurityManager::CheckChannel(nsIChannel* aChannel)
{
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
MOZ_ASSERT(loadInfo);
// CORS mode is handled by nsCORSListenerProxy
nsSecurityFlags securityMode = loadInfo->GetSecurityMode();
if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
return NS_OK;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
// if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply
if ((securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) ||
(securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) {
rv = DoSOPChecks(finalChannelURI, loadInfo);
rv = DoSOPChecks(uri, loadInfo);
NS_ENSURE_SUCCESS(rv, rv);
}
// if dealing with a redirected channel then we only enforce SOP
// and can return at this point.
if (initialSecurityCheckDone) {
return NS_OK;
}
if ((securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) ||
(securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) {
// Please note that DoCheckLoadURIChecks should only be enforced for
// cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set
// within the loadInfo, then then CheckLoadURIWithPrincipal is performed
// within nsCorsListenerProxy
rv = DoCheckLoadURIChecks(finalChannelURI, loadInfo);
rv = DoCheckLoadURIChecks(uri, loadInfo);
NS_ENSURE_SUCCESS(rv, rv);
}
if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener);
NS_ENSURE_SUCCESS(rv, rv);
}
// Perform all ContentPolicy checks (MixedContent, CSP, ...)
rv = DoContentSecurityChecks(finalChannelURI, loadInfo);
NS_ENSURE_SUCCESS(rv, rv);
// all security checks passed - lets allow the load
return NS_OK;
}
// ==== nsIContentSecurityManager implementation =====
NS_IMETHODIMP

View File

@ -9,6 +9,7 @@
#include "nsIContentSecurityManager.h"
#include "nsIChannel.h"
#include "nsIChannelEventSink.h"
class nsIStreamListener;
@ -19,10 +20,12 @@ class nsIStreamListener;
{ 0xa2, 0x94, 0xa6, 0x51, 0xfa, 0x35, 0x22, 0x7f } }
class nsContentSecurityManager : public nsIContentSecurityManager
, public nsIChannelEventSink
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICONTENTSECURITYMANAGER
NS_DECL_NSICHANNELEVENTSINK
nsContentSecurityManager() {}
@ -30,6 +33,8 @@ public:
nsCOMPtr<nsIStreamListener>& aInAndOutListener);
private:
static nsresult CheckChannel(nsIChannel* aChannel);
virtual ~nsContentSecurityManager() {}
};

View File

@ -18,7 +18,6 @@ var tests = { "font-src": thisSite+page+"?testid=font-src",
"object-src": thisSite+page+"?testid=object-src",
"script-src": thisSite+page+"?testid=script-src",
"style-src": thisSite+page+"?testid=style-src",
"worker": thisSite+page+"?testid=worker",
"xhr-src": thisSite+page+"?testid=xhr-src",
"from-worker": thisSite+page+"?testid=from-worker",
"from-blob-worker": thisSite+page+"?testid=from-blob-worker",

View File

@ -14,13 +14,8 @@ function handleRequest(request, response)
var resource = "/tests/dom/security/test/csp/file_redirects_resource.sjs";
// CSP header value
var additional = ""
if (query['testid'] == "worker") {
additional = "; script-src 'self' 'unsafe-inline'";
}
response.setHeader("Content-Security-Policy",
"default-src 'self' blob: ; style-src 'self' 'unsafe-inline'" + additional,
false);
"default-src 'self' blob: ; style-src 'self' 'unsafe-inline'", false);
// downloadable font that redirects to another site
if (query["testid"] == "font-src") {
@ -69,12 +64,6 @@ function handleRequest(request, response)
return;
}
// worker script resource that redirects to another site
if (query["testid"] == "worker") {
response.write('<script>var worker = new Worker("'+resource+'?res=worker&redir=other&id=worker-redir");</script>');
return;
}
// script that XHR's to a resource that redirects to another site
if (query["testid"] == "xhr-src") {
response.write('<script src="'+resource+'?res=xhr"></script>');

View File

@ -85,13 +85,6 @@ function handleRequest(request, response)
return;
}
// web worker resource
if (query["res"] == "worker") {
response.setHeader("Content-Type", "application/javascript", false);
response.write("worker script data...");
return;
}
// internal stylesheet that loads an image from an external site
if (query["res"] == "cssLoader") {
let bgURL = thisSite + resource + '?redir=other&res=image&id=' + query["id"];

View File

@ -82,14 +82,12 @@ var testExpectedResults = { "font-src": true,
"script-src-redir": false,
"style-src": true,
"style-src-redir": false,
"worker": true,
"worker-redir": false,
"xhr-src": true,
"xhr-src-redir": false,
"from-worker": true,
"script-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */
"xhr-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */
"fetch-src-redir-from-worker": true, /* redir is allowed since policy isn't inherited */
"script-src-redir-from-worker": true, // redir is allowed since policy isn't inherited
"xhr-src-redir-from-worker": true, // redir is allowed since policy isn't inherited
"fetch-src-redir-from-worker": true, // redir is allowed since policy isn't inherited
"from-blob-worker": true,
"script-src-redir-from-blob-worker": false,
"xhr-src-redir-from-blob-worker": false,

View File

@ -1285,7 +1285,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
{ NS_PARENTPROCESSMESSAGEMANAGER_CONTRACTID, &kNS_PARENTPROCESSMESSAGEMANAGER_CID },
{ NS_CHILDPROCESSMESSAGEMANAGER_CONTRACTID, &kNS_CHILDPROCESSMESSAGEMANAGER_CID },
{ NS_SCRIPTSECURITYMANAGER_CONTRACTID, &kNS_SCRIPTSECURITYMANAGER_CID },
{ NS_GLOBAL_CHANNELEVENTSINK_CONTRACTID, &kNS_SCRIPTSECURITYMANAGER_CID },
{ NS_PRINCIPAL_CONTRACTID, &kNS_PRINCIPAL_CID },
{ NS_SYSTEMPRINCIPAL_CONTRACTID, &kNS_SYSTEMPRINCIPAL_CID },
{ NS_NULLPRINCIPAL_CONTRACTID, &kNS_NULLPRINCIPAL_CID },

View File

@ -50,6 +50,7 @@
#include "CaptivePortalService.h"
#include "ClosingService.h"
#include "ReferrerPolicy.h"
#include "nsContentSecurityManager.h"
#ifdef MOZ_WIDGET_GONK
#include "nsINetworkManager.h"
@ -415,8 +416,11 @@ nsIOService::AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
// are in a captive portal, so we trigger a recheck.
RecheckCaptivePortalIfLocalRedirect(newChan);
// This is silly. I wish there was a simpler way to get at the global
// reference of the contentSecurityManager. But it lives in the XPCOM
// service registry.
nsCOMPtr<nsIChannelEventSink> sink =
do_GetService(NS_GLOBAL_CHANNELEVENTSINK_CONTRACTID);
do_GetService(NS_CONTENTSECURITYMANAGER_CONTRACTID);
if (sink) {
nsresult rv = helper->DelegateOnChannelRedirect(sink, oldChan,
newChan, flags);

View File

@ -1004,15 +1004,6 @@
* Contracts that can be implemented by necko users.
*/
/**
* This contract ID will be gotten as a service and gets the opportunity to look
* at and veto all redirects that are processed by necko.
*
* Must implement nsIChannelEventSink
*/
#define NS_GLOBAL_CHANNELEVENTSINK_CONTRACTID \
"@mozilla.org/netwerk/global-channel-event-sink;1"
/**
* This contract ID will be gotten as a service implementing nsINetworkLinkService
* and monitored by IOService for automatic online/offline management.