LCOV - code coverage report
Current view: top level - dom/security - nsCSPService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 45 116 38.8 %
Date: 2017-07-14 16:53:18 Functions: 6 10 60.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "mozilla/Logging.h"
       8             : #include "nsString.h"
       9             : #include "nsCOMPtr.h"
      10             : #include "nsIURI.h"
      11             : #include "nsIPrincipal.h"
      12             : #include "nsIObserver.h"
      13             : #include "nsIContent.h"
      14             : #include "nsCSPService.h"
      15             : #include "nsIContentSecurityPolicy.h"
      16             : #include "nsError.h"
      17             : #include "nsIAsyncVerifyRedirectCallback.h"
      18             : #include "nsAsyncRedirectVerifyHelper.h"
      19             : #include "mozilla/Preferences.h"
      20             : #include "nsIScriptError.h"
      21             : #include "nsContentUtils.h"
      22             : #include "nsContentPolicyUtils.h"
      23             : 
      24             : using namespace mozilla;
      25             : 
      26             : /* Keeps track of whether or not CSP is enabled */
      27             : bool CSPService::sCSPEnabled = true;
      28             : 
      29             : static LazyLogModule gCspPRLog("CSP");
      30             : 
      31           3 : CSPService::CSPService()
      32             : {
      33           3 :   Preferences::AddBoolVarCache(&sCSPEnabled, "security.csp.enable");
      34           3 : }
      35             : 
      36           0 : CSPService::~CSPService()
      37             : {
      38           0 :   mAppStatusCache.Clear();
      39           0 : }
      40             : 
      41          36 : NS_IMPL_ISUPPORTS(CSPService, nsIContentPolicy, nsIChannelEventSink)
      42             : 
      43             : // Helper function to identify protocols and content types not subject to CSP.
      44             : bool
      45          23 : subjectToCSP(nsIURI* aURI, nsContentPolicyType aContentType) {
      46             :   // These content types are not subject to CSP content policy checks:
      47             :   // TYPE_CSP_REPORT -- csp can't block csp reports
      48             :   // TYPE_REFRESH    -- never passed to ShouldLoad (see nsIContentPolicy.idl)
      49             :   // TYPE_DOCUMENT   -- used for frame-ancestors
      50          23 :   if (aContentType == nsIContentPolicy::TYPE_CSP_REPORT ||
      51          23 :       aContentType == nsIContentPolicy::TYPE_REFRESH ||
      52             :       aContentType == nsIContentPolicy::TYPE_DOCUMENT) {
      53          10 :     return false;
      54             :   }
      55             : 
      56             :   // The three protocols: data:, blob: and filesystem: share the same
      57             :   // protocol flag (URI_IS_LOCAL_RESOURCE) with other protocols, like
      58             :   // chrome:, resource:, moz-icon:, but those three protocols get
      59             :   // special attention in CSP and are subject to CSP, hence we have
      60             :   // to make sure those protocols are subject to CSP, see:
      61             :   // http://www.w3.org/TR/CSP2/#source-list-guid-matching
      62          13 :   bool match = false;
      63          13 :   nsresult rv = aURI->SchemeIs("data", &match);
      64          13 :   if (NS_SUCCEEDED(rv) && match) {
      65           0 :     return true;
      66             :   }
      67          13 :   rv = aURI->SchemeIs("blob", &match);
      68          13 :   if (NS_SUCCEEDED(rv) && match) {
      69           0 :     return true;
      70             :   }
      71          13 :   rv = aURI->SchemeIs("filesystem", &match);
      72          13 :   if (NS_SUCCEEDED(rv) && match) {
      73           0 :     return true;
      74             :   }
      75             : 
      76             :   // Finally we have to whitelist "about:" which does not fall into
      77             :   // the category underneath and also "javascript:" which is not
      78             :   // subject to CSP content loading rules.
      79          13 :   rv = aURI->SchemeIs("about", &match);
      80          13 :   if (NS_SUCCEEDED(rv) && match) {
      81           0 :     return false;
      82             :   }
      83          13 :   rv = aURI->SchemeIs("javascript", &match);
      84          13 :   if (NS_SUCCEEDED(rv) && match) {
      85           0 :     return false;
      86             :   }
      87             : 
      88             :   // Other protocols are not subject to CSP and can be whitelisted:
      89             :   // * URI_IS_LOCAL_RESOURCE
      90             :   //   e.g. chrome:, data:, blob:, resource:, moz-icon:
      91             :   // Please note that it should be possible for websites to
      92             :   // whitelist their own protocol handlers with respect to CSP,
      93             :   // hence we use protocol flags to accomplish that.
      94          13 :   rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, &match);
      95          13 :   if (NS_SUCCEEDED(rv) && match) {
      96           8 :     return false;
      97             :   }
      98             :   // all other protocols are subject To CSP.
      99           5 :   return true;
     100             : }
     101             : 
     102             : /* nsIContentPolicy implementation */
     103             : NS_IMETHODIMP
     104          23 : CSPService::ShouldLoad(uint32_t aContentType,
     105             :                        nsIURI *aContentLocation,
     106             :                        nsIURI *aRequestOrigin,
     107             :                        nsISupports *aRequestContext,
     108             :                        const nsACString &aMimeTypeGuess,
     109             :                        nsISupports *aExtra,
     110             :                        nsIPrincipal *aRequestPrincipal,
     111             :                        int16_t *aDecision)
     112             : {
     113          23 :   if (!aContentLocation) {
     114           0 :     return NS_ERROR_FAILURE;
     115             :   }
     116             : 
     117          23 :   if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
     118           0 :     MOZ_LOG(gCspPRLog, LogLevel::Debug,
     119             :            ("CSPService::ShouldLoad called for %s",
     120             :            aContentLocation->GetSpecOrDefault().get()));
     121             :   }
     122             : 
     123             :   // default decision, CSP can revise it if there's a policy to enforce
     124          23 :   *aDecision = nsIContentPolicy::ACCEPT;
     125             : 
     126             :   // No need to continue processing if CSP is disabled or if the protocol
     127             :   // or type is *not* subject to CSP.
     128             :   // Please note, the correct way to opt-out of CSP using a custom
     129             :   // protocolHandler is to set one of the nsIProtocolHandler flags
     130             :   // that are whitelistet in subjectToCSP()
     131          23 :   if (!sCSPEnabled || !subjectToCSP(aContentLocation, aContentType)) {
     132          18 :     return NS_OK;
     133             :   }
     134             : 
     135             :   // query the principal of the document; if no document is passed, then
     136             :   // fall back to using the requestPrincipal (e.g. service workers do not
     137             :   // pass a document).
     138          10 :   nsCOMPtr<nsINode> node(do_QueryInterface(aRequestContext));
     139           4 :   nsCOMPtr<nsIPrincipal> principal = node ? node->NodePrincipal()
     140          14 :                                           : aRequestPrincipal;
     141           5 :   if (!principal) {
     142             :     // if we can't query a principal, then there is nothing to do.
     143           0 :     return NS_OK;
     144             :   }
     145           5 :   nsresult rv = NS_OK;
     146             : 
     147             :   // 1) Apply speculate CSP for preloads
     148           5 :   bool isPreload = nsContentUtils::IsPreloadType(aContentType);
     149             : 
     150           5 :   if (isPreload) {
     151           4 :     nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
     152           2 :     rv = principal->GetPreloadCsp(getter_AddRefs(preloadCsp));
     153           2 :     NS_ENSURE_SUCCESS(rv, rv);
     154             : 
     155           2 :     if (preloadCsp) {
     156             :       // obtain the enforcement decision
     157             :       // (don't pass aExtra, we use that slot for redirects)
     158           0 :       rv = preloadCsp->ShouldLoad(aContentType,
     159             :                                   aContentLocation,
     160             :                                   aRequestOrigin,
     161             :                                   aRequestContext,
     162             :                                   aMimeTypeGuess,
     163             :                                   nullptr, // aExtra
     164           0 :                                   aDecision);
     165           0 :       NS_ENSURE_SUCCESS(rv, rv);
     166             : 
     167             :       // if the preload policy already denied the load, then there
     168             :       // is no point in checking the real policy
     169           0 :       if (NS_CP_REJECTED(*aDecision)) {
     170           0 :         return NS_OK;
     171             :       }
     172             :     }
     173             :   }
     174             : 
     175             :   // 2) Apply actual CSP to all loads
     176          10 :   nsCOMPtr<nsIContentSecurityPolicy> csp;
     177           5 :   rv = principal->GetCsp(getter_AddRefs(csp));
     178           5 :   NS_ENSURE_SUCCESS(rv, rv);
     179             : 
     180           5 :   if (csp) {
     181             :     // obtain the enforcement decision
     182             :     // (don't pass aExtra, we use that slot for redirects)
     183           0 :     rv = csp->ShouldLoad(aContentType,
     184             :                          aContentLocation,
     185             :                          aRequestOrigin,
     186             :                          aRequestContext,
     187             :                          aMimeTypeGuess,
     188             :                          nullptr,
     189           0 :                          aDecision);
     190           0 :     NS_ENSURE_SUCCESS(rv, rv);
     191             :   }
     192           5 :   return NS_OK;
     193             : }
     194             : 
     195             : NS_IMETHODIMP
     196           0 : CSPService::ShouldProcess(uint32_t         aContentType,
     197             :                           nsIURI           *aContentLocation,
     198             :                           nsIURI           *aRequestOrigin,
     199             :                           nsISupports      *aRequestContext,
     200             :                           const nsACString &aMimeTypeGuess,
     201             :                           nsISupports      *aExtra,
     202             :                           nsIPrincipal     *aRequestPrincipal,
     203             :                           int16_t          *aDecision)
     204             : {
     205           0 :   if (!aContentLocation) {
     206           0 :     return NS_ERROR_FAILURE;
     207             :   }
     208             : 
     209           0 :   if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
     210           0 :     MOZ_LOG(gCspPRLog, LogLevel::Debug,
     211             :             ("CSPService::ShouldProcess called for %s",
     212             :             aContentLocation->GetSpecOrDefault().get()));
     213             :   }
     214             : 
     215             :   // ShouldProcess is only relevant to TYPE_OBJECT, so let's convert the
     216             :   // internal contentPolicyType to the mapping external one.
     217             :   // If it is not TYPE_OBJECT, we can return at this point.
     218             :   // Note that we should still pass the internal contentPolicyType
     219             :   // (aContentType) to ShouldLoad().
     220             :   uint32_t policyType =
     221           0 :     nsContentUtils::InternalContentPolicyTypeToExternal(aContentType);
     222             : 
     223           0 :   if (policyType != nsIContentPolicy::TYPE_OBJECT) {
     224           0 :     *aDecision = nsIContentPolicy::ACCEPT;
     225           0 :     return NS_OK;
     226             :   }
     227             : 
     228             :   return ShouldLoad(aContentType,
     229             :                     aContentLocation,
     230             :                     aRequestOrigin,
     231             :                     aRequestContext,
     232             :                     aMimeTypeGuess,
     233             :                     aExtra,
     234             :                     aRequestPrincipal,
     235           0 :                     aDecision);
     236             : }
     237             : 
     238             : /* nsIChannelEventSink implementation */
     239             : NS_IMETHODIMP
     240           0 : CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
     241             :                                    nsIChannel *newChannel,
     242             :                                    uint32_t flags,
     243             :                                    nsIAsyncVerifyRedirectCallback *callback)
     244             : {
     245           0 :   net::nsAsyncRedirectAutoCallback autoCallback(callback);
     246             : 
     247           0 :   nsCOMPtr<nsIURI> newUri;
     248           0 :   nsresult rv = newChannel->GetURI(getter_AddRefs(newUri));
     249           0 :   NS_ENSURE_SUCCESS(rv, rv);
     250             : 
     251           0 :   nsCOMPtr<nsILoadInfo> loadInfo = oldChannel->GetLoadInfo();
     252             : 
     253             :   // if no loadInfo on the channel, nothing for us to do
     254           0 :   if (!loadInfo) {
     255           0 :     return NS_OK;
     256             :   }
     257             : 
     258             :   // No need to continue processing if CSP is disabled or if the protocol
     259             :   // is *not* subject to CSP.
     260             :   // Please note, the correct way to opt-out of CSP using a custom
     261             :   // protocolHandler is to set one of the nsIProtocolHandler flags
     262             :   // that are whitelistet in subjectToCSP()
     263           0 :   nsContentPolicyType policyType = loadInfo->InternalContentPolicyType();
     264           0 :   if (!sCSPEnabled || !subjectToCSP(newUri, policyType)) {
     265           0 :     return NS_OK;
     266             :   }
     267             : 
     268             :   /* Since redirecting channels don't call into nsIContentPolicy, we call our
     269             :    * Content Policy implementation directly when redirects occur using the
     270             :    * information set in the LoadInfo when channels are created.
     271             :    *
     272             :    * We check if the CSP permits this host for this type of load, if not,
     273             :    * we cancel the load now.
     274             :    */
     275           0 :   nsCOMPtr<nsIURI> originalUri;
     276           0 :   rv = oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
     277           0 :   if (NS_FAILED(rv)) {
     278           0 :     autoCallback.DontCallback();
     279           0 :     oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
     280           0 :     return rv;
     281             :   }
     282             : 
     283           0 :   bool isPreload = nsContentUtils::IsPreloadType(policyType);
     284             : 
     285             :   /* On redirect, if the content policy is a preload type, rejecting the preload
     286             :    * results in the load silently failing, so we convert preloads to the actual
     287             :    * type. See Bug 1219453.
     288             :    */
     289             :   policyType =
     290           0 :     nsContentUtils::InternalContentPolicyTypeToExternalOrWorker(policyType);
     291             : 
     292           0 :   int16_t aDecision = nsIContentPolicy::ACCEPT;
     293             :   // 1) Apply speculative CSP for preloads
     294           0 :   if (isPreload) {
     295           0 :     nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
     296           0 :     loadInfo->LoadingPrincipal()->GetPreloadCsp(getter_AddRefs(preloadCsp));
     297             : 
     298           0 :     if (preloadCsp) {
     299             :       // Pass  originalURI as aExtra to indicate the redirect
     300           0 :       preloadCsp->ShouldLoad(policyType,     // load type per nsIContentPolicy (uint32_t)
     301             :                              newUri,         // nsIURI
     302             :                              nullptr,        // nsIURI
     303             :                              nullptr,        // nsISupports
     304           0 :                              EmptyCString(), // ACString - MIME guess
     305             :                              originalUri,    // aExtra
     306           0 :                              &aDecision);
     307             : 
     308             :       // if the preload policy already denied the load, then there
     309             :       // is no point in checking the real policy
     310           0 :       if (NS_CP_REJECTED(aDecision)) {
     311           0 :         autoCallback.DontCallback();
     312           0 :         oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
     313           0 :         return NS_BINDING_FAILED;
     314             :       }
     315             :     }
     316             :   }
     317             : 
     318             :   // 2) Apply actual CSP to all loads
     319           0 :   nsCOMPtr<nsIContentSecurityPolicy> csp;
     320           0 :   loadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(csp));
     321             : 
     322           0 :   if (csp) {
     323             :     // Pass  originalURI as aExtra to indicate the redirect
     324           0 :     csp->ShouldLoad(policyType,     // load type per nsIContentPolicy (uint32_t)
     325             :                     newUri,         // nsIURI
     326             :                     nullptr,        // nsIURI
     327             :                     nullptr,        // nsISupports
     328           0 :                     EmptyCString(), // ACString - MIME guess
     329             :                     originalUri,    // aExtra
     330           0 :                     &aDecision);
     331             :   }
     332             : 
     333             :   // if ShouldLoad doesn't accept the load, cancel the request
     334           0 :   if (!NS_CP_ACCEPTED(aDecision)) {
     335           0 :     autoCallback.DontCallback();
     336           0 :     oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
     337           0 :     return NS_BINDING_FAILED;
     338             :   }
     339           0 :   return NS_OK;
     340             : }

Generated by: LCOV version 1.13