Bug 1188028 - Use channel->ascynOpen2 in dom/security/nsCSPContext.cpp (r=sicking)

This commit is contained in:
Christoph Kerschbaumer 2015-07-27 11:57:56 -07:00
parent 5aac275e85
commit a876eba5c9
10 changed files with 90 additions and 148 deletions

View File

@ -71,7 +71,6 @@ public:
bool MayLoadInternal(nsIURI* aURI) override;
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
};
#endif // nsNullPrincipal_h__

View File

@ -25,6 +25,7 @@
#include "nsNetCID.h"
#include "jswrapper.h"
#include "mozilla/dom/nsCSPContext.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/Preferences.h"
#include "mozilla/HashFunctions.h"
@ -76,7 +77,12 @@ nsPrincipal::nsPrincipal()
{ }
nsPrincipal::~nsPrincipal()
{ }
{
// let's clear the principal within the csp to avoid a tangling pointer
if (mCSP) {
static_cast<nsCSPContext*>(mCSP.get())->clearLoadingPrincipal();
}
}
nsresult
nsPrincipal::Init(nsIURI *aCodebase, const OriginAttributes& aOriginAttributes)
@ -404,7 +410,7 @@ nsPrincipal::Read(nsIObjectInputStream* aStream)
// need to link in the CSP context here (link in the URI of the protected
// resource).
if (csp) {
csp->SetRequestContext(codebase, nullptr, nullptr);
csp->SetRequestContext(nullptr, this);
}
SetDomain(domain);

View File

