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 : }
|