content/base/src/nsCSPService.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "prlog.h"
     7 #include "nsString.h"
     8 #include "nsCOMPtr.h"
     9 #include "nsIURI.h"
    10 #include "nsIPrincipal.h"
    11 #include "nsIObserver.h"
    12 #include "nsIContent.h"
    13 #include "nsCSPService.h"
    14 #include "nsIContentSecurityPolicy.h"
    15 #include "nsIChannelPolicy.h"
    16 #include "nsIChannelEventSink.h"
    17 #include "nsIPropertyBag2.h"
    18 #include "nsIWritablePropertyBag2.h"
    19 #include "nsError.h"
    20 #include "nsChannelProperties.h"
    21 #include "nsIAsyncVerifyRedirectCallback.h"
    22 #include "nsAsyncRedirectVerifyHelper.h"
    23 #include "mozilla/Preferences.h"
    24 #include "nsIScriptError.h"
    25 #include "nsContentUtils.h"
    27 using namespace mozilla;
    29 /* Keeps track of whether or not CSP is enabled */
    30 bool CSPService::sCSPEnabled = true;
    32 #ifdef PR_LOGGING
    33 static PRLogModuleInfo* gCspPRLog;
    34 #endif
    36 CSPService::CSPService()
    37 {
    38   Preferences::AddBoolVarCache(&sCSPEnabled, "security.csp.enable");
    40 #ifdef PR_LOGGING
    41   if (!gCspPRLog)
    42     gCspPRLog = PR_NewLogModule("CSP");
    43 #endif
    44 }
    46 CSPService::~CSPService()
    47 {
    48   mAppStatusCache.Clear();
    49 }
    51 NS_IMPL_ISUPPORTS(CSPService, nsIContentPolicy, nsIChannelEventSink)
    53 /* nsIContentPolicy implementation */
    54 NS_IMETHODIMP
    55 CSPService::ShouldLoad(uint32_t aContentType,
    56                        nsIURI *aContentLocation,
    57                        nsIURI *aRequestOrigin,
    58                        nsISupports *aRequestContext,
    59                        const nsACString &aMimeTypeGuess,
    60                        nsISupports *aExtra,
    61                        nsIPrincipal *aRequestPrincipal,
    62                        int16_t *aDecision)
    63 {
    64   if (!aContentLocation)
    65     return NS_ERROR_FAILURE;
    67 #ifdef PR_LOGGING
    68   {
    69     nsAutoCString location;
    70     aContentLocation->GetSpec(location);
    71     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
    72            ("CSPService::ShouldLoad called for %s", location.get()));
    73   }
    74 #endif
    76   // default decision, CSP can revise it if there's a policy to enforce
    77   *aDecision = nsIContentPolicy::ACCEPT;
    79   // No need to continue processing if CSP is disabled
    80   if (!sCSPEnabled)
    81     return NS_OK;
    83   // shortcut for about: chrome: and resource: and javascript: uris since
    84   // they're not subject to CSP content policy checks.
    85   bool schemeMatch = false;
    86   NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("about", &schemeMatch), NS_OK);
    87   if (schemeMatch)
    88     return NS_OK;
    89   NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("chrome", &schemeMatch), NS_OK);
    90   if (schemeMatch)
    91     return NS_OK;
    92   NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("resource", &schemeMatch), NS_OK);
    93   if (schemeMatch)
    94     return NS_OK;
    95   NS_ENSURE_SUCCESS(aContentLocation->SchemeIs("javascript", &schemeMatch), NS_OK);
    96   if (schemeMatch)
    97     return NS_OK;
   100   // These content types are not subject to CSP content policy checks:
   101   // TYPE_CSP_REPORT, TYPE_REFRESH, TYPE_DOCUMENT
   102   // (their mappings are null in contentSecurityPolicy.js)
   103   if (aContentType == nsIContentPolicy::TYPE_CSP_REPORT ||
   104     aContentType == nsIContentPolicy::TYPE_REFRESH ||
   105     aContentType == nsIContentPolicy::TYPE_DOCUMENT) {
   106     return NS_OK;
   107   }
   109   // ----- THIS IS A TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
   110   // ----- PLEASE REMOVE ONCE bug 925004 LANDS.              -----
   112   // Cache the app status for this origin.
   113   uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
   114   nsAutoCString contentOrigin;
   115   aContentLocation->GetPrePath(contentOrigin);
   116   if (aRequestPrincipal && !mAppStatusCache.Get(contentOrigin, &status)) {
   117     aRequestPrincipal->GetAppStatus(&status);
   118     mAppStatusCache.Put(contentOrigin, status);
   119   }
   121   if (status == nsIPrincipal::APP_STATUS_CERTIFIED) {
   122     // The CSP for certified apps is :
   123     // "default-src *; script-src 'self'; object-src 'none'; style-src 'self'"
   124     // That means we can optimize for this case by:
   125     // - loading only same origin scripts and stylesheets.
   126     // - never loading objects.
   127     // - accepting everything else.
   129     switch (aContentType) {
   130       case nsIContentPolicy::TYPE_SCRIPT:
   131       case nsIContentPolicy::TYPE_STYLESHEET:
   132         {
   133           nsAutoCString sourceOrigin;
   134           aRequestOrigin->GetPrePath(sourceOrigin);
   135           if (!sourceOrigin.Equals(contentOrigin)) {
   136             *aDecision = nsIContentPolicy::REJECT_SERVER;
   137           }
   138         }
   139         break;
   141       case nsIContentPolicy::TYPE_OBJECT:
   142         *aDecision = nsIContentPolicy::REJECT_SERVER;
   143         break;
   145       default:
   146         *aDecision = nsIContentPolicy::ACCEPT;
   147     }
   149     // Only cache and return if we are successful. If not, we want the error
   150     // to be reported, and thus fallback to the slow path.
   151     if (*aDecision == nsIContentPolicy::ACCEPT) {
   152       return NS_OK;
   153     }
   154   }
   156   // ----- END OF TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
   158   // find the principal of the document that initiated this request and see
   159   // if it has a CSP policy object
   160   nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
   161   nsCOMPtr<nsIPrincipal> principal;
   162   nsCOMPtr<nsIContentSecurityPolicy> csp;
   163   if (node) {
   164     principal = node->NodePrincipal();
   165     principal->GetCsp(getter_AddRefs(csp));
   167     if (csp) {
   168 #ifdef PR_LOGGING
   169       {
   170         int numPolicies = 0;
   171         nsresult rv = csp->GetPolicyCount(&numPolicies);
   172         if (NS_SUCCEEDED(rv)) {
   173           for (int i=0; i<numPolicies; i++) {
   174             nsAutoString policy;
   175             csp->GetPolicy(i, policy);
   176             PR_LOG(gCspPRLog, PR_LOG_DEBUG,
   177                    ("Document has CSP[%d]: %s", i,
   178                    NS_ConvertUTF16toUTF8(policy).get()));
   179           }
   180         }
   181       }
   182 #endif
   183       // obtain the enforcement decision
   184       // (don't pass aExtra, we use that slot for redirects)
   185       csp->ShouldLoad(aContentType,
   186                       aContentLocation,
   187                       aRequestOrigin,
   188                       aRequestContext,
   189                       aMimeTypeGuess,
   190                       nullptr,
   191                       aDecision);
   192     }
   193   }
   194 #ifdef PR_LOGGING
   195   else {
   196     nsAutoCString uriSpec;
   197     aContentLocation->GetSpec(uriSpec);
   198     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
   199            ("COULD NOT get nsINode for location: %s", uriSpec.get()));
   200   }
   201 #endif
   203   return NS_OK;
   204 }
   206 NS_IMETHODIMP
   207 CSPService::ShouldProcess(uint32_t         aContentType,
   208                           nsIURI           *aContentLocation,
   209                           nsIURI           *aRequestOrigin,
   210                           nsISupports      *aRequestContext,
   211                           const nsACString &aMimeTypeGuess,
   212                           nsISupports      *aExtra,
   213                           nsIPrincipal     *aRequestPrincipal,
   214                           int16_t          *aDecision)
   215 {
   216   if (!aContentLocation)
   217     return NS_ERROR_FAILURE;
   219   // default decision is to accept the item
   220   *aDecision = nsIContentPolicy::ACCEPT;
   222   // No need to continue processing if CSP is disabled
   223   if (!sCSPEnabled)
   224     return NS_OK;
   226   // find the nsDocument that initiated this request and see if it has a
   227   // CSP policy object
   228   nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
   229   nsCOMPtr<nsIPrincipal> principal;
   230   nsCOMPtr<nsIContentSecurityPolicy> csp;
   231   if (node) {
   232     principal = node->NodePrincipal();
   233     principal->GetCsp(getter_AddRefs(csp));
   235     if (csp) {
   236 #ifdef PR_LOGGING
   237       {
   238         int numPolicies = 0;
   239         nsresult rv = csp->GetPolicyCount(&numPolicies);
   240         if (NS_SUCCEEDED(rv)) {
   241           for (int i=0; i<numPolicies; i++) {
   242             nsAutoString policy;
   243             csp->GetPolicy(i, policy);
   244             PR_LOG(gCspPRLog, PR_LOG_DEBUG,
   245                    ("shouldProcess - document has policy[%d]: %s", i,
   246                    NS_ConvertUTF16toUTF8(policy).get()));
   247           }
   248         }
   249       }
   250 #endif
   251       // obtain the enforcement decision
   252       csp->ShouldProcess(aContentType,
   253                          aContentLocation,
   254                          aRequestOrigin,
   255                          aRequestContext,
   256                          aMimeTypeGuess,
   257                          aExtra,
   258                          aDecision);
   259     }
   260   }
   261 #ifdef PR_LOGGING
   262   else {
   263     nsAutoCString uriSpec;
   264     aContentLocation->GetSpec(uriSpec);
   265     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
   266            ("COULD NOT get nsINode for location: %s", uriSpec.get()));
   267   }
   268 #endif
   269   return NS_OK;
   270 }
   272 /* nsIChannelEventSink implementation */
   273 NS_IMETHODIMP
   274 CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
   275                                    nsIChannel *newChannel,
   276                                    uint32_t flags,
   277                                    nsIAsyncVerifyRedirectCallback *callback)
   278 {
   279   nsAsyncRedirectAutoCallback autoCallback(callback);
   281   // get the Content Security Policy and load type from the property bag
   282   nsCOMPtr<nsISupports> policyContainer;
   283   nsCOMPtr<nsIPropertyBag2> props(do_QueryInterface(oldChannel));
   284   if (!props)
   285     return NS_OK;
   287   props->GetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
   288                                 NS_GET_IID(nsISupports),
   289                                 getter_AddRefs(policyContainer));
   291   // see if we have a valid nsIChannelPolicy containing CSP and load type
   292   nsCOMPtr<nsIChannelPolicy> channelPolicy(do_QueryInterface(policyContainer));
   293   if (!channelPolicy)
   294     return NS_OK;
   296   nsCOMPtr<nsISupports> supports;
   297   nsCOMPtr<nsIContentSecurityPolicy> csp;
   298   channelPolicy->GetContentSecurityPolicy(getter_AddRefs(supports));
   299   csp = do_QueryInterface(supports);
   300   uint32_t loadType;
   301   channelPolicy->GetLoadType(&loadType);
   303   // if no CSP in the channelPolicy, nothing for us to add to the channel
   304   if (!csp)
   305     return NS_OK;
   307   /* Since redirecting channels don't call into nsIContentPolicy, we call our
   308    * Content Policy implementation directly when redirects occur. When channels
   309    * are created using NS_NewChannel(), callers can optionally pass in a
   310    * nsIChannelPolicy containing a CSP object and load type, which is placed in
   311    * the new channel's property bag. This container is propagated forward when
   312    * channels redirect.
   313    */
   315   // Does the CSP permit this host for this type of load?
   316   // If not, cancel the load now.
   317   nsCOMPtr<nsIURI> newUri;
   318   newChannel->GetURI(getter_AddRefs(newUri));
   319   nsCOMPtr<nsIURI> originalUri;
   320   oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
   321   int16_t aDecision = nsIContentPolicy::ACCEPT;
   322   csp->ShouldLoad(loadType,        // load type per nsIContentPolicy (uint32_t)
   323                   newUri,          // nsIURI
   324                   nullptr,          // nsIURI
   325                   nullptr,          // nsISupports
   326                   EmptyCString(),  // ACString - MIME guess
   327                   originalUri,     // nsISupports - extra
   328                   &aDecision);
   330 #ifdef PR_LOGGING
   331   if (newUri) {
   332     nsAutoCString newUriSpec("None");
   333     newUri->GetSpec(newUriSpec);
   334     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
   335            ("CSPService::AsyncOnChannelRedirect called for %s",
   336             newUriSpec.get()));
   337   }
   338   if (aDecision == 1)
   339     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
   340            ("CSPService::AsyncOnChannelRedirect ALLOWING request."));
   341   else
   342     PR_LOG(gCspPRLog, PR_LOG_DEBUG,
   343            ("CSPService::AsyncOnChannelRedirect CANCELLING request."));
   344 #endif
   346   // if ShouldLoad doesn't accept the load, cancel the request
   347   if (aDecision != 1) {
   348     autoCallback.DontCallback();
   349     return NS_BINDING_FAILED;
   350   }
   352   // the redirect is permitted, so propagate the Content Security Policy
   353   // and load type to the redirecting channel
   354   nsresult rv;
   355   nsCOMPtr<nsIWritablePropertyBag2> props2 = do_QueryInterface(newChannel);
   356   if (props2) {
   357     rv = props2->SetPropertyAsInterface(NS_CHANNEL_PROP_CHANNEL_POLICY,
   358                                         channelPolicy);
   359     if (NS_SUCCEEDED(rv)) {
   360       return NS_OK;
   361     }
   362   }
   364   // The redirecting channel isn't a writable property bag, we won't be able
   365   // to enforce the load policy if it redirects again, so we stop it now.
   366   nsAutoCString newUriSpec;
   367   rv = newUri->GetSpec(newUriSpec);
   368   const char16_t *formatParams[] = { NS_ConvertUTF8toUTF16(newUriSpec).get() };
   369   if (NS_SUCCEEDED(rv)) {
   370     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
   371                                     NS_LITERAL_CSTRING("Redirect Error"), nullptr,
   372                                     nsContentUtils::eDOM_PROPERTIES,
   373                                     "InvalidRedirectChannelWarning",
   374                                     formatParams, 1);
   375   }
   377   return NS_BINDING_FAILED;
   378 }

mercurial