@ -2779,7 +2779,7 @@ nsDocument::InitCSP(nsIChannel* aChannel)
aChannel->GetURI(getter_AddRefs(selfURI));
// Store the request context for violation reports
csp->SetRequestContext(nullptr, nullptr, aChannel);
csp->SetRequestContext(this, nullptr);
// ----- if the doc is an app and we want a default CSP, apply it.
if (applyAppDefaultCSP) {

View File

@ -8,6 +8,7 @@
interface nsIURI;
interface nsIChannel;
interface nsIDocShell;
interface nsIDOMDocument;
interface nsIPrincipal;
interface nsIURI;
@ -20,7 +21,7 @@ interface nsIURI;
typedef unsigned short CSPDirective;
[scriptable, uuid(36c6d419-24c2-40e8-9adb-11d0b1341770)]
[scriptable, builtinclass, uuid(b756d344-ee2f-44d0-825e-ea4febd0af14)]
interface nsIContentSecurityPolicy : nsISerializable
{
/**
@ -173,16 +174,12 @@ interface nsIContentSecurityPolicy : nsISerializable
/**
* Called after the CSP object is created to fill in appropriate request
* context and give it a reference to its owning principal for violation
* report generation.
* This will use whatever data is available, choosing earlier arguments first
* if multiple are available. Either way, it will attempt to obtain the URI,
* referrer and the principal from whatever is available. If the channel is
* available, it'll also store that for processing policy-uri directives.
* context. Either use
* * aDocument (preferred), or if no document is available, then provide
* * aPrincipal
*/
void setRequestContext(in nsIURI selfURI,
in nsIURI referrer,
in nsIChannel aChannel);
void setRequestContext(in nsIDOMDocument aDocument,
in nsIPrincipal aPrincipal);
/**
* Verifies ancestry as permitted by the policy.

View File

@ -31,11 +31,11 @@
#include "nsIWebNavigation.h"
#include "nsMimeTypes.h"
#include "nsNetUtil.h"
#include "nsNullPrincipal.h"
#include "nsIContentPolicy.h"
#include "nsSupportsPrimitives.h"
#include "nsThreadUtils.h"
#include "nsString.h"
#include "nsScriptSecurityManager.h"
#include "nsStringStream.h"
#include "mozilla/Logging.h"
#include "mozilla/dom/CSPReportBinding.h"
@ -265,7 +265,9 @@ NS_IMPL_ISUPPORTS_CI(nsCSPContext,
nsISerializable)
nsCSPContext::nsCSPContext()
: mSelfURI(nullptr)
: mInnerWindowID(0)
, mLoadingContext(nullptr)
, mLoadingPrincipal(nullptr)
{
CSPCONTEXTLOG(("nsCSPContext::nsCSPContext"));
}
@ -590,52 +592,28 @@ nsCSPContext::LogViolationDetails(uint16_t aViolationType,
#undef CASE_CHECK_AND_REPORT
NS_IMETHODIMP
nsCSPContext::SetRequestContext(nsIURI* aSelfURI,
nsIURI* aReferrer,
nsIChannel* aChannel)
nsCSPContext::SetRequestContext(nsIDOMDocument* aDOMDocument,
nsIPrincipal* aPrincipal)
{
NS_PRECONDITION(aSelfURI || aChannel, "Need aSelfURI or aChannel to set the context properly");
NS_ENSURE_ARG(aSelfURI || aChannel);
NS_PRECONDITION(aDOMDocument || aPrincipal,
"Can't set context without doc or principal");
NS_ENSURE_ARG(aDOMDocument || aPrincipal);
// first use aSelfURI. If that's not available get the URI from aChannel.
mSelfURI = aSelfURI;
if (!mSelfURI) {
nsresult rv = aChannel->GetURI(getter_AddRefs(mSelfURI));
NS_ENSURE_SUCCESS(rv, rv);
}
NS_ASSERTION(mSelfURI, "No aSelfURI and no URI available from channel in SetRequestContext, can not translate 'self' into actual URI");
if (aChannel) {
mInnerWindowID = nsContentUtils::GetInnerWindowID(aChannel);
aChannel->GetLoadGroup(getter_AddRefs(mCallingChannelLoadGroup));
// Storing the nsINode from the LoadInfo of the original channel,
// so we can reuse that information when sending reports.
nsCOMPtr<nsILoadInfo> loadInfo;
aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
if (loadInfo) {
nsINode* loadingNode = loadInfo->LoadingNode();
if (loadingNode) {
mLoadingContext = do_GetWeakReference(loadingNode);
}
}
if (aDOMDocument) {
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDOMDocument);
mLoadingContext = do_GetWeakReference(doc);
mSelfURI = doc->GetDocumentURI();
doc->GetReferrer(mReferrer);
mInnerWindowID = doc->InnerWindowID();
mCallingChannelLoadGroup = doc->GetDocumentLoadGroup();
}
else {
NS_WARNING("Channel needed (but null) in SetRequestContext. Cannot query loadgroup, which means report sending may fail.");
}
mReferrer = aReferrer;
if (!mReferrer) {
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
if (httpChannel) {
httpChannel->GetReferrer(getter_AddRefs(mReferrer));
}
else {
NS_WARNING("Channel provided to SetRequestContext is not an nsIHttpChannel so referrer is not available for reporting." );
}
mLoadingPrincipal = aPrincipal;
mLoadingPrincipal->GetURI(getter_AddRefs(mSelfURI));
NS_WARNING("No Document in SetRequestContext; can not query loadgroup; sending reports may fail.");
}
NS_ASSERTION(mSelfURI, "mSelfURI not available, can not translate 'self' into actual URI");
return NS_OK;
}
@ -720,10 +698,8 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
report.mCsp_report.mOriginal_policy = originalPolicy;
// referrer
if (mReferrer) {
nsAutoCString referrerURI;
mReferrer->GetSpec(referrerURI);
report.mCsp_report.mReferrer = NS_ConvertUTF8toUTF16(referrerURI);
if (!mReferrer.IsEmpty()) {
report.mCsp_report.mReferrer = mReferrer;
}
// violated-directive
@ -765,12 +741,11 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
nsTArray<nsString> reportURIs;
mPolicies[aViolatedPolicyIndex]->getReportURIs(reportURIs);
nsCOMPtr<nsIDocument> doc = do_QueryReferent(mLoadingContext);
nsCOMPtr<nsIURI> reportURI;
nsCOMPtr<nsIChannel> reportChannel;
nsCOMPtr<nsIDOMNode> loadingContext = do_QueryReferent(mLoadingContext);
nsCOMPtr<nsINode> loadingNode = do_QueryInterface(loadingContext);
for (uint32_t r = 0; r < reportURIs.Length(); r++) {
nsAutoCString reportURICstring = NS_ConvertUTF16toUTF8(reportURIs[r]);
// try to create a new uri from every report-uri string
@ -786,27 +761,19 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
continue; // don't return yet, there may be more URIs
}
nsIDocShell* docShell = nullptr;
// try to create a new channel for every report-uri
if (loadingNode) {
nsIDocument* doc = loadingNode->OwnerDoc();
if (doc) {
docShell = doc->GetDocShell();
}
if (doc) {
rv = NS_NewChannel(getter_AddRefs(reportChannel),
reportURI,
loadingNode,
nsILoadInfo::SEC_NORMAL,
doc,
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_CSP_REPORT);
}
else {
nsCOMPtr<nsIPrincipal> nullPrincipal = nsNullPrincipal::Create();
NS_ENSURE_TRUE(nullPrincipal, NS_ERROR_FAILURE);
rv = NS_NewChannel(getter_AddRefs(reportChannel),
reportURI,
nullPrincipal,
nsILoadInfo::SEC_NORMAL,
mLoadingPrincipal,
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
nsIContentPolicy::TYPE_CSP_REPORT);
}
@ -841,8 +808,9 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
// we need to set an nsIChannelEventSink on the channel object
// so we can tell it to not follow redirects when posting the reports
RefPtr<CSPReportRedirectSink> reportSink = new CSPReportRedirectSink();
if (docShell) {
nsCOMPtr<nsINetworkInterceptController> interceptController = do_QueryInterface(docShell);
if (doc && doc->GetDocShell()) {
nsCOMPtr<nsINetworkInterceptController> interceptController =
do_QueryInterface(doc->GetDocShell());
reportSink->SetInterceptController(interceptController);
}
reportChannel->SetNotificationCallbacks(reportSink);
@ -853,32 +821,6 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
rv = reportChannel->SetLoadGroup(mCallingChannelLoadGroup);
NS_ENSURE_SUCCESS(rv, rv);
// check content policy
int16_t shouldLoad = nsIContentPolicy::ACCEPT;
nsCOMPtr<nsIContentPolicy> cp = do_GetService(NS_CONTENTPOLICY_CONTRACTID);
if (!cp) {
return NS_ERROR_FAILURE;
}
rv = cp->ShouldLoad(nsIContentPolicy::TYPE_CSP_REPORT,
reportURI,
mSelfURI,
nullptr, // Context
EmptyCString(), // mime type
nullptr, // Extra parameter
nullptr, // optional request principal
&shouldLoad);
// refuse to load if we can't do a security check
NS_ENSURE_SUCCESS(rv, rv);
if (NS_CP_REJECTED(shouldLoad)) {
// skip unauthorized URIs
CSPCONTEXTLOG(("nsIContentPolicy blocked sending report to %s",
reportURICstring.get()));
continue; // don't return yet, there may be more URIs
}
// wire in the string input stream to send the report
nsCOMPtr<nsIStringInputStream> sis(do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID));
NS_ASSERTION(sis, "nsIStringInputStream is needed but not available to send CSP violation reports");
@ -897,7 +839,7 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource,
}
RefPtr<CSPViolationReportListener> listener = new CSPViolationReportListener();
rv = reportChannel->AsyncOpen(listener, nullptr);
rv = reportChannel->AsyncOpen2(listener);
// AsyncOpen should not fail, but could if there's no load group (like if
// SetRequestContext is not given a channel). This should fail quietly and

View File

@ -57,6 +57,13 @@ class nsCSPContext : public nsIContentSecurityPolicy
const nsAString& aScriptSample,
uint32_t aLineNum);
// Hands off! Don't call this method unless you know what you
// are doing. It's only supposed to be called from within
// the principal destructor to avoid a tangling pointer.
void clearLoadingPrincipal() {
mLoadingPrincipal = nullptr;
}
private:
bool permitsInternal(CSPDirective aDir,
nsIURI* aContentLocation,
@ -76,13 +83,17 @@ class nsCSPContext : public nsIContentSecurityPolicy
uint32_t aViolatedPolicyIndex,
uint32_t aLineNumber);
nsCOMPtr<nsIURI> mReferrer;
nsString mReferrer;
uint64_t mInnerWindowID; // used for web console logging
nsTArray<nsCSPPolicy*> mPolicies;
nsCOMPtr<nsIURI> mSelfURI;
nsDataHashtable<nsCStringHashKey, int16_t> mShouldLoadCache;
nsCOMPtr<nsILoadGroup> mCallingChannelLoadGroup;
nsWeakPtr mLoadingContext;
// The CSP hangs off the principal, so let's store a raw pointer of the principal
// to avoid memory leaks. Within the destructor of the principal we explicitly
// set mLoadingPrincipal to null.
nsIPrincipal* mLoadingPrincipal;
};
// Class that listens to violation report transmission and logs errors.

View File

@ -237,12 +237,17 @@ DoContentSecurityChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo)
break;
}
case nsIContentPolicy::TYPE_WEBSOCKET:
case nsIContentPolicy::TYPE_CSP_REPORT: {
case nsIContentPolicy::TYPE_WEBSOCKET: {
MOZ_ASSERT(false, "contentPolicyType not supported yet");
break;
}
case nsIContentPolicy::TYPE_CSP_REPORT: {
mimeTypeGuess = EmptyCString();
requestingContext = aLoadInfo->LoadingNode();
break;
}
case nsIContentPolicy::TYPE_XSLT: {
mimeTypeGuess = NS_LITERAL_CSTRING("application/xml");
requestingContext = aLoadInfo->LoadingNode();

View File

@ -90,26 +90,18 @@ nsresult runTest(uint32_t aExpectedPolicyCount, // this should be 0 for policies
const char* aPolicy,
const char* aExpextedResult) {
// we init the csp with http://www.selfuri.com
nsCOMPtr<nsIURI> selfURI;
nsresult rv = NS_NewURI(getter_AddRefs(selfURI), "http://www.selfuri.com");
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv;
nsCOMPtr<nsIScriptSecurityManager> secman =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrincipal> systemPrincipal;
rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
// we init the csp with http://www.selfuri.com
nsCOMPtr<nsIURI> selfURI;
rv = NS_NewURI(getter_AddRefs(selfURI), "http://www.selfuri.com");
NS_ENSURE_SUCCESS(rv, rv);
// we also init the csp with a dummyChannel, which is unused
// for the parser tests but surpresses assertions in SetRequestContext.
nsCOMPtr<nsIChannel> dummyChannel;
rv = NS_NewChannel(getter_AddRefs(dummyChannel),
selfURI,
systemPrincipal,
nsILoadInfo::SEC_NORMAL,
nsIContentPolicy::TYPE_OTHER);
nsCOMPtr<nsIPrincipal> selfURIPrincipal;
rv = secman->GetSimpleCodebasePrincipal(selfURI, getter_AddRefs(selfURIPrincipal));
NS_ENSURE_SUCCESS(rv, rv);
// create a CSP object
@ -117,12 +109,9 @@ nsresult runTest(uint32_t aExpectedPolicyCount, // this should be 0 for policies
do_CreateInstance(NS_CSPCONTEXT_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
// for testing the parser we only need to set selfURI which is needed
// to translate the keyword 'self' into an actual URI. All other
// arguments can be nullptrs.
csp->SetRequestContext(selfURI,
nullptr, // nsIURI* aReferrer
dummyChannel);
// for testing the parser we only need to set a principal which is needed
// to translate the keyword 'self' into an actual URI.
rv = csp->SetRequestContext(nullptr, selfURIPrincipal);
NS_ENSURE_SUCCESS(rv, rv);
// append a policy

View File

@ -21,6 +21,7 @@
SimpleTest.waitForExplicitFinish();
var app;
var principal;
function setupTest() {
// We have to install an app in order for the app URL to be valid
@ -37,21 +38,12 @@
}
function runTest() {
// We have to use a mochitest to test app:// urls,
// as app channels can't be instanciated in xpcshell.
// Because app protocol depends on webapps.jsm,
// which doesn't instanciate properly on xpcshell without many hacks
let appchan = SpecialPowers.Services.io.newChannel2(gManifestURL,
null,
null,
null, // aLoadingNode
SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
null, // aTriggeringPrincipal
SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL,
SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
try {
csp.setRequestContext(null, null, appchan);
var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(SpecialPowers.Ci.nsIScriptSecurityManager);
var manifestURI = SpecialPowers.Services.io.newURI(gManifestURL, null, null);
principal = secMan.getSimpleCodebasePrincipal(manifestURI);
csp.setRequestContext(null, principal);
ok(true, "setRequestContext hasn't thown");
} catch(e) {
ok(false, "setRequestContext throws");

View File

@ -15,6 +15,8 @@ var httpServer = new HttpServer();
httpServer.start(-1);
var testsToFinish = 0;
var principal;
const REPORT_SERVER_PORT = httpServer.identity.primaryPort;
const REPORT_SERVER_URI = "http://localhost";
const REPORT_SERVER_PATH = "/report";
@ -72,14 +74,13 @@ function makeTest(id, expectedJSON, useReportOnlyPolicy, callback) {
var selfuri = NetUtil.newURI(REPORT_SERVER_URI +
":" + REPORT_SERVER_PORT +
"/foo/self");
var selfchan = NetUtil.newChannel({
uri: selfuri,
loadUsingSystemPrincipal: true});
dump("Created test " + id + " : " + policy + "\n\n");
// make the reports seem authentic by "binding" them to a channel.
csp.setRequestContext(selfuri, null, selfchan);
let ssm = Cc["@mozilla.org/scriptsecuritymanager;1"]
.getService(Ci.nsIScriptSecurityManager);
principal = ssm.getSimpleCodebasePrincipal(selfuri);
csp.setRequestContext(null, principal);
// Load up the policy
// set as report-only if that's the case