Bug 523239 - CSP nsIChannelEventSink impl, r=jst, a=dholbert_sheriff
authorBrandon Sterne <bsterne@mozilla.com>
Fri, 23 Apr 2010 10:03:03 -0700
changeset 41205 a96445f7bcc3fed66f01e4a0a918ac3ba839d841
parent 41204 27d44d82781f8e5151d7314b1971f842f338f6b1
child 41206 179f87824c8824ed6583f8e35fdec388b816a2f1
push idunknown
push userunknown
push dateunknown
reviewersjst, dholbert_sheriff
bugs523239
milestone1.9.3a5pre
Bug 523239 - CSP nsIChannelEventSink impl, r=jst, a=dholbert_sheriff
content/base/src/nsCSPService.cpp
content/base/src/nsCSPService.h
layout/build/nsLayoutModule.cpp
--- a/content/base/src/nsCSPService.cpp
+++ b/content/base/src/nsCSPService.cpp
@@ -42,16 +42,22 @@
 #include "nsIURI.h"
 #include "nsIPrincipal.h"
 #include "nsIObserver.h"
 #include "nsIDocument.h"
 #include "nsIContent.h"
 #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;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gCspPRLog;
 #endif
 
@@ -64,82 +70,82 @@ CSPService::CSPService()
     gCspPRLog = PR_NewLogModule("CSP");
 #endif
 }
 
 CSPService::~CSPService()
 {
 }
 
-NS_IMPL_ISUPPORTS1(CSPService, nsIContentPolicy)
+NS_IMPL_ISUPPORTS2(CSPService, nsIContentPolicy, nsIChannelEventSink)
 
 /* 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;
 
-#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
     // 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
     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<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 
+#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
             csp->ShouldLoad(aContentType,
                             aContentLocation,
                             aRequestOrigin,
                             aRequestContext,
                             aMimeTypeGuess,
                             aExtra,
                             aDecision);
         }
     }
 #ifdef PR_LOGGING
     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;
 }
 
 NS_IMETHODIMP
 CSPService::ShouldProcess(PRUint32         aContentType,
                           nsIURI           *aContentLocation,
                           nsIURI           *aRequestOrigin,
                           nsISupports      *aRequestContext,
@@ -165,17 +171,17 @@ CSPService::ShouldProcess(PRUint32      
     if (node) {
         principal = node->NodePrincipal();
         principal->GetCsp(getter_AddRefs(csp));
 
         if (csp) {
 #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
             // obtain the enforcement decision
             csp->ShouldProcess(aContentType,
                                aContentLocation,
                                aRequestOrigin,
                                aRequestContext,
@@ -183,14 +189,98 @@ CSPService::ShouldProcess(PRUint32      
                                aExtra,
                                aDecision);
         }
     }
 #ifdef PR_LOGGING
     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<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,
+           ("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<nsIWritablePropertyBag2> props = do_QueryInterface(newChannel, &rv);
+    if (props)
+      props->SetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
+                                    channelPolicy);
+  }
+
+  return NS_OK;
+}
--- a/content/base/src/nsCSPService.h
+++ b/content/base/src/nsCSPService.h
@@ -33,25 +33,29 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #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();
 
 private:
   PRBool mEnabled;
 };
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -870,16 +870,26 @@ CSPServiceRegistration(nsIComponentManag
   
   nsXPIDLCString previous;
   rv = catman->AddCategoryEntry("content-policy",
                                 "CSPService",
                                 CSPSERVICE_CONTRACTID,
                                 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;
 }
 
 static NS_METHOD
 CSPServiceUnregistration(nsIComponentManager *aCompMgr,
                          nsIFile *aPath,
                          const char *registryLocation,
                          const nsModuleComponentInfo *info){
@@ -893,16 +903,20 @@ CSPServiceUnregistration(nsIComponentMan
                                        NS_GET_IID(nsICategoryManager),
                                        getter_AddRefs(catman));
   if (NS_FAILED(rv)) return rv;
 
   rv = catman->DeleteCategoryEntry("content-policy",
                                    "CSPService",
                                    PR_TRUE);
 
+  rv = catman->DeleteCategoryEntry("net-channel-event-sinks",
+                                   "CSPService",
+                                   PR_TRUE);
+
   return rv;
 }
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(CSPService)
 
 // The list of components we register
 static const nsModuleComponentInfo gComponents[] = {
 #ifdef DEBUG