diff --git a/content/base/src/nsCSPService.cpp b/content/base/src/nsCSPService.cpp index 3b99c00c627c..61e62c5affef 100644 --- a/content/base/src/nsCSPService.cpp +++ b/content/base/src/nsCSPService.cpp @@ -47,6 +47,12 @@ #include "nsContentUtils.h" #include "nsCSPService.h" #include "nsIContentSecurityPolicy.h" +#include "nsIChannelPolicy.h" +#include "nsIChannelEventSink.h" +#include "nsIPropertyBag2.h" +#include "nsIWritablePropertyBag2.h" +#include "nsNetError.h" +#include "nsChannelProperties.h" /* Keeps track of whether or not CSP is enabled */ static PRBool gCSPEnabled = PR_TRUE; @@ -69,7 +75,7 @@ CSPService::~CSPService() { } -NS_IMPL_ISUPPORTS1(CSPService, nsIContentPolicy) +NS_IMPL_ISUPPORTS2(CSPService, nsIContentPolicy, nsIChannelEventSink) /* nsIContentPolicy implementation */ NS_IMETHODIMP @@ -84,11 +90,11 @@ CSPService::ShouldLoad(PRUint32 aContentType, if (!aContentLocation) return NS_ERROR_FAILURE; -#ifdef PR_LOGGING +#ifdef PR_LOGGING { nsCAutoString location; aContentLocation->GetSpec(location); - PR_LOG(gCspPRLog, PR_LOG_DEBUG, + PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSPService::ShouldLoad called for %s", location.get())); } #endif @@ -99,8 +105,8 @@ CSPService::ShouldLoad(PRUint32 aContentType, if (!gCSPEnabled) return NS_OK; - // find the nsDocument that initiated this request and see if it has a - // CSP policy object + // find the principal of the document that initiated this request and see + // if it has a CSP policy object nsCOMPtr node(do_QueryInterface(aRequestContext)); nsCOMPtr principal; nsCOMPtr csp; @@ -109,11 +115,11 @@ CSPService::ShouldLoad(PRUint32 aContentType, principal->GetCsp(getter_AddRefs(csp)); if (csp) { -#ifdef PR_LOGGING +#ifdef PR_LOGGING nsAutoString policy; csp->GetPolicy(policy); - PR_LOG(gCspPRLog, PR_LOG_DEBUG, - ("Document has CSP: %s", + PR_LOG(gCspPRLog, PR_LOG_DEBUG, + ("Document has CSP: %s", NS_ConvertUTF16toUTF8(policy).get())); #endif // obtain the enforcement decision @@ -130,11 +136,11 @@ CSPService::ShouldLoad(PRUint32 aContentType, else { nsCAutoString uriSpec; aContentLocation->GetSpec(uriSpec); - PR_LOG(gCspPRLog, PR_LOG_DEBUG, + PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("COULD NOT get nsINode for location: %s", uriSpec.get())); } #endif - + return NS_OK; } @@ -170,7 +176,7 @@ CSPService::ShouldProcess(PRUint32 aContentType, #ifdef PR_LOGGING nsAutoString policy; csp->GetPolicy(policy); - PR_LOG(gCspPRLog, PR_LOG_DEBUG, + PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("shouldProcess - document has policy: %s", NS_ConvertUTF16toUTF8(policy).get())); #endif @@ -188,9 +194,93 @@ CSPService::ShouldProcess(PRUint32 aContentType, else { nsCAutoString uriSpec; aContentLocation->GetSpec(uriSpec); - PR_LOG(gCspPRLog, PR_LOG_DEBUG, + PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("COULD NOT get nsINode for location: %s", uriSpec.get())); } #endif return NS_OK; } + +/* nsIChannelEventSink implementation */ +NS_IMETHODIMP +CSPService::OnChannelRedirect(nsIChannel *oldChannel, + nsIChannel *newChannel, + PRUint32 flags) +{ + // get the Content Security Policy and load type from the property bag + nsCOMPtr policyContainer; + nsCOMPtr 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 channelPolicy(do_QueryInterface(policyContainer)); + if (!channelPolicy) + return NS_OK; + + nsCOMPtr 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 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, + ("CSPService::OnChannelRedirect called for %s", newUriSpec.get())); + } + if (aDecision == 1) + PR_LOG(gCspPRLog, PR_LOG_DEBUG, + ("CSPService::OnChannelRedirect ALLOWING request.")); + else + PR_LOG(gCspPRLog, PR_LOG_DEBUG, + ("CSPService::OnChannelRedirect CANCELLING request.")); +#endif + + // if ShouldLoad doesn't accept the load, cancel the request + if (aDecision != 1) { + newChannel->Cancel(NS_BINDING_FAILED); + } + + else { + // the redirect is permitted, so propagate the Content Security Policy + // and load type to the redirecting channel + nsresult rv; + nsCOMPtr props = do_QueryInterface(newChannel, &rv); + if (props) + props->SetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY, + channelPolicy); + } + + return NS_OK; +} diff --git a/content/base/src/nsCSPService.h b/content/base/src/nsCSPService.h index a1e056164cd1..b256574965f4 100644 --- a/content/base/src/nsCSPService.h +++ b/content/base/src/nsCSPService.h @@ -38,16 +38,20 @@ #include "nsXPCOM.h" #include "nsIContentPolicy.h" +#include "nsIChannel.h" +#include "nsIChannelEventSink.h" #define CSPSERVICE_CONTRACTID "@mozilla.org/cspservice;1" #define CSPSERVICE_CID \ { 0x8d2f40b2, 0x4875, 0x4c95, \ { 0x97, 0xd9, 0x3f, 0x7d, 0xca, 0x2c, 0xb4, 0x60 } } -class CSPService : public nsIContentPolicy +class CSPService : public nsIContentPolicy, + public nsIChannelEventSink { public: NS_DECL_ISUPPORTS NS_DECL_NSICONTENTPOLICY + NS_DECL_NSICHANNELEVENTSINK CSPService(); virtual ~CSPService(); diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index acfaf77e18c8..82ff438a8009 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -875,6 +875,16 @@ CSPServiceRegistration(nsIComponentManager *aCompMgr, PR_TRUE, PR_TRUE, getter_Copies(previous)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = catman->AddCategoryEntry("net-channel-event-sinks", + "CSPService", + CSPSERVICE_CONTRACTID, + PR_TRUE, + PR_TRUE, + getter_Copies(previous)); + NS_ENSURE_SUCCESS(rv, rv); + return rv; } @@ -898,6 +908,10 @@ CSPServiceUnregistration(nsIComponentManager *aCompMgr, "CSPService", PR_TRUE); + rv = catman->DeleteCategoryEntry("net-channel-event-sinks", + "CSPService", + PR_TRUE); + return rv; }