2010-01-22 13:38:21 -08:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2012-05-21 04:12:37 -07:00
|
|
|
/* 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/. */
|
2010-01-22 13:38:21 -08:00
|
|
|
|
|
|
|
#include "prlog.h"
|
|
|
|
#include "nsString.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
|
|
#include "nsIURI.h"
|
|
|
|
#include "nsIPrincipal.h"
|
|
|
|
#include "nsIObserver.h"
|
|
|
|
#include "nsIContent.h"
|
|
|
|
#include "nsCSPService.h"
|
|
|
|
#include "nsIContentSecurityPolicy.h"
|
2010-04-23 10:03:03 -07:00
|
|
|
#include "nsIChannelPolicy.h"
|
|
|
|
#include "nsIChannelEventSink.h"
|
|
|
|
#include "nsIPropertyBag2.h"
|
|
|
|
#include "nsIWritablePropertyBag2.h"
|
|
|
|
#include "nsNetError.h"
|
|
|
|
#include "nsChannelProperties.h"
|
2010-08-04 19:15:55 -07:00
|
|
|
#include "nsIAsyncVerifyRedirectCallback.h"
|
|
|
|
#include "nsAsyncRedirectVerifyHelper.h"
|
2011-05-28 16:42:57 -07:00
|
|
|
#include "mozilla/Preferences.h"
|
2011-12-05 09:42:08 -08:00
|
|
|
#include "nsIScriptError.h"
|
|
|
|
#include "nsContentUtils.h"
|
2011-05-28 16:42:57 -07:00
|
|
|
|
|
|
|
using namespace mozilla;
|
2010-01-22 13:38:21 -08:00
|
|
|
|
|
|
|
/* Keeps track of whether or not CSP is enabled */
|
2011-09-28 23:19:26 -07:00
|
|
|
bool CSPService::sCSPEnabled = true;
|
2010-01-22 13:38:21 -08:00
|
|
|
|
|
|
|
#ifdef PR_LOGGING
|
|
|
|
static PRLogModuleInfo* gCspPRLog;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
CSPService::CSPService()
|
|
|
|
{
|
2011-05-28 16:42:57 -07:00
|
|
|
Preferences::AddBoolVarCache(&sCSPEnabled, "security.csp.enable");
|
2010-01-22 13:38:21 -08:00
|
|
|
|
|
|
|
#ifdef PR_LOGGING
|
|
|
|
if (!gCspPRLog)
|
|
|
|
gCspPRLog = PR_NewLogModule("CSP");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
CSPService::~CSPService()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2010-04-23 10:03:03 -07:00
|
|
|
NS_IMPL_ISUPPORTS2(CSPService, nsIContentPolicy, nsIChannelEventSink)
|
2010-01-22 13:38:21 -08:00
|
|
|
|
|
|
|
/* nsIContentPolicy implementation */
|
|
|
|
NS_IMETHODIMP
|
|
|
|
CSPService::ShouldLoad(PRUint32 aContentType,
|
|
|
|
nsIURI *aContentLocation,
|
|
|
|
nsIURI *aRequestOrigin,
|
|
|
|
nsISupports *aRequestContext,
|
|
|
|
const nsACString &aMimeTypeGuess,
|
|
|
|
nsISupports *aExtra,
|
|
|
|
PRInt16 *aDecision)
|
|
|
|
{
|
|
|
|
if (!aContentLocation)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
2010-04-23 10:03:03 -07:00
|
|
|
#ifdef PR_LOGGING
|
2010-01-22 13:38:21 -08:00
|
|
|
{
|
|
|
|
nsCAutoString location;
|
|
|
|
aContentLocation->GetSpec(location);
|
2010-04-23 10:03:03 -07:00
|
|
|
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
2010-01-22 13:38:21 -08:00
|
|
|
("CSPService::ShouldLoad called for %s", location.get()));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// default decision, CSP can revise it if there's a policy to enforce
|
|
|
|
*aDecision = nsIContentPolicy::ACCEPT;
|
|
|
|
|
|
|
|
// No need to continue processing if CSP is disabled
|
2010-07-12 20:12:32 -07:00
|
|
|
if (!sCSPEnabled)
|
2010-01-22 13:38:21 -08:00
|
|
|
return NS_OK;
|
|
|
|
|
2010-04-23 10:03:03 -07:00
|
|
|
// find the principal of the document that initiated this request and see
|
|
|
|
// if it has a CSP policy object
|
2010-01-22 13:38:21 -08:00
|
|
|
nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
|
|
|
|
nsCOMPtr<nsIPrincipal> principal;
|
|
|
|
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
|
|
|
if (node) {
|
|
|
|
principal = node->NodePrincipal();
|
|
|
|
principal->GetCsp(getter_AddRefs(csp));
|
|
|
|
|
|
|
|
if (csp) {
|
2010-04-23 10:03:03 -07:00
|
|
|
#ifdef PR_LOGGING
|
2010-01-22 13:38:21 -08:00
|
|
|
nsAutoString policy;
|
2010-07-02 18:15:13 -07:00
|
|
|
csp->GetPolicy(policy);
|
|
|
|
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
|
|
|
("Document has CSP: %s",
|
|
|
|
NS_ConvertUTF16toUTF8(policy).get()));
|
2010-01-22 13:38:21 -08:00
|
|
|
#endif
|
|
|
|
// obtain the enforcement decision
|
|
|
|
csp->ShouldLoad(aContentType,
|
|
|
|
aContentLocation,
|
|
|
|
aRequestOrigin,
|
|
|
|
aRequestContext,
|
|
|
|
aMimeTypeGuess,
|
|
|
|
aExtra,
|
|
|
|
aDecision);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef PR_LOGGING
|
|
|
|
else {
|
|
|
|
nsCAutoString uriSpec;
|
|
|
|
aContentLocation->GetSpec(uriSpec);
|
2010-04-23 10:03:03 -07:00
|
|
|
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
2010-01-22 13:38:21 -08:00
|
|
|
("COULD NOT get nsINode for location: %s", uriSpec.get()));
|
|
|
|
}
|
|
|
|
#endif
|
2010-04-23 10:03:03 -07:00
|
|
|
|
2010-01-22 13:38:21 -08:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
CSPService::ShouldProcess(PRUint32 aContentType,
|
|
|
|
nsIURI *aContentLocation,
|
|
|
|
nsIURI *aRequestOrigin,
|
|
|
|
nsISupports *aRequestContext,
|
|
|
|
const nsACString &aMimeTypeGuess,
|
|
|
|
nsISupports *aExtra,
|
|
|
|
PRInt16 *aDecision)
|
|
|
|
{
|
|
|
|
if (!aContentLocation)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// default decision is to accept the item
|
|
|
|
*aDecision = nsIContentPolicy::ACCEPT;
|
|
|
|
|
|
|
|
// No need to continue processing if CSP is disabled
|
2010-07-12 20:12:32 -07:00
|
|
|
if (!sCSPEnabled)
|
2010-01-22 13:38:21 -08:00
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
// find the nsDocument that initiated this request and see if it has a
|
|
|
|
// CSP policy object
|
|
|
|
nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
|
|
|
|
nsCOMPtr<nsIPrincipal> principal;
|
|
|
|
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
|
|
|
if (node) {
|
|
|
|
principal = node->NodePrincipal();
|
|
|
|
principal->GetCsp(getter_AddRefs(csp));
|
|
|
|
|
|
|
|
if (csp) {
|
|
|
|
#ifdef PR_LOGGING
|
|
|
|
nsAutoString policy;
|
2010-07-02 18:15:13 -07:00
|
|
|
csp->GetPolicy(policy);
|
|
|
|
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
|
|
|
("shouldProcess - document has policy: %s",
|
|
|
|
NS_ConvertUTF16toUTF8(policy).get()));
|
2010-01-22 13:38:21 -08:00
|
|
|
#endif
|
|
|
|
// obtain the enforcement decision
|
|
|
|
csp->ShouldProcess(aContentType,
|
|
|
|
aContentLocation,
|
|
|
|
aRequestOrigin,
|
|
|
|
aRequestContext,
|
|
|
|
aMimeTypeGuess,
|
|
|
|
aExtra,
|
|
|
|
aDecision);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef PR_LOGGING
|
|
|
|
else {
|
|
|
|
nsCAutoString uriSpec;
|
|
|
|
aContentLocation->GetSpec(uriSpec);
|
2010-04-23 10:03:03 -07:00
|
|
|
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
2010-01-22 13:38:21 -08:00
|
|
|
("COULD NOT get nsINode for location: %s", uriSpec.get()));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2010-04-23 10:03:03 -07:00
|
|
|
|
|
|
|
/* nsIChannelEventSink implementation */
|
|
|
|
NS_IMETHODIMP
|
2010-08-04 19:15:55 -07:00
|
|
|
CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
|
|
|
|
nsIChannel *newChannel,
|
|
|
|
PRUint32 flags,
|
|
|
|
nsIAsyncVerifyRedirectCallback *callback)
|
2010-04-23 10:03:03 -07:00
|
|
|
{
|
2010-08-04 19:15:55 -07:00
|
|
|
nsAsyncRedirectAutoCallback autoCallback(callback);
|
|
|
|
|
2010-04-23 10:03:03 -07:00
|
|
|
// get the Content Security Policy and load type from the property bag
|
|
|
|
nsCOMPtr<nsISupports> policyContainer;
|
|
|
|
nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(oldChannel));
|
|
|
|
if (!props)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
props->GetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
|
|
|
|
NS_GET_IID(nsISupports),
|
|
|
|
getter_AddRefs(policyContainer));
|
|
|
|
|
|
|
|
// see if we have a valid nsIChannelPolicy containing CSP and load type
|
|
|
|
nsCOMPtr<nsIChannelPolicy> channelPolicy(do_QueryInterface(policyContainer));
|
|
|
|
if (!channelPolicy)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
|
|
|
channelPolicy->GetContentSecurityPolicy(getter_AddRefs(csp));
|
|
|
|
PRUint32 loadType;
|
|
|
|
channelPolicy->GetLoadType(&loadType);
|
|
|
|
|
|
|
|
// if no CSP in the channelPolicy, nothing for us to add to the channel
|
|
|
|
if (!csp)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
/* Since redirecting channels don't call into nsIContentPolicy, we call our
|
|
|
|
* Content Policy implementation directly when redirects occur. When channels
|
|
|
|
* are created using NS_NewChannel(), callers can optionally pass in a
|
|
|
|
* nsIChannelPolicy containing a CSP object and load type, which is placed in
|
|
|
|
* the new channel's property bag. This container is propagated forward when
|
|
|
|
* channels redirect.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Does the CSP permit this host for this type of load?
|
|
|
|
// If not, cancel the load now.
|
|
|
|
nsCOMPtr<nsIURI> newUri;
|
|
|
|
newChannel->GetURI(getter_AddRefs(newUri));
|
|
|
|
PRInt16 aDecision = nsIContentPolicy::ACCEPT;
|
|
|
|
csp->ShouldLoad(loadType, // load type per nsIContentPolicy (PRUint32)
|
|
|
|
newUri, // nsIURI
|
|
|
|
nsnull, // nsIURI
|
|
|
|
nsnull, // nsISupports
|
|
|
|
EmptyCString(), // ACString - MIME guess
|
|
|
|
nsnull, // nsISupports - extra
|
|
|
|
&aDecision);
|
|
|
|
|
|
|
|
#ifdef PR_LOGGING
|
|
|
|
if (newUri) {
|
|
|
|
nsCAutoString newUriSpec("None");
|
|
|
|
newUri->GetSpec(newUriSpec);
|
|
|
|
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
2010-08-04 19:15:55 -07:00
|
|
|
("CSPService::AsyncOnChannelRedirect called for %s",
|
|
|
|
newUriSpec.get()));
|
2010-04-23 10:03:03 -07:00
|
|
|
}
|
|
|
|
if (aDecision == 1)
|
|
|
|
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
2010-08-04 19:15:55 -07:00
|
|
|
("CSPService::AsyncOnChannelRedirect ALLOWING request."));
|
2010-04-23 10:03:03 -07:00
|
|
|
else
|
|
|
|
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
2010-08-04 19:15:55 -07:00
|
|
|
("CSPService::AsyncOnChannelRedirect CANCELLING request."));
|
2010-04-23 10:03:03 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
// if ShouldLoad doesn't accept the load, cancel the request
|
2010-08-04 19:15:55 -07:00
|
|
|
if (aDecision != 1) {
|
|
|
|
autoCallback.DontCallback();
|
2010-05-12 03:12:04 -07:00
|
|
|
return NS_BINDING_FAILED;
|
2010-08-04 19:15:55 -07:00
|
|
|
}
|
2010-04-23 10:03:03 -07:00
|
|
|
|
2010-05-12 03:12:04 -07:00
|
|
|
// the redirect is permitted, so propagate the Content Security Policy
|
|
|
|
// and load type to the redirecting channel
|
|
|
|
nsresult rv;
|
2011-12-05 09:42:08 -08:00
|
|
|
nsCOMPtr<nsIWritablePropertyBag2> props2 = do_QueryInterface(newChannel);
|
|
|
|
if (props2) {
|
|
|
|
rv = props2->SetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
|
|
|
|
channelPolicy);
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The redirecting channel isn't a writable property bag, we won't be able
|
|
|
|
// to enforce the load policy if it redirects again, so we stop it now.
|
|
|
|
nsCAutoString newUriSpec;
|
|
|
|
newUri->GetSpec(newUriSpec);
|
|
|
|
const PRUnichar *formatParams[] = { NS_ConvertUTF8toUTF16(newUriSpec).get() };
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
2011-12-15 06:47:03 -08:00
|
|
|
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
|
|
|
|
"Redirect Error", nsnull,
|
|
|
|
nsContentUtils::eDOM_PROPERTIES,
|
|
|
|
"InvalidRedirectChannelWarning",
|
|
|
|
formatParams, 1);
|
2011-12-05 09:42:08 -08:00
|
|
|
}
|
2010-04-23 10:03:03 -07:00
|
|
|
|
2011-12-05 09:42:08 -08:00
|
|
|
return NS_BINDING_FAILED;
|
2010-04-23 10:03:03 -07:00
|
|
|
}
|