LCOV - code coverage report
Current view: top level - dom/security - nsMixedContentBlocker.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 87 478 18.2 %
Date: 2017-07-14 16:53:18 Functions: 10 22 45.5 %
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 "nsMixedContentBlocker.h"
       8             : 
       9             : #include "nsContentPolicyUtils.h"
      10             : #include "nsCSPContext.h"
      11             : #include "nsThreadUtils.h"
      12             : #include "nsINode.h"
      13             : #include "nsCOMPtr.h"
      14             : #include "nsIDocShell.h"
      15             : #include "nsISecurityEventSink.h"
      16             : #include "nsIWebProgressListener.h"
      17             : #include "nsContentUtils.h"
      18             : #include "nsIRequest.h"
      19             : #include "nsIDocument.h"
      20             : #include "nsIContentViewer.h"
      21             : #include "nsIChannel.h"
      22             : #include "nsIHttpChannel.h"
      23             : #include "nsIParentChannel.h"
      24             : #include "mozilla/Preferences.h"
      25             : #include "nsIScriptObjectPrincipal.h"
      26             : #include "nsISecureBrowserUI.h"
      27             : #include "nsIDocumentLoader.h"
      28             : #include "nsIWebNavigation.h"
      29             : #include "nsLoadGroup.h"
      30             : #include "nsIScriptError.h"
      31             : #include "nsIURI.h"
      32             : #include "nsIChannelEventSink.h"
      33             : #include "nsNetUtil.h"
      34             : #include "nsAsyncRedirectVerifyHelper.h"
      35             : #include "mozilla/LoadInfo.h"
      36             : #include "nsISiteSecurityService.h"
      37             : #include "prnetdb.h"
      38             : 
      39             : #include "mozilla/Logging.h"
      40             : #include "mozilla/Telemetry.h"
      41             : #include "mozilla/dom/ContentChild.h"
      42             : #include "mozilla/ipc/URIUtils.h"
      43             : 
      44             : 
      45             : using namespace mozilla;
      46             : 
      47             : enum nsMixedContentBlockerMessageType {
      48             :   eBlocked = 0x00,
      49             :   eUserOverride = 0x01
      50             : };
      51             : 
      52             : // Is mixed script blocking (fonts, plugin content, scripts, stylesheets,
      53             : // iframes, websockets, XHR) enabled?
      54             : bool nsMixedContentBlocker::sBlockMixedScript = false;
      55             : 
      56             : // Is mixed display content blocking (images, audio, video, <a ping>) enabled?
      57             : bool nsMixedContentBlocker::sBlockMixedDisplay = false;
      58             : 
      59             : // Do we move HSTS before mixed-content
      60             : bool nsMixedContentBlocker::sUseHSTS = false;
      61             : // Do we send an HSTS priming request
      62             : bool nsMixedContentBlocker::sSendHSTSPriming = false;
      63             : // Default HSTS Priming failure timeout to 7 days, in seconds
      64             : uint32_t nsMixedContentBlocker::sHSTSPrimingCacheTimeout = (60 * 60 * 24 * 7);
      65             : 
      66             : bool
      67           0 : IsEligibleForHSTSPriming(nsIURI* aContentLocation) {
      68           0 :   bool isHttpScheme = false;
      69           0 :   nsresult rv = aContentLocation->SchemeIs("http", &isHttpScheme);
      70           0 :   NS_ENSURE_SUCCESS(rv, false);
      71           0 :   if (!isHttpScheme) {
      72           0 :     return false;
      73             :   }
      74             : 
      75           0 :   int32_t port = -1;
      76           0 :   rv = aContentLocation->GetPort(&port);
      77           0 :   NS_ENSURE_SUCCESS(rv, false);
      78           0 :   int32_t defaultPort = NS_GetDefaultPort("https");
      79             : 
      80           0 :   if (port != -1 && port != defaultPort) {
      81             :     // HSTS priming requests are only sent if the port is the default port
      82           0 :     return false;
      83             :   }
      84             : 
      85           0 :   nsAutoCString hostname;
      86           0 :   rv = aContentLocation->GetHost(hostname);
      87           0 :   NS_ENSURE_SUCCESS(rv, false);
      88             : 
      89             :   PRNetAddr hostAddr;
      90           0 :   return (PR_StringToNetAddr(hostname.get(), &hostAddr) != PR_SUCCESS);
      91             : }
      92             : 
      93             : enum MixedContentHSTSState {
      94             :   MCB_HSTS_PASSIVE_NO_HSTS   = 0,
      95             :   MCB_HSTS_PASSIVE_WITH_HSTS = 1,
      96             :   MCB_HSTS_ACTIVE_NO_HSTS    = 2,
      97             :   MCB_HSTS_ACTIVE_WITH_HSTS  = 3
      98             : };
      99             : 
     100             : // Similar to the existing mixed-content HSTS, except MCB_HSTS_*_NO_HSTS is
     101             : // broken into two distinct states, indicating whether we plan to send a priming
     102             : // request or not. If we decided not go send a priming request, it could be
     103             : // because it is a type we do not support, or because we cached a previous
     104             : // negative response.
     105             : enum MixedContentHSTSPrimingState {
     106             :   eMCB_HSTS_PASSIVE_WITH_HSTS  = 0,
     107             :   eMCB_HSTS_ACTIVE_WITH_HSTS   = 1,
     108             :   eMCB_HSTS_PASSIVE_NO_PRIMING = 2,
     109             :   eMCB_HSTS_PASSIVE_DO_PRIMING = 3,
     110             :   eMCB_HSTS_ACTIVE_NO_PRIMING  = 4,
     111             :   eMCB_HSTS_ACTIVE_DO_PRIMING  = 5,
     112             :   eMCB_HSTS_PASSIVE_UPGRADE    = 6,
     113             :   eMCB_HSTS_ACTIVE_UPGRADE     = 7,
     114             : };
     115             : 
     116             : // Fired at the document that attempted to load mixed content.  The UI could
     117             : // handle this event, for example, by displaying an info bar that offers the
     118             : // choice to reload the page with mixed content permitted.
     119           0 : class nsMixedContentEvent : public Runnable
     120             : {
     121             : public:
     122           0 :   nsMixedContentEvent(nsISupports* aContext,
     123             :                       MixedContentTypes aType,
     124             :                       bool aRootHasSecureConnection)
     125           0 :     : mozilla::Runnable("nsMixedContentEvent")
     126             :     , mContext(aContext)
     127             :     , mType(aType)
     128           0 :     , mRootHasSecureConnection(aRootHasSecureConnection)
     129           0 :   {}
     130             : 
     131           0 :   NS_IMETHOD Run() override
     132             :   {
     133           0 :     NS_ASSERTION(mContext,
     134             :                  "You can't call this runnable without a requesting context");
     135             : 
     136             :     // To update the security UI in the tab with the blocked mixed content, call
     137             :     // nsISecurityEventSink::OnSecurityChange.  You can get to the event sink by
     138             :     // calling NS_CP_GetDocShellFromContext on the context, and QI'ing to
     139             :     // nsISecurityEventSink.
     140             : 
     141             : 
     142             :     // Mixed content was allowed and is about to load; get the document and
     143             :     // set the approriate flag to true if we are about to load Mixed Active
     144             :     // Content.
     145           0 :     nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(mContext);
     146           0 :     if (!docShell) {
     147           0 :         return NS_OK;
     148             :     }
     149           0 :     nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
     150           0 :     docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
     151           0 :     NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
     152             : 
     153             :     // now get the document from sameTypeRoot
     154           0 :     nsCOMPtr<nsIDocument> rootDoc = sameTypeRoot->GetDocument();
     155           0 :     NS_ASSERTION(rootDoc, "No root document from document shell root tree item.");
     156             : 
     157             :     // Get eventSink and the current security state from the docShell
     158           0 :     nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell);
     159           0 :     NS_ASSERTION(eventSink, "No eventSink from docShell.");
     160           0 :     nsCOMPtr<nsIDocShell> rootShell = do_GetInterface(sameTypeRoot);
     161           0 :     NS_ASSERTION(rootShell, "No root docshell from document shell root tree item.");
     162           0 :     uint32_t state = nsIWebProgressListener::STATE_IS_BROKEN;
     163           0 :     nsCOMPtr<nsISecureBrowserUI> securityUI;
     164           0 :     rootShell->GetSecurityUI(getter_AddRefs(securityUI));
     165             :     // If there is no securityUI, document doesn't have a security state to
     166             :     // update.  But we still want to set the document flags, so we don't return
     167             :     // early.
     168           0 :     nsresult stateRV = NS_ERROR_FAILURE;
     169           0 :     if (securityUI) {
     170           0 :       stateRV = securityUI->GetState(&state);
     171             :     }
     172             : 
     173           0 :     if (mType == eMixedScript) {
     174             :        // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
     175           0 :        if (rootDoc->GetHasMixedActiveContentLoaded()) {
     176           0 :          return NS_OK;
     177             :        }
     178           0 :        rootDoc->SetHasMixedActiveContentLoaded(true);
     179             : 
     180             :       // Update the security UI in the tab with the allowed mixed active content
     181           0 :       if (securityUI) {
     182             :         // Bug 1182551 - before changing the security state to broken, check
     183             :         // that the root is actually secure.
     184           0 :         if (mRootHasSecureConnection) {
     185             :           // reset state security flag
     186           0 :           state = state >> 4 << 4;
     187             :           // set state security flag to broken, since there is mixed content
     188           0 :           state |= nsIWebProgressListener::STATE_IS_BROKEN;
     189             : 
     190             :           // If mixed display content is loaded, make sure to include that in the state.
     191           0 :           if (rootDoc->GetHasMixedDisplayContentLoaded()) {
     192           0 :             state |= nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT;
     193             :           }
     194             : 
     195           0 :           eventSink->OnSecurityChange(mContext,
     196           0 :                                       (state | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
     197             :         } else {
     198             :           // root not secure, mixed active content loaded in an https subframe
     199           0 :           if (NS_SUCCEEDED(stateRV)) {
     200           0 :             eventSink->OnSecurityChange(mContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
     201             :           }
     202             :         }
     203             :       }
     204             : 
     205           0 :     } else if (mType == eMixedDisplay) {
     206             :       // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
     207           0 :       if (rootDoc->GetHasMixedDisplayContentLoaded()) {
     208           0 :         return NS_OK;
     209             :       }
     210           0 :       rootDoc->SetHasMixedDisplayContentLoaded(true);
     211             : 
     212             :       // Update the security UI in the tab with the allowed mixed display content.
     213           0 :       if (securityUI) {
     214             :         // Bug 1182551 - before changing the security state to broken, check
     215             :         // that the root is actually secure.
     216           0 :         if (mRootHasSecureConnection) {
     217             :           // reset state security flag
     218           0 :           state = state >> 4 << 4;
     219             :           // set state security flag to broken, since there is mixed content
     220           0 :           state |= nsIWebProgressListener::STATE_IS_BROKEN;
     221             : 
     222             :           // If mixed active content is loaded, make sure to include that in the state.
     223           0 :           if (rootDoc->GetHasMixedActiveContentLoaded()) {
     224           0 :             state |= nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT;
     225             :           }
     226             : 
     227           0 :           eventSink->OnSecurityChange(mContext,
     228           0 :                                       (state | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
     229             :         } else {
     230             :           // root not secure, mixed display content loaded in an https subframe
     231           0 :           if (NS_SUCCEEDED(stateRV)) {
     232           0 :             eventSink->OnSecurityChange(mContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
     233             :           }
     234             :         }
     235             :       }
     236             :     }
     237             : 
     238           0 :     return NS_OK;
     239             :   }
     240             : private:
     241             :   // The requesting context for the content load. Generally, a DOM node from
     242             :   // the document that caused the load.
     243             :   nsCOMPtr<nsISupports> mContext;
     244             : 
     245             :   // The type of mixed content detected, e.g. active or display
     246             :   const MixedContentTypes mType;
     247             : 
     248             :   // Indicates whether the top level load is https or not.
     249             :   bool mRootHasSecureConnection;
     250             : };
     251             : 
     252             : 
     253           3 : nsMixedContentBlocker::nsMixedContentBlocker()
     254             : {
     255             :   // Cache the pref for mixed script blocking
     256             :   Preferences::AddBoolVarCache(&sBlockMixedScript,
     257           3 :                                "security.mixed_content.block_active_content");
     258             : 
     259             :   // Cache the pref for mixed display blocking
     260             :   Preferences::AddBoolVarCache(&sBlockMixedDisplay,
     261           3 :                                "security.mixed_content.block_display_content");
     262             : 
     263             :   // Cache the pref for HSTS
     264             :   Preferences::AddBoolVarCache(&sUseHSTS,
     265           3 :                                "security.mixed_content.use_hsts");
     266             : 
     267             :   // Cache the pref for sending HSTS priming
     268             :   Preferences::AddBoolVarCache(&sSendHSTSPriming,
     269           3 :                                "security.mixed_content.send_hsts_priming");
     270             : 
     271             :   // Cache the pref for HSTS priming failure cache time
     272             :   Preferences::AddUintVarCache(&sHSTSPrimingCacheTimeout,
     273           3 :                                "security.mixed_content.hsts_priming_cache_timeout");
     274           3 : }
     275             : 
     276           0 : nsMixedContentBlocker::~nsMixedContentBlocker()
     277             : {
     278           0 : }
     279             : 
     280          36 : NS_IMPL_ISUPPORTS(nsMixedContentBlocker, nsIContentPolicy, nsIChannelEventSink)
     281             : 
     282             : static void
     283           0 : LogMixedContentMessage(MixedContentTypes aClassification,
     284             :                        nsIURI* aContentLocation,
     285             :                        nsIDocument* aRootDoc,
     286             :                        nsMixedContentBlockerMessageType aMessageType)
     287             : {
     288           0 :   nsAutoCString messageCategory;
     289             :   uint32_t severityFlag;
     290           0 :   nsAutoCString messageLookupKey;
     291             : 
     292           0 :   if (aMessageType == eBlocked) {
     293           0 :     severityFlag = nsIScriptError::errorFlag;
     294           0 :     messageCategory.AssignLiteral("Mixed Content Blocker");
     295           0 :     if (aClassification == eMixedDisplay) {
     296           0 :       messageLookupKey.AssignLiteral("BlockMixedDisplayContent");
     297             :     } else {
     298           0 :       messageLookupKey.AssignLiteral("BlockMixedActiveContent");
     299             :     }
     300             :   } else {
     301           0 :     severityFlag = nsIScriptError::warningFlag;
     302           0 :     messageCategory.AssignLiteral("Mixed Content Message");
     303           0 :     if (aClassification == eMixedDisplay) {
     304           0 :       messageLookupKey.AssignLiteral("LoadingMixedDisplayContent2");
     305             :     } else {
     306           0 :       messageLookupKey.AssignLiteral("LoadingMixedActiveContent2");
     307             :     }
     308             :   }
     309             : 
     310           0 :   NS_ConvertUTF8toUTF16 locationSpecUTF16(aContentLocation->GetSpecOrDefault());
     311           0 :   const char16_t* strings[] = { locationSpecUTF16.get() };
     312           0 :   nsContentUtils::ReportToConsole(severityFlag, messageCategory, aRootDoc,
     313             :                                   nsContentUtils::eSECURITY_PROPERTIES,
     314           0 :                                   messageLookupKey.get(), strings, ArrayLength(strings));
     315           0 : }
     316             : 
     317             : /* nsIChannelEventSink implementation
     318             :  * This code is called when a request is redirected.
     319             :  * We check the channel associated with the new uri is allowed to load
     320             :  * in the current context
     321             :  */
     322             : NS_IMETHODIMP
     323           0 : nsMixedContentBlocker::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
     324             :                                               nsIChannel* aNewChannel,
     325             :                                               uint32_t aFlags,
     326             :                                               nsIAsyncVerifyRedirectCallback* aCallback)
     327             : {
     328           0 :   nsAsyncRedirectAutoCallback autoCallback(aCallback);
     329             : 
     330           0 :   if (!aOldChannel) {
     331           0 :     NS_ERROR("No channel when evaluating mixed content!");
     332           0 :     return NS_ERROR_FAILURE;
     333             :   }
     334             : 
     335             :   // If we are in the parent process in e10s, we don't have access to the
     336             :   // document node, and hence ShouldLoad will fail when we try to get
     337             :   // the docShell.  If that's the case, ignore mixed content checks
     338             :   // on redirects in the parent.  Let the child check for mixed content.
     339           0 :   nsCOMPtr<nsIParentChannel> is_ipc_channel;
     340           0 :   NS_QueryNotificationCallbacks(aNewChannel, is_ipc_channel);
     341           0 :   if (is_ipc_channel) {
     342           0 :     return NS_OK;
     343             :   }
     344             : 
     345             :   nsresult rv;
     346           0 :   nsCOMPtr<nsIURI> oldUri;
     347           0 :   rv = aOldChannel->GetURI(getter_AddRefs(oldUri));
     348           0 :   NS_ENSURE_SUCCESS(rv, rv);
     349             : 
     350           0 :   nsCOMPtr<nsIURI> newUri;
     351           0 :   rv = aNewChannel->GetURI(getter_AddRefs(newUri));
     352           0 :   NS_ENSURE_SUCCESS(rv, rv);
     353             : 
     354             :   // Get the loading Info from the old channel
     355           0 :   nsCOMPtr<nsILoadInfo> loadInfo;
     356           0 :   rv = aOldChannel->GetLoadInfo(getter_AddRefs(loadInfo));
     357           0 :   NS_ENSURE_SUCCESS(rv, rv);
     358           0 :   if (!loadInfo) {
     359             :     // XXX: We want to have a loadInfo on all channels, but we don't yet.
     360             :     // If an addon creates a channel, they may not set loadinfo. If that
     361             :     // channel redirects from one page to another page, we would get caught
     362             :     // in this code path. Hence, we have to return NS_OK. Once we have more
     363             :     // confidence that all channels have loadinfo, we can change this to
     364             :     // a failure. See bug 1077201.
     365           0 :     return NS_OK;
     366             :   }
     367             : 
     368           0 :   nsContentPolicyType contentPolicyType = loadInfo->InternalContentPolicyType();
     369           0 :   nsCOMPtr<nsIPrincipal> requestingPrincipal = loadInfo->LoadingPrincipal();
     370             : 
     371             :   // Since we are calling shouldLoad() directly on redirects, we don't go through the code
     372             :   // in nsContentPolicyUtils::NS_CheckContentLoadPolicy(). Hence, we have to
     373             :   // duplicate parts of it here.
     374           0 :   nsCOMPtr<nsIURI> requestingLocation;
     375           0 :   if (requestingPrincipal) {
     376             :     // We check to see if the loadingPrincipal is systemPrincipal and return
     377             :     // early if it is
     378           0 :     if (nsContentUtils::IsSystemPrincipal(requestingPrincipal)) {
     379           0 :       return NS_OK;
     380             :     }
     381             :     // We set the requestingLocation from the RequestingPrincipal.
     382           0 :     rv = requestingPrincipal->GetURI(getter_AddRefs(requestingLocation));
     383           0 :     NS_ENSURE_SUCCESS(rv, rv);
     384             :   }
     385             : 
     386           0 :   nsCOMPtr<nsISupports> requestingContext = loadInfo->LoadingNode();
     387             : 
     388           0 :   int16_t decision = REJECT_REQUEST;
     389           0 :   rv = ShouldLoad(contentPolicyType,
     390             :                   newUri,
     391             :                   requestingLocation,
     392             :                   requestingContext,
     393           0 :                   EmptyCString(),       // aMimeGuess
     394             :                   nullptr,              // aExtra
     395             :                   requestingPrincipal,
     396           0 :                   &decision);
     397           0 :   NS_ENSURE_SUCCESS(rv, rv);
     398             : 
     399           0 :   if (nsMixedContentBlocker::sSendHSTSPriming) {
     400             :     // The LoadInfo passed in is for the original channel, HSTS priming needs to
     401             :     // be set on the new channel, if required. If the redirect changes
     402             :     // http->https, or vice-versa, the need for priming may change.
     403           0 :     nsCOMPtr<nsILoadInfo> newLoadInfo;
     404           0 :     rv = aNewChannel->GetLoadInfo(getter_AddRefs(newLoadInfo));
     405           0 :     NS_ENSURE_SUCCESS(rv, rv);
     406           0 :     if (newLoadInfo) {
     407           0 :       rv = nsMixedContentBlocker::MarkLoadInfoForPriming(newUri,
     408             :                                                          requestingContext,
     409           0 :                                                          newLoadInfo);
     410           0 :       if (NS_FAILED(rv)) {
     411           0 :         decision = REJECT_REQUEST;
     412           0 :         newLoadInfo->ClearHSTSPriming();
     413             :       }
     414             :     } else {
     415           0 :       decision = REJECT_REQUEST;
     416             :     }
     417             :   }
     418             : 
     419             :   // If the channel is about to load mixed content, abort the channel
     420           0 :   if (!NS_CP_ACCEPTED(decision)) {
     421           0 :     autoCallback.DontCallback();
     422           0 :     return NS_BINDING_FAILED;
     423             :   }
     424             : 
     425           0 :   return NS_OK;
     426             : }
     427             : 
     428             : /* This version of ShouldLoad() is non-static and called by the Content Policy
     429             :  * API and AsyncOnChannelRedirect().  See nsIContentPolicy::ShouldLoad()
     430             :  * for detailed description of the parameters.
     431             :  */
     432             : NS_IMETHODIMP
     433          23 : nsMixedContentBlocker::ShouldLoad(uint32_t aContentType,
     434             :                                   nsIURI* aContentLocation,
     435             :                                   nsIURI* aRequestingLocation,
     436             :                                   nsISupports* aRequestingContext,
     437             :                                   const nsACString& aMimeGuess,
     438             :                                   nsISupports* aExtra,
     439             :                                   nsIPrincipal* aRequestPrincipal,
     440             :                                   int16_t* aDecision)
     441             : {
     442             :   // We pass in false as the first parameter to ShouldLoad(), because the
     443             :   // callers of this method don't know whether the load went through cached
     444             :   // image redirects.  This is handled by direct callers of the static
     445             :   // ShouldLoad.
     446             :   nsresult rv = ShouldLoad(false,   // aHadInsecureImageRedirect
     447             :                            aContentType,
     448             :                            aContentLocation,
     449             :                            aRequestingLocation,
     450             :                            aRequestingContext,
     451             :                            aMimeGuess,
     452             :                            aExtra,
     453             :                            aRequestPrincipal,
     454          23 :                            aDecision);
     455          23 :   return rv;
     456             : }
     457             : 
     458             : bool
     459           0 : nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(nsIURI* aURL) {
     460           0 :   nsAutoCString host;
     461           0 :   nsresult rv = aURL->GetHost(host);
     462           0 :   NS_ENSURE_SUCCESS(rv, false);
     463             : 
     464             :   // We could also allow 'localhost' (if we can guarantee that it resolves
     465             :   // to a loopback address), but Chrome doesn't support it as of writing. For
     466             :   // web compat, lets only allow what Chrome allows.
     467           0 :   return host.Equals("127.0.0.1") || host.Equals("::1");
     468             : }
     469             : 
     470             : /* Static version of ShouldLoad() that contains all the Mixed Content Blocker
     471             :  * logic.  Called from non-static ShouldLoad().
     472             :  */
     473             : nsresult
     474          23 : nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
     475             :                                   uint32_t aContentType,
     476             :                                   nsIURI* aContentLocation,
     477             :                                   nsIURI* aRequestingLocation,
     478             :                                   nsISupports* aRequestingContext,
     479             :                                   const nsACString& aMimeGuess,
     480             :                                   nsISupports* aExtra,
     481             :                                   nsIPrincipal* aRequestPrincipal,
     482             :                                   int16_t* aDecision)
     483             : {
     484             :   // Asserting that we are on the main thread here and hence do not have to lock
     485             :   // and unlock sBlockMixedScript and sBlockMixedDisplay before reading/writing
     486             :   // to them.
     487          23 :   MOZ_ASSERT(NS_IsMainThread());
     488             : 
     489          23 :   bool isPreload = nsContentUtils::IsPreloadType(aContentType);
     490             : 
     491             :   // The content policy type that we receive may be an internal type for
     492             :   // scripts.  Let's remember if we have seen a worker type, and reset it to the
     493             :   // external type in all cases right now.
     494          23 :   bool isWorkerType = aContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
     495          46 :                       aContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER ||
     496          23 :                       aContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER;
     497          23 :   aContentType = nsContentUtils::InternalContentPolicyTypeToExternal(aContentType);
     498             : 
     499             :   // Assume active (high risk) content and blocked by default
     500          23 :   MixedContentTypes classification = eMixedScript;
     501             :   // Make decision to block/reject by default
     502          23 :   *aDecision = REJECT_REQUEST;
     503             : 
     504             :   // Notes on non-obvious decisions:
     505             :   //
     506             :   // TYPE_DTD: A DTD can contain entity definitions that expand to scripts.
     507             :   //
     508             :   // TYPE_FONT: The TrueType hinting mechanism is basically a scripting
     509             :   // language that gets interpreted by the operating system's font rasterizer.
     510             :   // Mixed content web fonts are relatively uncommon, and we can can fall back
     511             :   // to built-in fonts with minimal disruption in almost all cases.
     512             :   //
     513             :   // TYPE_OBJECT_SUBREQUEST could actually be either active content (e.g. a
     514             :   // script that a plugin will execute) or display content (e.g. Flash video
     515             :   // content).  Until we have a way to determine active vs passive content
     516             :   // from plugin requests (bug 836352), we will treat this as passive content.
     517             :   // This is to prevent false positives from causing users to become
     518             :   // desensitized to the mixed content blocker.
     519             :   //
     520             :   // TYPE_CSP_REPORT: High-risk because they directly leak information about
     521             :   // the content of the page, and because blocking them does not have any
     522             :   // negative effect on the page loading.
     523             :   //
     524             :   // TYPE_PING: Ping requests are POSTS, not GETs like images and media.
     525             :   // Also, PING requests have no bearing on the rendering or operation of
     526             :   // the page when used as designed, so even though they are lower risk than
     527             :   // scripts, blocking them is basically risk-free as far as compatibility is
     528             :   // concerned.
     529             :   //
     530             :   // TYPE_STYLESHEET: XSLT stylesheets can insert scripts. CSS positioning
     531             :   // and other advanced CSS features can possibly be exploited to cause
     532             :   // spoofing attacks (e.g. make a "grant permission" button look like a
     533             :   // "refuse permission" button).
     534             :   //
     535             :   // TYPE_BEACON: Beacon requests are similar to TYPE_PING, and are blocked by
     536             :   // default.
     537             :   //
     538             :   // TYPE_WEBSOCKET: The Websockets API requires browsers to
     539             :   // reject mixed-content websockets: "If secure is false but the origin of
     540             :   // the entry script has a scheme component that is itself a secure protocol,
     541             :   // e.g. HTTPS, then throw a SecurityError exception." We already block mixed
     542             :   // content websockets within the websockets implementation, so we don't need
     543             :   // to do any blocking here, nor do we need to provide a way to undo or
     544             :   // override the blocking. Websockets without TLS are very flaky anyway in the
     545             :   // face of many HTTP-aware proxies. Compared to passive content, there is
     546             :   // additional risk that the script using WebSockets will disclose sensitive
     547             :   // information from the HTTPS page and/or eval (directly or indirectly)
     548             :   // received data.
     549             :   //
     550             :   // TYPE_XMLHTTPREQUEST: XHR requires either same origin or CORS, so most
     551             :   // mixed-content XHR will already be blocked by that check. This will also
     552             :   // block HTTPS-to-HTTP XHR with CORS. The same security concerns mentioned
     553             :   // above for WebSockets apply to XHR, and XHR should have the same security
     554             :   // properties as WebSockets w.r.t. mixed content. XHR's handling of redirects
     555             :   // amplifies these concerns.
     556             : 
     557             : 
     558             :   static_assert(TYPE_DATAREQUEST == TYPE_XMLHTTPREQUEST,
     559             :                 "TYPE_DATAREQUEST is not a synonym for "
     560             :                 "TYPE_XMLHTTPREQUEST");
     561             : 
     562          23 :   switch (aContentType) {
     563             :     // The top-level document cannot be mixed content by definition
     564             :     case TYPE_DOCUMENT:
     565          10 :       *aDecision = ACCEPT;
     566          10 :       return NS_OK;
     567             :     // Creating insecure websocket connections in a secure page is blocked already
     568             :     // in the websocket constructor. We don't need to check the blocking here
     569             :     // and we don't want to un-block
     570             :     case TYPE_WEBSOCKET:
     571           0 :       *aDecision = ACCEPT;
     572           0 :       return NS_OK;
     573             : 
     574             :     // Static display content is considered moderate risk for mixed content so
     575             :     // these will be blocked according to the mixed display preference
     576             :     case TYPE_IMAGE:
     577             :     case TYPE_MEDIA:
     578             :     case TYPE_OBJECT_SUBREQUEST:
     579           2 :       classification = eMixedDisplay;
     580           2 :       break;
     581             : 
     582             :     // Active content (or content with a low value/risk-of-blocking ratio)
     583             :     // that has been explicitly evaluated; listed here for documentation
     584             :     // purposes and to avoid the assertion and warning for the default case.
     585             :     case TYPE_BEACON:
     586             :     case TYPE_CSP_REPORT:
     587             :     case TYPE_DTD:
     588             :     case TYPE_FETCH:
     589             :     case TYPE_FONT:
     590             :     case TYPE_IMAGESET:
     591             :     case TYPE_OBJECT:
     592             :     case TYPE_SCRIPT:
     593             :     case TYPE_STYLESHEET:
     594             :     case TYPE_SUBDOCUMENT:
     595             :     case TYPE_PING:
     596             :     case TYPE_WEB_MANIFEST:
     597             :     case TYPE_XBL:
     598             :     case TYPE_XMLHTTPREQUEST:
     599             :     case TYPE_XSLT:
     600             :     case TYPE_OTHER:
     601          11 :       break;
     602             : 
     603             : 
     604             :     // This content policy works as a whitelist.
     605             :     default:
     606           0 :       MOZ_ASSERT(false, "Mixed content of unknown type");
     607             :   }
     608             : 
     609             :   // Make sure to get the URI the load started with. No need to check
     610             :   // outer schemes because all the wrapping pseudo protocols inherit the
     611             :   // security properties of the actual network request represented
     612             :   // by the innerMost URL.
     613          26 :   nsCOMPtr<nsIURI> innerContentLocation = NS_GetInnermostURI(aContentLocation);
     614          13 :   if (!innerContentLocation) {
     615           0 :     NS_ERROR("Can't get innerURI from aContentLocation");
     616           0 :     *aDecision = REJECT_REQUEST;
     617           0 :     return NS_OK;
     618             :   }
     619             : 
     620             :  /* Get the scheme of the sub-document resource to be requested. If it is
     621             :   * a safe to load in an https context then mixed content doesn't apply.
     622             :   *
     623             :   * Check Protocol Flags to determine if scheme is safe to load:
     624             :   * URI_DOES_NOT_RETURN_DATA - e.g.
     625             :   *   "mailto"
     626             :   * URI_IS_LOCAL_RESOURCE - e.g.
     627             :   *   "data",
     628             :   *   "resource",
     629             :   *   "moz-icon"
     630             :   * URI_INHERITS_SECURITY_CONTEXT - e.g.
     631             :   *   "javascript"
     632             :   * URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT - e.g.
     633             :   *   "https",
     634             :   *   "moz-safe-about"
     635             :   *
     636             :   */
     637          13 :   bool schemeLocal = false;
     638          13 :   bool schemeNoReturnData = false;
     639          13 :   bool schemeInherits = false;
     640          13 :   bool schemeSecure = false;
     641          39 :   if (NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE , &schemeLocal))  ||
     642          26 :       NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &schemeNoReturnData)) ||
     643          39 :       NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &schemeInherits)) ||
     644          13 :       NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT, &schemeSecure))) {
     645           0 :     *aDecision = REJECT_REQUEST;
     646           0 :     return NS_ERROR_FAILURE;
     647             :   }
     648             :   // TYPE_IMAGE redirects are cached based on the original URI, not the final
     649             :   // destination and hence cache hits for images may not have the correct
     650             :   // innerContentLocation.  Check if the cached hit went through an http redirect,
     651             :   // and if it did, we can't treat this as a secure subresource.
     652          13 :   if (!aHadInsecureImageRedirect &&
     653           5 :       (schemeLocal || schemeNoReturnData || schemeInherits || schemeSecure)) {
     654           8 :     *aDecision = ACCEPT;
     655           8 :      return NS_OK;
     656             :   }
     657             : 
     658             :   // Since there are cases where aRequestingLocation and aRequestPrincipal are
     659             :   // definitely not the owning document, we try to ignore them by extracting the
     660             :   // requestingLocation in the following order:
     661             :   // 1) from the aRequestingContext, either extracting
     662             :   //    a) the node's principal, or the
     663             :   //    b) script object's principal.
     664             :   // 2) if aRequestingContext yields a principal but no location, we check
     665             :   //    if its the system principal. If it is, allow the load.
     666             :   // 3) Special case handling for:
     667             :   //    a) speculative loads, where shouldLoad is called twice (bug 839235)
     668             :   //       and the first speculative load does not include a context.
     669             :   //       In this case we use aRequestingLocation to set requestingLocation.
     670             :   //    b) TYPE_CSP_REPORT which does not provide a context. In this case we
     671             :   //       use aRequestingLocation to set requestingLocation.
     672             :   //    c) content scripts from addon code that do not provide aRequestingContext
     673             :   //       or aRequestingLocation, but do provide aRequestPrincipal.
     674             :   //       If aRequestPrincipal is an expanded principal, we allow the load.
     675             :   // 4) If we still end up not having a requestingLocation, we reject the load.
     676             : 
     677          10 :   nsCOMPtr<nsIPrincipal> principal;
     678             :   // 1a) Try to get the principal if aRequestingContext is a node.
     679          10 :   nsCOMPtr<nsINode> node = do_QueryInterface(aRequestingContext);
     680           5 :   if (node) {
     681           4 :     principal = node->NodePrincipal();
     682             :   }
     683             : 
     684             :   // 1b) Try using the window's script object principal if it's not a node.
     685           5 :   if (!principal) {
     686           2 :     nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(aRequestingContext);
     687           1 :     if (scriptObjPrin) {
     688           0 :       principal = scriptObjPrin->GetPrincipal();
     689             :     }
     690             :   }
     691             : 
     692          10 :   nsCOMPtr<nsIURI> requestingLocation;
     693           5 :   if (principal) {
     694           4 :     principal->GetURI(getter_AddRefs(requestingLocation));
     695             :   }
     696             : 
     697             :   // 2) if aRequestingContext yields a principal but no location, we check if its a system principal.
     698           5 :   if (principal && !requestingLocation) {
     699           0 :     if (nsContentUtils::IsSystemPrincipal(principal)) {
     700           0 :       *aDecision = ACCEPT;
     701           0 :       return NS_OK;
     702             :     }
     703             :   }
     704             : 
     705             :   // 3a,b) Special case handling for speculative loads and TYPE_CSP_REPORT. In
     706             :   // such cases, aRequestingContext doesn't exist, so we use aRequestingLocation.
     707             :   // Unfortunately we can not distinguish between speculative and normal loads here,
     708             :   // otherwise we could special case this assignment.
     709           5 :   if (!requestingLocation) {
     710           1 :     requestingLocation = aRequestingLocation;
     711             :   }
     712             : 
     713             :   // 3c) Special case handling for content scripts from addons code, which only
     714             :   // provide a aRequestPrincipal; aRequestingContext and aRequestingLocation are
     715             :   // both null; if the aRequestPrincipal is an expandedPrincipal, we allow the load.
     716           5 :   if (!principal && !requestingLocation && aRequestPrincipal) {
     717           0 :     nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aRequestPrincipal);
     718           0 :     if (expanded) {
     719           0 :       *aDecision = ACCEPT;
     720           0 :       return NS_OK;
     721             :     }
     722             :   }
     723             : 
     724             :   // 4) Giving up. We still don't have a requesting location, therefore we can't tell
     725             :   //    if this is a mixed content load. Deny to be safe.
     726           5 :   if (!requestingLocation) {
     727           0 :     *aDecision = REJECT_REQUEST;
     728           0 :     return NS_OK;
     729             :   }
     730             : 
     731             :   // Check the parent scheme. If it is not an HTTPS page then mixed content
     732             :   // restrictions do not apply.
     733             :   bool parentIsHttps;
     734          10 :   nsCOMPtr<nsIURI> innerRequestingLocation = NS_GetInnermostURI(requestingLocation);
     735           5 :   if (!innerRequestingLocation) {
     736           0 :     NS_ERROR("Can't get innerURI from requestingLocation");
     737           0 :     *aDecision = REJECT_REQUEST;
     738           0 :     return NS_OK;
     739             :   }
     740             : 
     741           5 :   nsresult rv = innerRequestingLocation->SchemeIs("https", &parentIsHttps);
     742           5 :   if (NS_FAILED(rv)) {
     743           0 :     NS_ERROR("requestingLocation->SchemeIs failed");
     744           0 :     *aDecision = REJECT_REQUEST;
     745           0 :     return NS_OK;
     746             :   }
     747           5 :   if (!parentIsHttps) {
     748           5 :     *aDecision = ACCEPT;
     749           5 :     return NS_OK;
     750             :   }
     751             : 
     752           0 :   nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aRequestingContext);
     753           0 :   NS_ENSURE_TRUE(docShell, NS_OK);
     754             : 
     755             :   // Disallow mixed content loads for workers, shared workers and service
     756             :   // workers.
     757           0 :   if (isWorkerType) {
     758             :     // For workers, we can assume that we're mixed content at this point, since
     759             :     // the parent is https, and the protocol associated with innerContentLocation
     760             :     // doesn't map to the secure URI flags checked above.  Assert this for
     761             :     // sanity's sake
     762             : #ifdef DEBUG
     763           0 :     bool isHttpsScheme = false;
     764           0 :     rv = innerContentLocation->SchemeIs("https", &isHttpsScheme);
     765           0 :     NS_ENSURE_SUCCESS(rv, rv);
     766           0 :     MOZ_ASSERT(!isHttpsScheme);
     767             : #endif
     768           0 :     *aDecision = REJECT_REQUEST;
     769           0 :     return NS_OK;
     770             :   }
     771             : 
     772           0 :   bool isHttpScheme = false;
     773           0 :   rv = innerContentLocation->SchemeIs("http", &isHttpScheme);
     774           0 :   NS_ENSURE_SUCCESS(rv, rv);
     775             : 
     776             :   // Loopback origins are not considered mixed content even over HTTP. See:
     777             :   // https://w3c.github.io/webappsec-mixed-content/#should-block-fetch
     778           0 :   if (isHttpScheme &&
     779           0 :       IsPotentiallyTrustworthyLoopbackURL(innerContentLocation)) {
     780           0 :     *aDecision = ACCEPT;
     781           0 :     return NS_OK;
     782             :   }
     783             : 
     784             :   // The page might have set the CSP directive 'upgrade-insecure-requests'. In such
     785             :   // a case allow the http: load to succeed with the promise that the channel will
     786             :   // get upgraded to https before fetching any data from the netwerk.
     787             :   // Please see: nsHttpChannel::Connect()
     788             :   //
     789             :   // Please note that the CSP directive 'upgrade-insecure-requests' only applies to
     790             :   // http: and ws: (for websockets). Websockets are not subject to mixed content
     791             :   // blocking since insecure websockets are not allowed within secure pages. Hence,
     792             :   // we only have to check against http: here. Skip mixed content blocking if the
     793             :   // subresource load uses http: and the CSP directive 'upgrade-insecure-requests'
     794             :   // is present on the page.
     795           0 :   nsIDocument* document = docShell->GetDocument();
     796           0 :   MOZ_ASSERT(document, "Expected a document");
     797           0 :   if (isHttpScheme && document->GetUpgradeInsecureRequests(isPreload)) {
     798           0 :     *aDecision = ACCEPT;
     799           0 :     return NS_OK;
     800             :   }
     801             : 
     802             :   // The page might have set the CSP directive 'block-all-mixed-content' which
     803             :   // should block not only active mixed content loads but in fact all mixed content
     804             :   // loads, see https://www.w3.org/TR/mixed-content/#strict-checking
     805             :   // Block all non secure loads in case the CSP directive is present. Please note
     806             :   // that at this point we already know, based on |schemeSecure| that the load is
     807             :   // not secure, so we can bail out early at this point.
     808           0 :   if (document->GetBlockAllMixedContent(isPreload)) {
     809             :     // log a message to the console before returning.
     810           0 :     nsAutoCString spec;
     811           0 :     rv = aContentLocation->GetSpec(spec);
     812           0 :     NS_ENSURE_SUCCESS(rv, rv);
     813           0 :     NS_ConvertUTF8toUTF16 reportSpec(spec);
     814             : 
     815           0 :     const char16_t* params[] = { reportSpec.get()};
     816           0 :     CSP_LogLocalizedStr(u"blockAllMixedContent",
     817           0 :                         params, ArrayLength(params),
     818           0 :                         EmptyString(), // aSourceFile
     819           0 :                         EmptyString(), // aScriptSample
     820             :                         0, // aLineNumber
     821             :                         0, // aColumnNumber
     822             :                         nsIScriptError::errorFlag, "CSP",
     823           0 :                         document->InnerWindowID());
     824           0 :     *aDecision = REJECT_REQUEST;
     825           0 :     return NS_OK;
     826             :   }
     827             : 
     828             :   // Determine if the rootDoc is https and if the user decided to allow Mixed Content
     829           0 :   bool rootHasSecureConnection = false;
     830           0 :   bool allowMixedContent = false;
     831           0 :   bool isRootDocShell = false;
     832           0 :   rv = docShell->GetAllowMixedContentAndConnectionData(&rootHasSecureConnection, &allowMixedContent, &isRootDocShell);
     833           0 :   if (NS_FAILED(rv)) {
     834           0 :     *aDecision = REJECT_REQUEST;
     835           0 :     return rv;
     836             :   }
     837             : 
     838             :   // Get the sameTypeRoot tree item from the docshell
     839           0 :   nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
     840           0 :   docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
     841           0 :   NS_ASSERTION(sameTypeRoot, "No root tree item from docshell!");
     842             : 
     843             :   // When navigating an iframe, the iframe may be https
     844             :   // but its parents may not be.  Check the parents to see if any of them are https.
     845             :   // If none of the parents are https, allow the load.
     846           0 :   if (aContentType == TYPE_SUBDOCUMENT && !rootHasSecureConnection) {
     847             : 
     848           0 :     bool httpsParentExists = false;
     849             : 
     850           0 :     nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
     851           0 :     parentTreeItem = docShell;
     852             : 
     853           0 :     while(!httpsParentExists && parentTreeItem) {
     854           0 :       nsCOMPtr<nsIWebNavigation> parentAsNav(do_QueryInterface(parentTreeItem));
     855           0 :       NS_ASSERTION(parentAsNav, "No web navigation object from parent's docshell tree item");
     856           0 :       nsCOMPtr<nsIURI> parentURI;
     857             : 
     858           0 :       parentAsNav->GetCurrentURI(getter_AddRefs(parentURI));
     859           0 :       if (!parentURI) {
     860             :         // if getting the URI fails, assume there is a https parent and break.
     861           0 :         httpsParentExists = true;
     862           0 :         break;
     863             :       }
     864             : 
     865           0 :       nsCOMPtr<nsIURI> innerParentURI = NS_GetInnermostURI(parentURI);
     866           0 :       if (!innerParentURI) {
     867           0 :         NS_ERROR("Can't get innerURI from parentURI");
     868           0 :         *aDecision = REJECT_REQUEST;
     869           0 :         return NS_OK;
     870             :       }
     871             : 
     872           0 :       if (NS_FAILED(innerParentURI->SchemeIs("https", &httpsParentExists))) {
     873             :         // if getting the scheme fails, assume there is a https parent and break.
     874           0 :         httpsParentExists = true;
     875           0 :         break;
     876             :       }
     877             : 
     878             :       // When the parent and the root are the same, we have traversed all the way up
     879             :       // the same type docshell tree.  Break out of the while loop.
     880           0 :       if(sameTypeRoot == parentTreeItem) {
     881           0 :         break;
     882             :       }
     883             : 
     884             :       // update the parent to the grandparent.
     885           0 :       nsCOMPtr<nsIDocShellTreeItem> newParentTreeItem;
     886           0 :       parentTreeItem->GetSameTypeParent(getter_AddRefs(newParentTreeItem));
     887           0 :       parentTreeItem = newParentTreeItem;
     888             :     } // end while loop.
     889             : 
     890           0 :     if (!httpsParentExists) {
     891           0 :       *aDecision = nsIContentPolicy::ACCEPT;
     892           0 :       return NS_OK;
     893             :     }
     894             :   }
     895             : 
     896             :   // Get the root document from the sameTypeRoot
     897           0 :   nsCOMPtr<nsIDocument> rootDoc = sameTypeRoot->GetDocument();
     898           0 :   NS_ASSERTION(rootDoc, "No root document from document shell root tree item.");
     899             : 
     900             :   // Get eventSink and the current security state from the docShell
     901           0 :   nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell);
     902           0 :   NS_ASSERTION(eventSink, "No eventSink from docShell.");
     903           0 :   nsCOMPtr<nsIDocShell> rootShell = do_GetInterface(sameTypeRoot);
     904           0 :   NS_ASSERTION(rootShell, "No root docshell from document shell root tree item.");
     905           0 :   uint32_t state = nsIWebProgressListener::STATE_IS_BROKEN;
     906           0 :   nsCOMPtr<nsISecureBrowserUI> securityUI;
     907           0 :   rootShell->GetSecurityUI(getter_AddRefs(securityUI));
     908             :   // If there is no securityUI, document doesn't have a security state.
     909             :   // Allow load and return early.
     910           0 :   if (!securityUI) {
     911           0 :     *aDecision = nsIContentPolicy::ACCEPT;
     912           0 :     return NS_OK;
     913             :   }
     914           0 :   nsresult stateRV = securityUI->GetState(&state);
     915             : 
     916           0 :   OriginAttributes originAttributes;
     917           0 :   if (principal) {
     918           0 :     originAttributes = principal->OriginAttributesRef();
     919           0 :   } else if (aRequestPrincipal) {
     920           0 :     originAttributes = aRequestPrincipal->OriginAttributesRef();
     921             :   }
     922             : 
     923           0 :   bool active = (classification == eMixedScript);
     924           0 :   bool doHSTSPriming = false;
     925           0 :   if (IsEligibleForHSTSPriming(aContentLocation)) {
     926           0 :     bool hsts = false;
     927           0 :     bool cached = false;
     928             :     nsCOMPtr<nsISiteSecurityService> sss =
     929           0 :       do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
     930           0 :     NS_ENSURE_SUCCESS(rv, rv);
     931           0 :     rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aContentLocation,
     932           0 :         0, originAttributes, &cached, nullptr, &hsts);
     933           0 :     NS_ENSURE_SUCCESS(rv, rv);
     934             : 
     935           0 :     if (hsts && sUseHSTS) {
     936             :       // assume we will be upgraded later
     937           0 :       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
     938             :           (active) ? MixedContentHSTSPrimingState::eMCB_HSTS_ACTIVE_UPGRADE
     939           0 :                    : MixedContentHSTSPrimingState::eMCB_HSTS_PASSIVE_UPGRADE);
     940           0 :       *aDecision = ACCEPT;
     941           0 :       return NS_OK;
     942             :     }
     943             : 
     944             :     // Send a priming request if the result is not already cached and priming
     945             :     // requests are allowed
     946           0 :     if (!cached && sSendHSTSPriming) {
     947             :       // add this URI as a priming location
     948           0 :       doHSTSPriming = true;
     949           0 :       document->AddHSTSPrimingLocation(innerContentLocation,
     950           0 :           HSTSPrimingState::eHSTS_PRIMING_ALLOW);
     951           0 :       *aDecision = ACCEPT;
     952             :     }
     953             :   }
     954             : 
     955             :   // At this point we know that the request is mixed content, and the only
     956             :   // question is whether we block it.  Record telemetry at this point as to
     957             :   // whether HSTS would have fixed things by making the content location
     958             :   // into an HTTPS URL.
     959             :   //
     960             :   // Note that we count this for redirects as well as primary requests. This
     961             :   // will cause some degree of double-counting, especially when mixed content
     962             :   // is not blocked (e.g., for images).  For more detail, see:
     963             :   //   https://bugzilla.mozilla.org/show_bug.cgi?id=1198572#c19
     964             :   //
     965             :   // We do not count requests aHadInsecureImageRedirect=true, since these are
     966             :   // just an artifact of the image caching system.
     967           0 :   if (!aHadInsecureImageRedirect) {
     968           0 :     if (XRE_IsParentProcess()) {
     969           0 :       AccumulateMixedContentHSTS(innerContentLocation, active, doHSTSPriming,
     970           0 :                                  originAttributes);
     971             :     } else {
     972             :       // Ask the parent process to do the same call
     973           0 :       mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
     974           0 :       if (cc) {
     975           0 :         mozilla::ipc::URIParams uri;
     976           0 :         SerializeURI(innerContentLocation, uri);
     977           0 :         cc->SendAccumulateMixedContentHSTS(uri, active, doHSTSPriming,
     978           0 :                                            originAttributes);
     979             :       }
     980             :     }
     981             :   }
     982             : 
     983             :   // set hasMixedContentObjectSubrequest on this object if necessary
     984           0 :   if (aContentType == TYPE_OBJECT_SUBREQUEST) {
     985           0 :     rootDoc->SetHasMixedContentObjectSubrequest(true);
     986             :   }
     987             : 
     988             :   // If the content is display content, and the pref says display content should be blocked, block it.
     989           0 :   if (sBlockMixedDisplay && classification == eMixedDisplay) {
     990           0 :     if (allowMixedContent) {
     991           0 :       LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride);
     992           0 :       *aDecision = nsIContentPolicy::ACCEPT;
     993             :       // See if mixed display content has already loaded on the page or if the state needs to be updated here.
     994             :       // If mixed display hasn't loaded previously, then we need to call OnSecurityChange() to update the UI.
     995           0 :       if (rootDoc->GetHasMixedDisplayContentLoaded()) {
     996           0 :         return NS_OK;
     997             :       }
     998           0 :       rootDoc->SetHasMixedDisplayContentLoaded(true);
     999             : 
    1000           0 :       if (rootHasSecureConnection) {
    1001             :         // reset state security flag
    1002           0 :         state = state >> 4 << 4;
    1003             :         // set state security flag to broken, since there is mixed content
    1004           0 :         state |= nsIWebProgressListener::STATE_IS_BROKEN;
    1005             : 
    1006             :         // If mixed active content is loaded, make sure to include that in the state.
    1007           0 :         if (rootDoc->GetHasMixedActiveContentLoaded()) {
    1008           0 :           state |= nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT;
    1009             :         }
    1010             : 
    1011           0 :         eventSink->OnSecurityChange(aRequestingContext,
    1012           0 :                                     (state | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
    1013             :       } else {
    1014             :         // User has overriden the pref and the root is not https;
    1015             :         // mixed display content was allowed on an https subframe.
    1016           0 :         if (NS_SUCCEEDED(stateRV)) {
    1017           0 :           eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
    1018             :         }
    1019             :       }
    1020             :     } else {
    1021           0 :       if (doHSTSPriming) {
    1022           0 :         document->AddHSTSPrimingLocation(innerContentLocation,
    1023           0 :             HSTSPrimingState::eHSTS_PRIMING_BLOCK);
    1024           0 :         *aDecision = nsIContentPolicy::ACCEPT;
    1025             :       } else {
    1026           0 :         *aDecision = nsIContentPolicy::REJECT_REQUEST;
    1027             :       }
    1028           0 :       LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
    1029           0 :       if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) {
    1030           0 :         rootDoc->SetHasMixedDisplayContentBlocked(true);
    1031           0 :         eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT));
    1032             :       }
    1033             :     }
    1034           0 :     return NS_OK;
    1035             : 
    1036           0 :   } else if (sBlockMixedScript && classification == eMixedScript) {
    1037             :     // If the content is active content, and the pref says active content should be blocked, block it
    1038             :     // unless the user has choosen to override the pref
    1039           0 :     if (allowMixedContent) {
    1040           0 :       LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride);
    1041           0 :       *aDecision = nsIContentPolicy::ACCEPT;
    1042             :       // See if the state will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
    1043           0 :       if (rootDoc->GetHasMixedActiveContentLoaded()) {
    1044           0 :         return NS_OK;
    1045             :       }
    1046           0 :       rootDoc->SetHasMixedActiveContentLoaded(true);
    1047             : 
    1048           0 :       if (rootHasSecureConnection) {
    1049             :         // reset state security flag
    1050           0 :         state = state >> 4 << 4;
    1051             :         // set state security flag to broken, since there is mixed content
    1052           0 :         state |= nsIWebProgressListener::STATE_IS_BROKEN;
    1053             : 
    1054             :         // If mixed display content is loaded, make sure to include that in the state.
    1055           0 :         if (rootDoc->GetHasMixedDisplayContentLoaded()) {
    1056           0 :           state |= nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT;
    1057             :         }
    1058             : 
    1059           0 :         eventSink->OnSecurityChange(aRequestingContext,
    1060           0 :                                     (state | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
    1061             : 
    1062           0 :         return NS_OK;
    1063             :       } else {
    1064             :         // User has already overriden the pref and the root is not https;
    1065             :         // mixed active content was allowed on an https subframe.
    1066           0 :         if (NS_SUCCEEDED(stateRV)) {
    1067           0 :           eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
    1068             :         }
    1069           0 :         return NS_OK;
    1070             :       }
    1071             :     } else {
    1072             :       //User has not overriden the pref by Disabling protection. Reject the request and update the security state.
    1073           0 :       if (doHSTSPriming) {
    1074           0 :         document->AddHSTSPrimingLocation(innerContentLocation,
    1075           0 :             HSTSPrimingState::eHSTS_PRIMING_BLOCK);
    1076           0 :         *aDecision = nsIContentPolicy::ACCEPT;
    1077             :       } else {
    1078           0 :         *aDecision = nsIContentPolicy::REJECT_REQUEST;
    1079             :       }
    1080           0 :       LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
    1081             :       // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
    1082           0 :       if (rootDoc->GetHasMixedActiveContentBlocked()) {
    1083           0 :         return NS_OK;
    1084             :       }
    1085           0 :       rootDoc->SetHasMixedActiveContentBlocked(true);
    1086             : 
    1087             :       // The user has not overriden the pref, so make sure they still have an option by calling eventSink
    1088             :       // which will invoke the doorhanger
    1089           0 :       if (NS_SUCCEEDED(stateRV)) {
    1090           0 :          eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT));
    1091             :       }
    1092           0 :       return NS_OK;
    1093             :     }
    1094             :   } else {
    1095             :     // The content is not blocked by the mixed content prefs.
    1096             : 
    1097             :     // Log a message that we are loading mixed content.
    1098           0 :     LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride);
    1099             : 
    1100             :     // Fire the event from a script runner as it is unsafe to run script
    1101             :     // from within ShouldLoad
    1102             :     nsContentUtils::AddScriptRunner(
    1103           0 :       new nsMixedContentEvent(aRequestingContext, classification, rootHasSecureConnection));
    1104           0 :     *aDecision = ACCEPT;
    1105           0 :     return NS_OK;
    1106             :   }
    1107             : }
    1108             : 
    1109             : NS_IMETHODIMP
    1110           0 : nsMixedContentBlocker::ShouldProcess(uint32_t aContentType,
    1111             :                                      nsIURI* aContentLocation,
    1112             :                                      nsIURI* aRequestingLocation,
    1113             :                                      nsISupports* aRequestingContext,
    1114             :                                      const nsACString& aMimeGuess,
    1115             :                                      nsISupports* aExtra,
    1116             :                                      nsIPrincipal* aRequestPrincipal,
    1117             :                                      int16_t* aDecision)
    1118             : {
    1119           0 :   aContentType = nsContentUtils::InternalContentPolicyTypeToExternal(aContentType);
    1120             : 
    1121           0 :   if (!aContentLocation) {
    1122             :     // aContentLocation may be null when a plugin is loading without an associated URI resource
    1123           0 :     if (aContentType == TYPE_OBJECT) {
    1124           0 :        *aDecision = ACCEPT;
    1125           0 :        return NS_OK;
    1126             :     } else {
    1127           0 :        *aDecision = REJECT_REQUEST;
    1128           0 :        return NS_ERROR_FAILURE;
    1129             :     }
    1130             :   }
    1131             : 
    1132             :   return ShouldLoad(aContentType, aContentLocation, aRequestingLocation,
    1133             :                     aRequestingContext, aMimeGuess, aExtra, aRequestPrincipal,
    1134           0 :                     aDecision);
    1135             : }
    1136             : 
    1137             : // Record information on when HSTS would have made mixed content not mixed
    1138             : // content (regardless of whether it was actually blocked)
    1139             : void
    1140           0 : nsMixedContentBlocker::AccumulateMixedContentHSTS(
    1141             :   nsIURI* aURI, bool aActive, bool aHasHSTSPriming,
    1142             :   const OriginAttributes& aOriginAttributes)
    1143             : {
    1144             :   // This method must only be called in the parent, because
    1145             :   // nsSiteSecurityService is only available in the parent
    1146           0 :   if (!XRE_IsParentProcess()) {
    1147           0 :     MOZ_ASSERT(false);
    1148             :     return;
    1149             :   }
    1150             : 
    1151             :   bool hsts;
    1152             :   nsresult rv;
    1153           0 :   nsCOMPtr<nsISiteSecurityService> sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
    1154           0 :   if (NS_FAILED(rv)) {
    1155           0 :     return;
    1156             :   }
    1157           0 :   rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, 0,
    1158           0 :                         aOriginAttributes, nullptr, nullptr, &hsts);
    1159           0 :   if (NS_FAILED(rv)) {
    1160           0 :     return;
    1161             :   }
    1162             : 
    1163             :   // states: would upgrade, would prime, hsts info cached
    1164             :   // active, passive
    1165             :   //
    1166           0 :   if (!aActive) {
    1167           0 :     if (!hsts) {
    1168             :       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
    1169           0 :                             MCB_HSTS_PASSIVE_NO_HSTS);
    1170           0 :       if (aHasHSTSPriming) {
    1171             :         Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
    1172           0 :                               eMCB_HSTS_PASSIVE_DO_PRIMING);
    1173             :       } else {
    1174             :         Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
    1175           0 :                               eMCB_HSTS_PASSIVE_NO_PRIMING);
    1176             :       }
    1177             :     }
    1178             :     else {
    1179             :       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
    1180           0 :                             MCB_HSTS_PASSIVE_WITH_HSTS);
    1181             :       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
    1182           0 :                             eMCB_HSTS_PASSIVE_WITH_HSTS);
    1183             :     }
    1184             :   } else {
    1185           0 :     if (!hsts) {
    1186             :       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
    1187           0 :                             MCB_HSTS_ACTIVE_NO_HSTS);
    1188           0 :       if (aHasHSTSPriming) {
    1189             :         Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
    1190           0 :                               eMCB_HSTS_ACTIVE_DO_PRIMING);
    1191             :       } else {
    1192             :         Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
    1193           0 :                               eMCB_HSTS_ACTIVE_NO_PRIMING);
    1194             :       }
    1195             :     }
    1196             :     else {
    1197             :       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
    1198           0 :                             MCB_HSTS_ACTIVE_WITH_HSTS);
    1199             :       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING_2,
    1200           0 :                             eMCB_HSTS_ACTIVE_WITH_HSTS);
    1201             :     }
    1202             :   }
    1203             : }
    1204             : 
    1205             : //static
    1206             : nsresult
    1207         187 : nsMixedContentBlocker::MarkLoadInfoForPriming(nsIURI* aURI,
    1208             :                                               nsISupports* aRequestingContext,
    1209             :                                               nsILoadInfo* aLoadInfo)
    1210             : {
    1211             :   nsresult rv;
    1212         187 :   bool sendPriming = false;
    1213         187 :   bool mixedContentWouldBlock = false;
    1214             :   rv = GetHSTSPrimingFromRequestingContext(aURI,
    1215             :                                            aRequestingContext,
    1216             :                                            &sendPriming,
    1217         187 :                                            &mixedContentWouldBlock);
    1218         187 :   NS_ENSURE_SUCCESS(rv, rv);
    1219             : 
    1220         187 :   if (sendPriming) {
    1221           0 :     aLoadInfo->SetHSTSPriming(mixedContentWouldBlock);
    1222             :   }
    1223             : 
    1224         187 :   return NS_OK;
    1225             : }
    1226             : 
    1227             : //static
    1228             : nsresult
    1229         191 : nsMixedContentBlocker::GetHSTSPrimingFromRequestingContext(nsIURI* aURI,
    1230             :     nsISupports* aRequestingContext,
    1231             :     bool* aSendPrimingRequest,
    1232             :     bool* aMixedContentWouldBlock)
    1233             : {
    1234         191 :   *aSendPrimingRequest = false;
    1235         191 :   *aMixedContentWouldBlock = false;
    1236             :   // If we marked for priming, we used the innermost URI, so get that
    1237         382 :   nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
    1238         191 :   if (!innerURI) {
    1239           0 :     NS_ERROR("Can't get innerURI from aContentLocation");
    1240           0 :     return NS_ERROR_CONTENT_BLOCKED;
    1241             :   }
    1242             : 
    1243         191 :   bool isHttp = false;
    1244         191 :   innerURI->SchemeIs("http", &isHttp);
    1245         191 :   if (!isHttp) {
    1246             :     // there is nothign to do
    1247         185 :     return NS_OK;
    1248             :   }
    1249             : 
    1250             :   // If the DocShell was marked for HSTS priming, propagate that to the LoadInfo
    1251          12 :   nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aRequestingContext);
    1252           6 :   if (!docShell) {
    1253           3 :     return NS_OK;
    1254             :   }
    1255           6 :   nsCOMPtr<nsIDocument> document = docShell->GetDocument();
    1256           3 :   if (!document) {
    1257           0 :     return NS_OK;
    1258             :   }
    1259             : 
    1260           3 :   HSTSPrimingState status = document->GetHSTSPrimingStateForLocation(innerURI);
    1261           3 :   if (status != HSTSPrimingState::eNO_HSTS_PRIMING) {
    1262           0 :     *aSendPrimingRequest = (status != HSTSPrimingState::eNO_HSTS_PRIMING);
    1263           0 :     *aMixedContentWouldBlock = (status == HSTSPrimingState::eHSTS_PRIMING_BLOCK);
    1264             :   }
    1265             : 
    1266           3 :   return NS_OK;
    1267           9 : }

Generated by: LCOV version 1.13