content/base/src/nsMixedContentBlocker.cpp

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

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

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

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsMixedContentBlocker.h"
michael@0 7
michael@0 8 #include "nsContentPolicyUtils.h"
michael@0 9 #include "nsThreadUtils.h"
michael@0 10 #include "nsINode.h"
michael@0 11 #include "nsCOMPtr.h"
michael@0 12 #include "nsIDocShell.h"
michael@0 13 #include "nsISecurityEventSink.h"
michael@0 14 #include "nsIWebProgressListener.h"
michael@0 15 #include "nsContentUtils.h"
michael@0 16 #include "nsNetUtil.h"
michael@0 17 #include "nsIRequest.h"
michael@0 18 #include "nsIDocument.h"
michael@0 19 #include "nsIContentViewer.h"
michael@0 20 #include "nsIChannel.h"
michael@0 21 #include "nsIHttpChannel.h"
michael@0 22 #include "mozilla/Preferences.h"
michael@0 23 #include "nsIScriptObjectPrincipal.h"
michael@0 24 #include "nsISecureBrowserUI.h"
michael@0 25 #include "nsIDocumentLoader.h"
michael@0 26 #include "nsIWebNavigation.h"
michael@0 27 #include "nsLoadGroup.h"
michael@0 28 #include "nsIScriptError.h"
michael@0 29
michael@0 30 #include "prlog.h"
michael@0 31
michael@0 32 using namespace mozilla;
michael@0 33
michael@0 34 enum nsMixedContentBlockerMessageType {
michael@0 35 eBlocked = 0x00,
michael@0 36 eUserOverride = 0x01
michael@0 37 };
michael@0 38
michael@0 39 // Is mixed script blocking (fonts, plugin content, scripts, stylesheets,
michael@0 40 // iframes, websockets, XHR) enabled?
michael@0 41 bool nsMixedContentBlocker::sBlockMixedScript = false;
michael@0 42
michael@0 43 // Is mixed display content blocking (images, audio, video, <a ping>) enabled?
michael@0 44 bool nsMixedContentBlocker::sBlockMixedDisplay = false;
michael@0 45
michael@0 46 // Fired at the document that attempted to load mixed content. The UI could
michael@0 47 // handle this event, for example, by displaying an info bar that offers the
michael@0 48 // choice to reload the page with mixed content permitted.
michael@0 49 class nsMixedContentEvent : public nsRunnable
michael@0 50 {
michael@0 51 public:
michael@0 52 nsMixedContentEvent(nsISupports *aContext, MixedContentTypes aType)
michael@0 53 : mContext(aContext), mType(aType)
michael@0 54 {}
michael@0 55
michael@0 56 NS_IMETHOD Run()
michael@0 57 {
michael@0 58 NS_ASSERTION(mContext,
michael@0 59 "You can't call this runnable without a requesting context");
michael@0 60
michael@0 61 // To update the security UI in the tab with the blocked mixed content, call
michael@0 62 // nsISecurityEventSink::OnSecurityChange. You can get to the event sink by
michael@0 63 // calling NS_CP_GetDocShellFromContext on the context, and QI'ing to
michael@0 64 // nsISecurityEventSink.
michael@0 65
michael@0 66
michael@0 67 // Mixed content was allowed and is about to load; get the document and
michael@0 68 // set the approriate flag to true if we are about to load Mixed Active
michael@0 69 // Content.
michael@0 70 nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(mContext);
michael@0 71 if (!docShell) {
michael@0 72 return NS_OK;
michael@0 73 }
michael@0 74 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
michael@0 75 docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
michael@0 76 NS_ASSERTION(sameTypeRoot, "No document shell root tree item from document shell tree item!");
michael@0 77
michael@0 78 // now get the document from sameTypeRoot
michael@0 79 nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(sameTypeRoot);
michael@0 80 NS_ASSERTION(rootDoc, "No root document from document shell root tree item.");
michael@0 81
michael@0 82
michael@0 83 if (mType == eMixedScript) {
michael@0 84 // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
michael@0 85 if (rootDoc->GetHasMixedActiveContentLoaded()) {
michael@0 86 return NS_OK;
michael@0 87 }
michael@0 88 rootDoc->SetHasMixedActiveContentLoaded(true);
michael@0 89
michael@0 90 // Update the security UI in the tab with the allowed mixed active content
michael@0 91 nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell);
michael@0 92 if (eventSink) {
michael@0 93 // If mixed display content is loaded, make sure to include that in the state.
michael@0 94 if (rootDoc->GetHasMixedDisplayContentLoaded()) {
michael@0 95 eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN |
michael@0 96 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
michael@0 97 nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
michael@0 98 } else {
michael@0 99 eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN |
michael@0 100 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
michael@0 101 }
michael@0 102 }
michael@0 103
michael@0 104 } else if (mType == eMixedDisplay) {
michael@0 105 // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
michael@0 106 if (rootDoc->GetHasMixedDisplayContentLoaded()) {
michael@0 107 return NS_OK;
michael@0 108 }
michael@0 109 rootDoc->SetHasMixedDisplayContentLoaded(true);
michael@0 110
michael@0 111 // Update the security UI in the tab with the allowed mixed display content.
michael@0 112 nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell);
michael@0 113 if (eventSink) {
michael@0 114 // If mixed active content is loaded, make sure to include that in the state.
michael@0 115 if (rootDoc->GetHasMixedActiveContentLoaded()) {
michael@0 116 eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN |
michael@0 117 nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
michael@0 118 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
michael@0 119 } else {
michael@0 120 eventSink->OnSecurityChange(mContext, (nsIWebProgressListener::STATE_IS_BROKEN |
michael@0 121 nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
michael@0 122 }
michael@0 123 }
michael@0 124 }
michael@0 125
michael@0 126 return NS_OK;
michael@0 127 }
michael@0 128 private:
michael@0 129 // The requesting context for the content load. Generally, a DOM node from
michael@0 130 // the document that caused the load.
michael@0 131 nsCOMPtr<nsISupports> mContext;
michael@0 132
michael@0 133 // The type of mixed content detected, e.g. active or display
michael@0 134 const MixedContentTypes mType;
michael@0 135 };
michael@0 136
michael@0 137
michael@0 138 nsMixedContentBlocker::nsMixedContentBlocker()
michael@0 139 {
michael@0 140 // Cache the pref for mixed script blocking
michael@0 141 Preferences::AddBoolVarCache(&sBlockMixedScript,
michael@0 142 "security.mixed_content.block_active_content");
michael@0 143
michael@0 144 // Cache the pref for mixed display blocking
michael@0 145 Preferences::AddBoolVarCache(&sBlockMixedDisplay,
michael@0 146 "security.mixed_content.block_display_content");
michael@0 147 }
michael@0 148
michael@0 149 nsMixedContentBlocker::~nsMixedContentBlocker()
michael@0 150 {
michael@0 151 }
michael@0 152
michael@0 153 NS_IMPL_ISUPPORTS(nsMixedContentBlocker, nsIContentPolicy)
michael@0 154
michael@0 155 static void
michael@0 156 LogMixedContentMessage(MixedContentTypes aClassification,
michael@0 157 nsIURI* aContentLocation,
michael@0 158 nsIDocument* aRootDoc,
michael@0 159 nsMixedContentBlockerMessageType aMessageType)
michael@0 160 {
michael@0 161 nsAutoCString messageCategory;
michael@0 162 uint32_t severityFlag;
michael@0 163 nsAutoCString messageLookupKey;
michael@0 164
michael@0 165 if (aMessageType == eBlocked) {
michael@0 166 severityFlag = nsIScriptError::errorFlag;
michael@0 167 messageCategory.AssignLiteral("Mixed Content Blocker");
michael@0 168 if (aClassification == eMixedDisplay) {
michael@0 169 messageLookupKey.AssignLiteral("BlockMixedDisplayContent");
michael@0 170 } else {
michael@0 171 messageLookupKey.AssignLiteral("BlockMixedActiveContent");
michael@0 172 }
michael@0 173 } else {
michael@0 174 severityFlag = nsIScriptError::warningFlag;
michael@0 175 messageCategory.AssignLiteral("Mixed Content Message");
michael@0 176 if (aClassification == eMixedDisplay) {
michael@0 177 messageLookupKey.AssignLiteral("LoadingMixedDisplayContent");
michael@0 178 } else {
michael@0 179 messageLookupKey.AssignLiteral("LoadingMixedActiveContent");
michael@0 180 }
michael@0 181 }
michael@0 182
michael@0 183 nsAutoCString locationSpec;
michael@0 184 aContentLocation->GetSpec(locationSpec);
michael@0 185 NS_ConvertUTF8toUTF16 locationSpecUTF16(locationSpec);
michael@0 186
michael@0 187 const char16_t* strings[] = { locationSpecUTF16.get() };
michael@0 188 nsContentUtils::ReportToConsole(severityFlag, messageCategory, aRootDoc,
michael@0 189 nsContentUtils::eSECURITY_PROPERTIES,
michael@0 190 messageLookupKey.get(), strings, ArrayLength(strings));
michael@0 191 }
michael@0 192
michael@0 193 NS_IMETHODIMP
michael@0 194 nsMixedContentBlocker::ShouldLoad(uint32_t aContentType,
michael@0 195 nsIURI* aContentLocation,
michael@0 196 nsIURI* aRequestingLocation,
michael@0 197 nsISupports* aRequestingContext,
michael@0 198 const nsACString& aMimeGuess,
michael@0 199 nsISupports* aExtra,
michael@0 200 nsIPrincipal* aRequestPrincipal,
michael@0 201 int16_t* aDecision)
michael@0 202 {
michael@0 203 // Asserting that we are on the main thread here and hence do not have to lock
michael@0 204 // and unlock sBlockMixedScript and sBlockMixedDisplay before reading/writing
michael@0 205 // to them.
michael@0 206 MOZ_ASSERT(NS_IsMainThread());
michael@0 207
michael@0 208 // Assume active (high risk) content and blocked by default
michael@0 209 MixedContentTypes classification = eMixedScript;
michael@0 210
michael@0 211
michael@0 212 // Notes on non-obvious decisions:
michael@0 213 //
michael@0 214 // TYPE_DTD: A DTD can contain entity definitions that expand to scripts.
michael@0 215 //
michael@0 216 // TYPE_FONT: The TrueType hinting mechanism is basically a scripting
michael@0 217 // language that gets interpreted by the operating system's font rasterizer.
michael@0 218 // Mixed content web fonts are relatively uncommon, and we can can fall back
michael@0 219 // to built-in fonts with minimal disruption in almost all cases.
michael@0 220 //
michael@0 221 // TYPE_OBJECT_SUBREQUEST could actually be either active content (e.g. a
michael@0 222 // script that a plugin will execute) or display content (e.g. Flash video
michael@0 223 // content). Until we have a way to determine active vs passive content
michael@0 224 // from plugin requests (bug 836352), we will treat this as passive content.
michael@0 225 // This is to prevent false positives from causing users to become
michael@0 226 // desensitized to the mixed content blocker.
michael@0 227 //
michael@0 228 // TYPE_CSP_REPORT: High-risk because they directly leak information about
michael@0 229 // the content of the page, and because blocking them does not have any
michael@0 230 // negative effect on the page loading.
michael@0 231 //
michael@0 232 // TYPE_PING: Ping requests are POSTS, not GETs like images and media.
michael@0 233 // Also, PING requests have no bearing on the rendering or operation of
michael@0 234 // the page when used as designed, so even though they are lower risk than
michael@0 235 // scripts, blocking them is basically risk-free as far as compatibility is
michael@0 236 // concerned. Ping is turned off by default in Firefox, so unless a user
michael@0 237 // opts into ping, no request will be made. Categorizing this as Mixed
michael@0 238 // Display Content for now, but this is subject to change.
michael@0 239 //
michael@0 240 // TYPE_STYLESHEET: XSLT stylesheets can insert scripts. CSS positioning
michael@0 241 // and other advanced CSS features can possibly be exploited to cause
michael@0 242 // spoofing attacks (e.g. make a "grant permission" button look like a
michael@0 243 // "refuse permission" button).
michael@0 244 //
michael@0 245 // TYPE_BEACON: Beacon requests are similar to TYPE_PING, but are default on.
michael@0 246 //
michael@0 247 // TYPE_WEBSOCKET: The Websockets API requires browsers to
michael@0 248 // reject mixed-content websockets: "If secure is false but the origin of
michael@0 249 // the entry script has a scheme component that is itself a secure protocol,
michael@0 250 // e.g. HTTPS, then throw a SecurityError exception." We already block mixed
michael@0 251 // content websockets within the websockets implementation, so we don't need
michael@0 252 // to do any blocking here, nor do we need to provide a way to undo or
michael@0 253 // override the blocking. Websockets without TLS are very flaky anyway in the
michael@0 254 // face of many HTTP-aware proxies. Compared to psasive content, there is
michael@0 255 // additional risk that the script using WebSockets will disclose sensitive
michael@0 256 // information from the HTTPS page and/or eval (directly or indirectly)
michael@0 257 // received data.
michael@0 258 //
michael@0 259 // TYPE_XMLHTTPREQUEST: XHR requires either same origin or CORS, so most
michael@0 260 // mixed-content XHR will already be blocked by that check. This will also
michael@0 261 // block HTTPS-to-HTTP XHR with CORS. The same security concerns mentioned
michael@0 262 // above for WebSockets apply to XHR, and XHR should have the same security
michael@0 263 // properties as WebSockets w.r.t. mixed content. XHR's handling of redirects
michael@0 264 // amplifies these concerns.
michael@0 265
michael@0 266
michael@0 267 static_assert(TYPE_DATAREQUEST == TYPE_XMLHTTPREQUEST,
michael@0 268 "TYPE_DATAREQUEST is not a synonym for "
michael@0 269 "TYPE_XMLHTTPREQUEST");
michael@0 270
michael@0 271 switch (aContentType) {
michael@0 272 // The top-level document cannot be mixed content by definition
michael@0 273 case TYPE_DOCUMENT:
michael@0 274 *aDecision = ACCEPT;
michael@0 275 return NS_OK;
michael@0 276 // Creating insecure websocket connections in a secure page is blocked already
michael@0 277 // in the websocket constructor. We don't need to check the blocking here
michael@0 278 // and we don't want to un-block
michael@0 279 case TYPE_WEBSOCKET:
michael@0 280 *aDecision = ACCEPT;
michael@0 281 return NS_OK;
michael@0 282
michael@0 283
michael@0 284 // Static display content is considered moderate risk for mixed content so
michael@0 285 // these will be blocked according to the mixed display preference
michael@0 286 case TYPE_IMAGE:
michael@0 287 case TYPE_MEDIA:
michael@0 288 case TYPE_OBJECT_SUBREQUEST:
michael@0 289 case TYPE_PING:
michael@0 290 case TYPE_BEACON:
michael@0 291 classification = eMixedDisplay;
michael@0 292 break;
michael@0 293
michael@0 294 // Active content (or content with a low value/risk-of-blocking ratio)
michael@0 295 // that has been explicitly evaluated; listed here for documentation
michael@0 296 // purposes and to avoid the assertion and warning for the default case.
michael@0 297 case TYPE_CSP_REPORT:
michael@0 298 case TYPE_DTD:
michael@0 299 case TYPE_FONT:
michael@0 300 case TYPE_OBJECT:
michael@0 301 case TYPE_SCRIPT:
michael@0 302 case TYPE_STYLESHEET:
michael@0 303 case TYPE_SUBDOCUMENT:
michael@0 304 case TYPE_XBL:
michael@0 305 case TYPE_XMLHTTPREQUEST:
michael@0 306 case TYPE_XSLT:
michael@0 307 case TYPE_OTHER:
michael@0 308 break;
michael@0 309
michael@0 310
michael@0 311 // This content policy works as a whitelist.
michael@0 312 default:
michael@0 313 MOZ_ASSERT(false, "Mixed content of unknown type");
michael@0 314 break;
michael@0 315 }
michael@0 316
michael@0 317 /* Get the scheme of the sub-document resource to be requested. If it is
michael@0 318 * a safe to load in an https context then mixed content doesn't apply.
michael@0 319 *
michael@0 320 * Check Protocol Flags to determine if scheme is safe to load:
michael@0 321 * URI_DOES_NOT_RETURN_DATA - e.g.
michael@0 322 * "mailto"
michael@0 323 * URI_IS_LOCAL_RESOURCE - e.g.
michael@0 324 * "data",
michael@0 325 * "resource",
michael@0 326 * "moz-icon"
michael@0 327 * URI_INHERITS_SECURITY_CONTEXT - e.g.
michael@0 328 * "javascript"
michael@0 329 * URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT - e.g.
michael@0 330 * "https",
michael@0 331 * "moz-safe-about"
michael@0 332 *
michael@0 333 */
michael@0 334 bool schemeLocal = false;
michael@0 335 bool schemeNoReturnData = false;
michael@0 336 bool schemeInherits = false;
michael@0 337 bool schemeSecure = false;
michael@0 338 if (NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE , &schemeLocal)) ||
michael@0 339 NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &schemeNoReturnData)) ||
michael@0 340 NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &schemeInherits)) ||
michael@0 341 NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT, &schemeSecure))) {
michael@0 342 return NS_ERROR_FAILURE;
michael@0 343 }
michael@0 344
michael@0 345 if (schemeLocal || schemeNoReturnData || schemeInherits || schemeSecure) {
michael@0 346 return NS_OK;
michael@0 347 }
michael@0 348
michael@0 349 // Since there are cases where aRequestingLocation and aRequestPrincipal are
michael@0 350 // definitely not the owning document, we try to ignore them by extracting the
michael@0 351 // requestingLocation in the following order:
michael@0 352 // 1) from the aRequestingContext, either extracting
michael@0 353 // a) the node's principal, or the
michael@0 354 // b) script object's principal.
michael@0 355 // 2) if aRequestingContext yields a principal but no location, we check
michael@0 356 // if its the system principal. If it is, allow the load.
michael@0 357 // 3) Special case handling for:
michael@0 358 // a) speculative loads, where shouldLoad is called twice (bug 839235)
michael@0 359 // and the first speculative load does not include a context.
michael@0 360 // In this case we use aRequestingLocation to set requestingLocation.
michael@0 361 // b) TYPE_CSP_REPORT which does not provide a context. In this case we
michael@0 362 // use aRequestingLocation to set requestingLocation.
michael@0 363 // c) content scripts from addon code that do not provide aRequestingContext
michael@0 364 // or aRequestingLocation, but do provide aRequestPrincipal.
michael@0 365 // If aRequestPrincipal is an expanded principal, we allow the load.
michael@0 366 // 4) If we still end up not having a requestingLocation, we reject the load.
michael@0 367
michael@0 368 nsCOMPtr<nsIPrincipal> principal;
michael@0 369 // 1a) Try to get the principal if aRequestingContext is a node.
michael@0 370 nsCOMPtr<nsINode> node = do_QueryInterface(aRequestingContext);
michael@0 371 if (node) {
michael@0 372 principal = node->NodePrincipal();
michael@0 373 }
michael@0 374
michael@0 375 // 1b) Try using the window's script object principal if it's not a node.
michael@0 376 if (!principal) {
michael@0 377 nsCOMPtr<nsIScriptObjectPrincipal> scriptObjPrin = do_QueryInterface(aRequestingContext);
michael@0 378 if (scriptObjPrin) {
michael@0 379 principal = scriptObjPrin->GetPrincipal();
michael@0 380 }
michael@0 381 }
michael@0 382
michael@0 383 nsCOMPtr<nsIURI> requestingLocation;
michael@0 384 if (principal) {
michael@0 385 principal->GetURI(getter_AddRefs(requestingLocation));
michael@0 386 }
michael@0 387
michael@0 388 // 2) if aRequestingContext yields a principal but no location, we check if its a system principal.
michael@0 389 if (principal && !requestingLocation) {
michael@0 390 if (nsContentUtils::IsSystemPrincipal(principal)) {
michael@0 391 *aDecision = ACCEPT;
michael@0 392 return NS_OK;
michael@0 393 }
michael@0 394 }
michael@0 395
michael@0 396 // 3a,b) Special case handling for speculative loads and TYPE_CSP_REPORT. In
michael@0 397 // such cases, aRequestingContext doesn't exist, so we use aRequestingLocation.
michael@0 398 // Unfortunately we can not distinguish between speculative and normal loads here,
michael@0 399 // otherwise we could special case this assignment.
michael@0 400 if (!requestingLocation) {
michael@0 401 requestingLocation = aRequestingLocation;
michael@0 402 }
michael@0 403
michael@0 404 // 3c) Special case handling for content scripts from addons code, which only
michael@0 405 // provide a aRequestPrincipal; aRequestingContext and aRequestingLocation are
michael@0 406 // both null; if the aRequestPrincipal is an expandedPrincipal, we allow the load.
michael@0 407 if (!principal && !requestingLocation && aRequestPrincipal) {
michael@0 408 nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aRequestPrincipal);
michael@0 409 if (expanded) {
michael@0 410 *aDecision = ACCEPT;
michael@0 411 return NS_OK;
michael@0 412 }
michael@0 413 }
michael@0 414
michael@0 415 // 4) Giving up. We still don't have a requesting location, therefore we can't tell
michael@0 416 // if this is a mixed content load. Deny to be safe.
michael@0 417 if (!requestingLocation) {
michael@0 418 *aDecision = REJECT_REQUEST;
michael@0 419 return NS_OK;
michael@0 420 }
michael@0 421
michael@0 422 // Check the parent scheme. If it is not an HTTPS page then mixed content
michael@0 423 // restrictions do not apply.
michael@0 424 bool parentIsHttps;
michael@0 425 nsresult rv = requestingLocation->SchemeIs("https", &parentIsHttps);
michael@0 426 if (NS_FAILED(rv)) {
michael@0 427 NS_ERROR("requestingLocation->SchemeIs failed");
michael@0 428 *aDecision = REJECT_REQUEST;
michael@0 429 return NS_OK;
michael@0 430 }
michael@0 431 if (!parentIsHttps) {
michael@0 432 *aDecision = ACCEPT;
michael@0 433 return NS_OK;
michael@0 434 }
michael@0 435
michael@0 436 // Determine if the rootDoc is https and if the user decided to allow Mixed Content
michael@0 437 nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aRequestingContext);
michael@0 438 NS_ENSURE_TRUE(docShell, NS_OK);
michael@0 439 bool rootHasSecureConnection = false;
michael@0 440 bool allowMixedContent = false;
michael@0 441 bool isRootDocShell = false;
michael@0 442 rv = docShell->GetAllowMixedContentAndConnectionData(&rootHasSecureConnection, &allowMixedContent, &isRootDocShell);
michael@0 443 if (NS_FAILED(rv)) {
michael@0 444 return rv;
michael@0 445 }
michael@0 446
michael@0 447
michael@0 448 // Get the sameTypeRoot tree item from the docshell
michael@0 449 nsCOMPtr<nsIDocShellTreeItem> sameTypeRoot;
michael@0 450 docShell->GetSameTypeRootTreeItem(getter_AddRefs(sameTypeRoot));
michael@0 451 NS_ASSERTION(sameTypeRoot, "No root tree item from docshell!");
michael@0 452
michael@0 453 // When navigating an iframe, the iframe may be https
michael@0 454 // but its parents may not be. Check the parents to see if any of them are https.
michael@0 455 // If none of the parents are https, allow the load.
michael@0 456 if (aContentType == TYPE_SUBDOCUMENT && !rootHasSecureConnection) {
michael@0 457
michael@0 458 bool httpsParentExists = false;
michael@0 459
michael@0 460 nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
michael@0 461 parentTreeItem = docShell;
michael@0 462
michael@0 463 while(!httpsParentExists && parentTreeItem) {
michael@0 464 nsCOMPtr<nsIWebNavigation> parentAsNav(do_QueryInterface(parentTreeItem));
michael@0 465 NS_ASSERTION(parentAsNav, "No web navigation object from parent's docshell tree item");
michael@0 466 nsCOMPtr<nsIURI> parentURI;
michael@0 467
michael@0 468 parentAsNav->GetCurrentURI(getter_AddRefs(parentURI));
michael@0 469 if (!parentURI || NS_FAILED(parentURI->SchemeIs("https", &httpsParentExists))) {
michael@0 470 // if getting the URI or the scheme fails, assume there is a https parent and break.
michael@0 471 httpsParentExists = true;
michael@0 472 break;
michael@0 473 }
michael@0 474
michael@0 475 // When the parent and the root are the same, we have traversed all the way up
michael@0 476 // the same type docshell tree. Break out of the while loop.
michael@0 477 if(sameTypeRoot == parentTreeItem) {
michael@0 478 break;
michael@0 479 }
michael@0 480
michael@0 481 // update the parent to the grandparent.
michael@0 482 nsCOMPtr<nsIDocShellTreeItem> newParentTreeItem;
michael@0 483 parentTreeItem->GetSameTypeParent(getter_AddRefs(newParentTreeItem));
michael@0 484 parentTreeItem = newParentTreeItem;
michael@0 485 } // end while loop.
michael@0 486
michael@0 487 if (!httpsParentExists) {
michael@0 488 *aDecision = nsIContentPolicy::ACCEPT;
michael@0 489 return NS_OK;
michael@0 490 }
michael@0 491 }
michael@0 492
michael@0 493 // Get the root document from the sameTypeRoot
michael@0 494 nsCOMPtr<nsIDocument> rootDoc = do_GetInterface(sameTypeRoot);
michael@0 495 NS_ASSERTION(rootDoc, "No root document from document shell root tree item.");
michael@0 496
michael@0 497 // Get eventSink and the current security state from the docShell
michael@0 498 nsCOMPtr<nsISecurityEventSink> eventSink = do_QueryInterface(docShell);
michael@0 499 NS_ASSERTION(eventSink, "No eventSink from docShell.");
michael@0 500 nsCOMPtr<nsIDocShell> rootShell = do_GetInterface(sameTypeRoot);
michael@0 501 NS_ASSERTION(rootShell, "No root docshell from document shell root tree item.");
michael@0 502 uint32_t State = nsIWebProgressListener::STATE_IS_BROKEN;
michael@0 503 nsCOMPtr<nsISecureBrowserUI> securityUI;
michael@0 504 rootShell->GetSecurityUI(getter_AddRefs(securityUI));
michael@0 505 // If there is no securityUI, document doesn't have a security state.
michael@0 506 // Allow load and return early.
michael@0 507 if (!securityUI) {
michael@0 508 *aDecision = nsIContentPolicy::ACCEPT;
michael@0 509 return NS_OK;
michael@0 510 }
michael@0 511 nsresult stateRV = securityUI->GetState(&State);
michael@0 512
michael@0 513 // If the content is display content, and the pref says display content should be blocked, block it.
michael@0 514 if (sBlockMixedDisplay && classification == eMixedDisplay) {
michael@0 515 if (allowMixedContent) {
michael@0 516 LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride);
michael@0 517 *aDecision = nsIContentPolicy::ACCEPT;
michael@0 518 rootDoc->SetHasMixedActiveContentLoaded(true);
michael@0 519 if (!rootDoc->GetHasMixedDisplayContentLoaded() && NS_SUCCEEDED(stateRV)) {
michael@0 520 rootDoc->SetHasMixedDisplayContentLoaded(true);
michael@0 521 eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
michael@0 522 }
michael@0 523 } else {
michael@0 524 *aDecision = nsIContentPolicy::REJECT_REQUEST;
michael@0 525 LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
michael@0 526 if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) {
michael@0 527 rootDoc->SetHasMixedDisplayContentBlocked(true);
michael@0 528 eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT));
michael@0 529 }
michael@0 530 }
michael@0 531 return NS_OK;
michael@0 532
michael@0 533 } else if (sBlockMixedScript && classification == eMixedScript) {
michael@0 534 // If the content is active content, and the pref says active content should be blocked, block it
michael@0 535 // unless the user has choosen to override the pref
michael@0 536 if (allowMixedContent) {
michael@0 537 LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride);
michael@0 538 *aDecision = nsIContentPolicy::ACCEPT;
michael@0 539 // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
michael@0 540 if (rootDoc->GetHasMixedActiveContentLoaded()) {
michael@0 541 return NS_OK;
michael@0 542 }
michael@0 543 rootDoc->SetHasMixedActiveContentLoaded(true);
michael@0 544
michael@0 545 if (rootHasSecureConnection) {
michael@0 546 // User has decided to override the pref and the root is https, so change the Security State.
michael@0 547 if (rootDoc->GetHasMixedDisplayContentLoaded()) {
michael@0 548 // If mixed display content is loaded, make sure to include that in the state.
michael@0 549 eventSink->OnSecurityChange(aRequestingContext, (nsIWebProgressListener::STATE_IS_BROKEN |
michael@0 550 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
michael@0 551 nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
michael@0 552 } else {
michael@0 553 eventSink->OnSecurityChange(aRequestingContext, (nsIWebProgressListener::STATE_IS_BROKEN |
michael@0 554 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
michael@0 555 }
michael@0 556 return NS_OK;
michael@0 557 } else {
michael@0 558 // User has already overriden the pref and the root is not https;
michael@0 559 // mixed content was allowed on an https subframe.
michael@0 560 if (NS_SUCCEEDED(stateRV)) {
michael@0 561 eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
michael@0 562 }
michael@0 563 return NS_OK;
michael@0 564 }
michael@0 565 } else {
michael@0 566 //User has not overriden the pref by Disabling protection. Reject the request and update the security state.
michael@0 567 *aDecision = nsIContentPolicy::REJECT_REQUEST;
michael@0 568 LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
michael@0 569 // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
michael@0 570 if (rootDoc->GetHasMixedActiveContentBlocked()) {
michael@0 571 return NS_OK;
michael@0 572 }
michael@0 573 rootDoc->SetHasMixedActiveContentBlocked(true);
michael@0 574
michael@0 575 // The user has not overriden the pref, so make sure they still have an option by calling eventSink
michael@0 576 // which will invoke the doorhanger
michael@0 577 if (NS_SUCCEEDED(stateRV)) {
michael@0 578 eventSink->OnSecurityChange(aRequestingContext, (State | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT));
michael@0 579 }
michael@0 580 return NS_OK;
michael@0 581 }
michael@0 582
michael@0 583 } else {
michael@0 584 // The content is not blocked by the mixed content prefs.
michael@0 585
michael@0 586 // Log a message that we are loading mixed content.
michael@0 587 LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride);
michael@0 588
michael@0 589 // Fire the event from a script runner as it is unsafe to run script
michael@0 590 // from within ShouldLoad
michael@0 591 nsContentUtils::AddScriptRunner(
michael@0 592 new nsMixedContentEvent(aRequestingContext, classification));
michael@0 593 return NS_OK;
michael@0 594 }
michael@0 595
michael@0 596 *aDecision = REJECT_REQUEST;
michael@0 597 return NS_OK;
michael@0 598 }
michael@0 599
michael@0 600 NS_IMETHODIMP
michael@0 601 nsMixedContentBlocker::ShouldProcess(uint32_t aContentType,
michael@0 602 nsIURI* aContentLocation,
michael@0 603 nsIURI* aRequestingLocation,
michael@0 604 nsISupports* aRequestingContext,
michael@0 605 const nsACString& aMimeGuess,
michael@0 606 nsISupports* aExtra,
michael@0 607 nsIPrincipal* aRequestPrincipal,
michael@0 608 int16_t* aDecision)
michael@0 609 {
michael@0 610 if (!aContentLocation) {
michael@0 611 // aContentLocation may be null when a plugin is loading without an associated URI resource
michael@0 612 if (aContentType == TYPE_OBJECT) {
michael@0 613 return NS_OK;
michael@0 614 } else {
michael@0 615 return NS_ERROR_FAILURE;
michael@0 616 }
michael@0 617 }
michael@0 618
michael@0 619 return ShouldLoad(aContentType, aContentLocation, aRequestingLocation,
michael@0 620 aRequestingContext, aMimeGuess, aExtra, aRequestPrincipal,
michael@0 621 aDecision);
michael@0 622 }

mercurial