Coverage Report

Created: 2018-09-25 14:53

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