nsHTTPSOnlyUtils.cpp (45659B)
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 "nsHTTPSOnlyUtils.h" 8 9 #include "mozilla/ClearOnShutdown.h" 10 #include "mozilla/Components.h" 11 #include "mozilla/NullPrincipal.h" 12 #include "mozilla/OriginAttributes.h" 13 #include "mozilla/StaticPrefs_dom.h" 14 #include "mozilla/TimeStamp.h" 15 #include "mozilla/glean/DomSecurityMetrics.h" 16 #include "mozilla/net/DNS.h" 17 #include "nsContentUtils.h" 18 #include "nsDNSPrefetch.h" 19 #include "nsIEffectiveTLDService.h" 20 #include "nsIHttpChannel.h" 21 #include "nsIHttpChannelInternal.h" 22 #include "nsIHttpsOnlyModePermission.h" 23 #include "nsILoadInfo.h" 24 #include "nsIPermissionManager.h" 25 #include "nsIPrincipal.h" 26 #include "nsIRedirectHistoryEntry.h" 27 #include "nsIScriptError.h" 28 #include "nsIURIMutator.h" 29 #include "nsNetUtil.h" 30 #include "prnetdb.h" 31 32 /* static */ 33 nsHTTPSOnlyUtils::UpgradeMode nsHTTPSOnlyUtils::GetUpgradeMode( 34 bool aFromPrivateWindow, 35 nsILoadInfo::SchemelessInputType aSchemelessInputType) { 36 if (mozilla::StaticPrefs::dom_security_https_only_mode() || 37 (aFromPrivateWindow && 38 mozilla::StaticPrefs::dom_security_https_only_mode_pbm())) { 39 return nsHTTPSOnlyUtils::HTTPS_ONLY_MODE; 40 } 41 42 if (mozilla::StaticPrefs::dom_security_https_first() || 43 (aFromPrivateWindow && 44 mozilla::StaticPrefs::dom_security_https_first_pbm())) { 45 return nsHTTPSOnlyUtils::HTTPS_FIRST_MODE; 46 } 47 48 if (mozilla::StaticPrefs::dom_security_https_first_schemeless() && 49 aSchemelessInputType == nsILoadInfo::SchemelessInputTypeSchemeless) { 50 return nsHTTPSOnlyUtils::SCHEMELESS_HTTPS_FIRST_MODE; 51 } 52 53 return NO_UPGRADE_MODE; 54 } 55 56 /* static */ 57 nsHTTPSOnlyUtils::UpgradeMode nsHTTPSOnlyUtils::GetUpgradeMode( 58 nsILoadInfo* aLoadInfo) { 59 bool isPrivateWin = aLoadInfo->GetOriginAttributes().IsPrivateBrowsing(); 60 return GetUpgradeMode(isPrivateWin, aLoadInfo->GetSchemelessInput()); 61 } 62 63 /* static */ 64 void nsHTTPSOnlyUtils::PotentiallyFireHttpRequestToShortenTimout( 65 mozilla::net::DocumentLoadListener* aDocumentLoadListener) { 66 // only send http background request to counter timeouts if the 67 // pref allows us to do that. 68 if (!mozilla::StaticPrefs:: 69 dom_security_https_only_mode_send_http_background_request()) { 70 return; 71 } 72 73 nsCOMPtr<nsIChannel> channel = aDocumentLoadListener->GetChannel(); 74 if (!channel) { 75 return; 76 } 77 78 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); 79 UpgradeMode upgradeMode = GetUpgradeMode(loadInfo); 80 81 // if neither HTTPS-Only nor HTTPS-First mode is enabled, then there is 82 // nothing to do here. 83 if (upgradeMode == NO_UPGRADE_MODE) { 84 return; 85 } 86 87 // if we are not dealing with a top-level load, then there is nothing to do 88 // here. 89 if (loadInfo->GetExternalContentPolicyType() != 90 ExtContentPolicy::TYPE_DOCUMENT) { 91 return; 92 } 93 94 // if the load is exempt, then there is nothing to do here. 95 uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus(); 96 if (httpsOnlyStatus & nsILoadInfo::nsILoadInfo::HTTPS_ONLY_EXEMPT) { 97 return; 98 } 99 100 // if it's not an http channel, then there is nothing to do here. 101 nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel)); 102 if (!httpChannel) { 103 return; 104 } 105 106 // if it's not a GET method, then there is nothing to do here either. 107 nsAutoCString method; 108 (void)httpChannel->GetRequestMethod(method); 109 if (!method.EqualsLiteral("GET")) { 110 return; 111 } 112 113 // if it's already an https channel, then there is nothing to do here. 114 nsCOMPtr<nsIURI> channelURI; 115 channel->GetURI(getter_AddRefs(channelURI)); 116 if (!channelURI->SchemeIs("http")) { 117 return; 118 } 119 120 // Upgrades for custom ports may be disabled in that case 121 // HTTPS-First only applies to standard ports but HTTPS-Only brute forces 122 // all http connections to be https and overrules HTTPS-First. In case 123 // HTTPS-First is enabled, but HTTPS-Only is not enabled, we might return 124 // early if attempting to send a background request to a non standard port. 125 if (!mozilla::StaticPrefs::dom_security_https_first_for_custom_ports() && 126 (upgradeMode == HTTPS_FIRST_MODE || 127 upgradeMode == SCHEMELESS_HTTPS_FIRST_MODE)) { 128 int32_t port = 0; 129 nsresult rv = channelURI->GetPort(&port); 130 int defaultPortforScheme = NS_GetDefaultPort("http"); 131 if (NS_SUCCEEDED(rv) && port != defaultPortforScheme && port != -1) { 132 return; 133 } 134 } 135 136 // Check for general exceptions 137 if (OnionException(channelURI) || LoopbackOrLocalException(channelURI)) { 138 return; 139 } 140 141 RefPtr<nsIRunnable> task = 142 new TestHTTPAnswerRunnable(channelURI, aDocumentLoadListener); 143 NS_DispatchToMainThread(task.forget()); 144 } 145 146 /* static */ 147 bool nsHTTPSOnlyUtils::ShouldUpgradeRequest(nsIURI* aURI, 148 nsILoadInfo* aLoadInfo) { 149 // 1. Check if the HTTPS-Only Mode is even enabled, before we do anything else 150 if (GetUpgradeMode(aLoadInfo) != HTTPS_ONLY_MODE) { 151 return false; 152 } 153 154 // 2. Check for general exceptions 155 if (OnionException(aURI) || LoopbackOrLocalException(aURI)) { 156 return false; 157 } 158 159 // 3. Check if NoUpgrade-flag is set in LoadInfo 160 uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus(); 161 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) { 162 AutoTArray<nsString, 1> params = { 163 NS_ConvertUTF8toUTF16(aURI->GetSpecOrDefault())}; 164 nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyNoUpgradeException", params, 165 nsIScriptError::infoFlag, aLoadInfo, 166 aURI); 167 return false; 168 } 169 170 // All subresources of an exempt triggering principal are also exempt 171 ExtContentPolicyType contentType = aLoadInfo->GetExternalContentPolicyType(); 172 if (contentType != ExtContentPolicy::TYPE_DOCUMENT) { 173 if (!aLoadInfo->TriggeringPrincipal()->IsSystemPrincipal() && 174 TestIfPrincipalIsExempt(aLoadInfo->TriggeringPrincipal(), 175 HTTPS_ONLY_MODE)) { 176 return false; 177 } 178 } 179 180 // We can not upgrade "Save-As" downloads, since we have no way of detecting 181 // if the upgrade failed (Bug 1674859). For now we will just allow the 182 // download, since there will still be a visual warning about the download 183 // being insecure. 184 if (contentType == ExtContentPolicyType::TYPE_SAVEAS_DOWNLOAD) { 185 return false; 186 } 187 188 // We can upgrade the request - let's log it to the console 189 // Appending an 's' to the scheme for the logging. (http -> https) 190 nsAutoCString scheme; 191 aURI->GetScheme(scheme); 192 scheme.AppendLiteral("s"); 193 NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault()); 194 NS_ConvertUTF8toUTF16 reportScheme(scheme); 195 196 bool isSpeculative = aLoadInfo->GetExternalContentPolicyType() == 197 ExtContentPolicy::TYPE_SPECULATIVE; 198 AutoTArray<nsString, 2> params = {reportSpec, reportScheme}; 199 nsHTTPSOnlyUtils::LogLocalizedString( 200 isSpeculative ? "HTTPSOnlyUpgradeSpeculativeConnection" 201 : "HTTPSOnlyUpgradeRequest", 202 params, nsIScriptError::warningFlag, aLoadInfo, aURI); 203 204 // If the status was not determined before, we now indicate that the request 205 // will get upgraded, but no event-listener has been registered yet. 206 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) { 207 httpsOnlyStatus ^= nsILoadInfo::HTTPS_ONLY_UNINITIALIZED; 208 httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED; 209 aLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus); 210 } 211 return true; 212 } 213 214 /* static */ 215 bool nsHTTPSOnlyUtils::ShouldUpgradeWebSocket(nsIURI* aURI, 216 nsILoadInfo* aLoadInfo) { 217 // 1. Check if the HTTPS-Only Mode is even enabled, before we do anything else 218 if (GetUpgradeMode(aLoadInfo) != HTTPS_ONLY_MODE) { 219 return false; 220 } 221 222 // 2. Check for general exceptions 223 if (OnionException(aURI) || LoopbackOrLocalException(aURI)) { 224 return false; 225 } 226 227 // 3. Check if NoUpgrade-flag is set in LoadInfo 228 uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus(); 229 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) { 230 // Let's log to the console, that we didn't upgrade this request 231 AutoTArray<nsString, 1> params = { 232 NS_ConvertUTF8toUTF16(aURI->GetSpecOrDefault())}; 233 nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyNoUpgradeException", params, 234 nsIScriptError::infoFlag, aLoadInfo, 235 aURI); 236 return false; 237 } 238 239 // All subresources of an exempt triggering principal are also exempt. 240 if (!aLoadInfo->TriggeringPrincipal()->IsSystemPrincipal() && 241 TestIfPrincipalIsExempt(aLoadInfo->TriggeringPrincipal(), 242 HTTPS_ONLY_MODE)) { 243 return false; 244 } 245 246 // We can upgrade the request - let's log it to the console 247 // Appending an 's' to the scheme for the logging. (ws -> wss) 248 nsAutoCString scheme; 249 aURI->GetScheme(scheme); 250 scheme.AppendLiteral("s"); 251 NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault()); 252 NS_ConvertUTF8toUTF16 reportScheme(scheme); 253 254 AutoTArray<nsString, 2> params = {reportSpec, reportScheme}; 255 nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyUpgradeRequest", params, 256 nsIScriptError::warningFlag, aLoadInfo, 257 aURI); 258 return true; 259 } 260 261 /* static */ 262 bool nsHTTPSOnlyUtils::IsUpgradeDowngradeEndlessLoop( 263 nsIURI* aOldURI, nsIURI* aNewURI, nsILoadInfo* aLoadInfo, 264 const mozilla::EnumSet<UpgradeDowngradeEndlessLoopOptions>& aOptions) { 265 // 1. Check if the HTTPS-Only/HTTPS-First is even enabled, before doing 266 // anything else 267 UpgradeMode upgradeMode = GetUpgradeMode(aLoadInfo); 268 bool enforceForHTTPSOnlyMode = 269 upgradeMode == HTTPS_ONLY_MODE && 270 aOptions.contains( 271 UpgradeDowngradeEndlessLoopOptions::EnforceForHTTPSOnlyMode); 272 bool enforceForHTTPSFirstMode = 273 upgradeMode == HTTPS_FIRST_MODE && 274 aOptions.contains( 275 UpgradeDowngradeEndlessLoopOptions::EnforceForHTTPSFirstMode); 276 bool enforceForHTTPSRR = 277 aOptions.contains(UpgradeDowngradeEndlessLoopOptions::EnforceForHTTPSRR); 278 if (!enforceForHTTPSOnlyMode && !enforceForHTTPSFirstMode && 279 !enforceForHTTPSRR) { 280 return false; 281 } 282 283 // 2. Check if the upgrade downgrade pref even wants us to try to break the 284 // cycle. In the case that HTTPS RR is presented, we ignore this pref. 285 if (!mozilla::StaticPrefs:: 286 dom_security_https_only_mode_break_upgrade_downgrade_endless_loop() && 287 !enforceForHTTPSRR) { 288 return false; 289 } 290 291 // 3. If it's not a top-level load, then there is nothing to do here either. 292 if (aLoadInfo->GetExternalContentPolicyType() != 293 ExtContentPolicy::TYPE_DOCUMENT) { 294 return false; 295 } 296 297 // 4. If the load is exempt, then it's defintely not related to https-only 298 uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus(); 299 if ((httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) && 300 !enforceForHTTPSRR) { 301 return false; 302 } 303 304 // 5. Check HTTP 3xx redirects. If the Principal that kicked off the 305 // load/redirect is not https, then it's definitely not a redirect cause by 306 // https-only. If the scheme of the principal however is https and the 307 // asciiHost of the URI to be loaded and the asciiHost of the Principal are 308 // identical, then we are dealing with an upgrade downgrade scenario and we 309 // have to break the cycle. 310 if (IsHttpDowngrade(aOldURI, aNewURI)) { 311 return true; 312 } 313 314 // TODO(Bug 1896691): Don't depend on triggeringPrincipal for JS/Meta 315 // redirects. Call this function at the correct places instead 316 317 // 6. Bug 1725026: Disable JS/Meta loop detection when the load was triggered 318 // by a user gesture. 319 if (aLoadInfo->GetHasValidUserGestureActivation()) { 320 return false; 321 } 322 323 // 7. Meta redirects and JS based redirects (win.location). We detect them 324 // during the https upgrade internal redirect. 325 nsCOMPtr<nsIPrincipal> triggeringPrincipal = aLoadInfo->TriggeringPrincipal(); 326 if (!triggeringPrincipal->SchemeIs("https")) { 327 return false; 328 } 329 330 // We detect Meta and JS based redirects during the upgrade. Check whether 331 // we are currently in an upgrade situation here. 332 if (!IsHttpDowngrade(aNewURI, aOldURI)) { 333 return false; 334 } 335 // If we upgrade to the same URI that the load is origining from we are 336 // creating a redirect loop. 337 bool isLoop = false; 338 nsresult rv = triggeringPrincipal->EqualsURI(aNewURI, &isLoop); 339 NS_ENSURE_SUCCESS(rv, false); 340 return isLoop; 341 } 342 343 /* static */ 344 bool nsHTTPSOnlyUtils::ShouldUpgradeHttpsFirstRequest(nsIURI* aURI, 345 nsILoadInfo* aLoadInfo) { 346 MOZ_ASSERT(aURI->SchemeIs("http"), "how come the request is not 'http'?"); 347 348 // 1. Check if HTTPS-First Mode is enabled 349 UpgradeMode upgradeMode = GetUpgradeMode(aLoadInfo); 350 if (upgradeMode != HTTPS_FIRST_MODE && 351 upgradeMode != SCHEMELESS_HTTPS_FIRST_MODE) { 352 return false; 353 } 354 // 2. HTTPS-First only upgrades top-level loads (and speculative connections) 355 ExtContentPolicyType contentType = aLoadInfo->GetExternalContentPolicyType(); 356 if (contentType != ExtContentPolicy::TYPE_DOCUMENT && 357 contentType != ExtContentPolicy::TYPE_SPECULATIVE) { 358 return false; 359 } 360 361 // 3. Check for general exceptions 362 if (OnionException(aURI) || 363 (!mozilla::StaticPrefs::dom_security_https_first_for_local_addresses() && 364 LoopbackOrLocalException(aURI)) || 365 (!mozilla::StaticPrefs::dom_security_https_first_for_unknown_suffixes() && 366 UnknownPublicSuffixException(aURI))) { 367 return false; 368 } 369 370 // 4. Don't upgrade if upgraded previously or exempt from upgrades 371 uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus(); 372 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) { 373 return false; 374 } 375 376 // 5. Don't upgrade if the user explicitly provided a scheme 377 if (aLoadInfo->GetSchemelessInput() == 378 nsILoadInfo::SchemelessInputTypeSchemeful && 379 aLoadInfo->GetExternalContentPolicyType() != 380 ExtContentPolicy::TYPE_SPECULATIVE && 381 aURI->SchemeIs("http")) { 382 AddHTTPSFirstException(aURI, aLoadInfo); 383 return false; 384 } 385 386 // 6. Make sure HTTPS-First does not upgrade custom ports when it is disabled 387 if (!mozilla::StaticPrefs::dom_security_https_first_for_custom_ports()) { 388 int defaultPortforScheme = NS_GetDefaultPort("http"); 389 // If no port is specified, then the API returns -1 to indicate the default 390 // port. 391 int32_t port = 0; 392 nsresult rv = aURI->GetPort(&port); 393 NS_ENSURE_SUCCESS(rv, false); 394 if (port != defaultPortforScheme && port != -1) { 395 return false; 396 } 397 } 398 399 // 7. Do not upgrade requests other than GET 400 if (!aLoadInfo->GetIsGETRequest()) { 401 return false; 402 } 403 404 // We can upgrade the request - let's log to the console and set the status 405 // so we know that we upgraded the request. 406 if (upgradeMode == SCHEMELESS_HTTPS_FIRST_MODE) { 407 nsAutoCString urlCString; 408 aURI->GetSpec(urlCString); 409 NS_ConvertUTF8toUTF16 urlString(urlCString); 410 411 AutoTArray<nsString, 1> params = {urlString}; 412 nsHTTPSOnlyUtils::LogLocalizedString("HTTPSFirstSchemeless", params, 413 nsIScriptError::warningFlag, aLoadInfo, 414 aURI, true); 415 } else { 416 nsAutoCString scheme; 417 418 aURI->GetScheme(scheme); 419 scheme.AppendLiteral("s"); 420 NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault()); 421 NS_ConvertUTF8toUTF16 reportScheme(scheme); 422 423 bool isSpeculative = contentType == ExtContentPolicy::TYPE_SPECULATIVE; 424 AutoTArray<nsString, 2> params = {reportSpec, reportScheme}; 425 nsHTTPSOnlyUtils::LogLocalizedString( 426 isSpeculative ? "HTTPSOnlyUpgradeSpeculativeConnection" 427 : "HTTPSOnlyUpgradeRequest", 428 params, nsIScriptError::warningFlag, aLoadInfo, aURI, true); 429 } 430 431 // Set flag so we know that we upgraded the request 432 httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST; 433 aLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus); 434 return true; 435 } 436 437 /* static */ 438 already_AddRefed<nsIURI> 439 nsHTTPSOnlyUtils::PotentiallyDowngradeHttpsFirstRequest( 440 mozilla::net::DocumentLoadListener* aDocumentLoadListener, 441 nsresult aStatus) { 442 nsCOMPtr<nsIChannel> channel = aDocumentLoadListener->GetChannel(); 443 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); 444 uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus(); 445 // Only downgrade if we this request was upgraded using HTTPS-First Mode 446 if (!(httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST)) { 447 return nullptr; 448 } 449 // Once loading is in progress we set that flag so that timeout counter 450 // measures do not kick in. 451 loadInfo->SetHttpsOnlyStatus( 452 httpsOnlyStatus | nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS); 453 454 nsresult status = aStatus; 455 // Since 4xx and 5xx errors return NS_OK instead of NS_ERROR_*, we need 456 // to check each NS_OK for those errors. 457 // Only downgrade an NS_OK status if it is an 4xx or 5xx error. 458 if (NS_SUCCEEDED(aStatus)) { 459 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel); 460 // If no httpChannel exists we have nothing to do here. 461 if (!httpChannel) { 462 return nullptr; 463 } 464 uint32_t responseStatus = 0; 465 if (NS_FAILED(httpChannel->GetResponseStatus(&responseStatus))) { 466 return nullptr; 467 } 468 469 // In case we found one 4xx or 5xx error we need to log it later on, 470 // for that reason we flip the nsresult 'status' from 'NS_OK' to the 471 // corresponding NS_ERROR_*. 472 // To do so we convert the response status to an nsresult error 473 // Every NS_OK that is NOT an 4xx or 5xx error code won't get downgraded. 474 if (responseStatus >= 400 && responseStatus < 600) { 475 // HttpProxyResponseToErrorCode() maps 400 and 404 on 476 // the same error as a 500 status which would lead to no downgrade 477 // later on. For that reason we explicit filter for 400 and 404 status 478 // codes to log them correctly and to downgrade them if possible. 479 switch (responseStatus) { 480 case 400: 481 status = NS_ERROR_PROXY_BAD_REQUEST; 482 break; 483 case 404: 484 status = NS_ERROR_PROXY_NOT_FOUND; 485 break; 486 default: 487 status = mozilla::net::HttpProxyResponseToErrorCode(responseStatus); 488 break; 489 } 490 } 491 if (NS_SUCCEEDED(status)) { 492 return nullptr; 493 } 494 } 495 496 // We're only downgrading if it's possible that the error was 497 // caused by the upgrade. 498 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal( 499 do_QueryInterface(channel)); 500 if (!httpChannelInternal) { 501 return nullptr; 502 } 503 bool proxyUsed = false; 504 nsresult rv = httpChannelInternal->GetIsProxyUsed(&proxyUsed); 505 MOZ_ASSERT(NS_SUCCEEDED(rv)); 506 if (!(proxyUsed && status == nsresult::NS_ERROR_UNKNOWN_HOST) 507 // When a proxy returns an error code it is converted by 508 // HttpProxyResponseToErrorCode. We do want to downgrade in 509 // that case. If the host is actually unreachable this will 510 // show the same error page, but technically for the HTTP 511 // site not the HTTPS site. 512 && HttpsUpgradeUnrelatedErrorCode(status)) { 513 return nullptr; 514 } 515 516 nsCOMPtr<nsIURI> uri; 517 rv = channel->GetURI(getter_AddRefs(uri)); 518 NS_ENSURE_SUCCESS(rv, nullptr); 519 520 nsAutoCString spec; 521 nsCOMPtr<nsIURI> newURI; 522 523 // Only downgrade if the current scheme is (a) https or (b) view-source:https 524 if (uri->SchemeIs("https")) { 525 rv = uri->GetSpec(spec); 526 NS_ENSURE_SUCCESS(rv, nullptr); 527 528 rv = NS_NewURI(getter_AddRefs(newURI), spec); 529 NS_ENSURE_SUCCESS(rv, nullptr); 530 531 rv = NS_MutateURI(newURI).SetScheme("http"_ns).Finalize( 532 getter_AddRefs(newURI)); 533 NS_ENSURE_SUCCESS(rv, nullptr); 534 } else if (uri->SchemeIs("view-source")) { 535 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(uri); 536 if (!nestedURI) { 537 return nullptr; 538 } 539 nsCOMPtr<nsIURI> innerURI; 540 rv = nestedURI->GetInnerURI(getter_AddRefs(innerURI)); 541 NS_ENSURE_SUCCESS(rv, nullptr); 542 if (!innerURI || !innerURI->SchemeIs("https")) { 543 return nullptr; 544 } 545 rv = NS_MutateURI(innerURI).SetScheme("http"_ns).Finalize( 546 getter_AddRefs(innerURI)); 547 NS_ENSURE_SUCCESS(rv, nullptr); 548 549 nsAutoCString innerSpec; 550 rv = innerURI->GetSpec(innerSpec); 551 NS_ENSURE_SUCCESS(rv, nullptr); 552 553 spec.Append("view-source:"); 554 spec.Append(innerSpec); 555 556 rv = NS_NewURI(getter_AddRefs(newURI), spec); 557 NS_ENSURE_SUCCESS(rv, nullptr); 558 } else { 559 return nullptr; 560 } 561 562 // Log downgrade to console 563 NS_ConvertUTF8toUTF16 reportSpec(uri->GetSpecOrDefault()); 564 AutoTArray<nsString, 1> params = {reportSpec}; 565 nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyFailedDowngradeAgain", params, 566 nsIScriptError::warningFlag, loadInfo, 567 uri, true); 568 569 if (mozilla::StaticPrefs:: 570 dom_security_https_first_add_exception_on_failure()) { 571 AddHTTPSFirstException(uri, loadInfo); 572 } 573 574 return newURI.forget(); 575 } 576 577 void nsHTTPSOnlyUtils::UpdateLoadStateAfterHTTPSFirstDowngrade( 578 mozilla::net::DocumentLoadListener* aDocumentLoadListener, 579 nsDocShellLoadState* aLoadState) { 580 // We have to exempt the load from HTTPS-First to prevent a upgrade-downgrade 581 // loop 582 aLoadState->SetIsExemptFromHTTPSFirstMode(true); 583 584 // we can safely set the flag to indicate the downgrade here and it will be 585 // propagated all the way to nsHttpChannel::OnStopRequest() where we collect 586 // the telemetry. 587 nsCOMPtr<nsIChannel> channel = aDocumentLoadListener->GetChannel(); 588 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); 589 if (loadInfo->GetSchemelessInput() == 590 nsILoadInfo::SchemelessInputTypeSchemeless) { 591 aLoadState->SetHttpsUpgradeTelemetry( 592 nsILoadInfo::HTTPS_FIRST_SCHEMELESS_UPGRADE_DOWNGRADE); 593 } else { 594 aLoadState->SetHttpsUpgradeTelemetry( 595 nsILoadInfo::HTTPS_FIRST_UPGRADE_DOWNGRADE); 596 } 597 598 // Add downgrade data for later telemetry usage to load state 599 nsDOMNavigationTiming* timing = aDocumentLoadListener->GetTiming(); 600 if (timing) { 601 mozilla::TimeStamp navigationStart = timing->GetNavigationStartTimeStamp(); 602 if (navigationStart) { 603 mozilla::TimeDuration duration = 604 mozilla::TimeStamp::Now() - navigationStart; 605 606 nsresult channelStatus; 607 channel->GetStatus(&channelStatus); 608 609 RefPtr downgradeData = mozilla::MakeRefPtr<HTTPSFirstDowngradeData>(); 610 downgradeData->downgradeTime = duration; 611 downgradeData->isOnTimer = channelStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL; 612 downgradeData->isSchemeless = 613 GetUpgradeMode(loadInfo) == SCHEMELESS_HTTPS_FIRST_MODE; 614 aLoadState->SetHttpsFirstDowngradeData(downgradeData); 615 } 616 } 617 } 618 619 void nsHTTPSOnlyUtils::SubmitHTTPSFirstTelemetry( 620 nsCOMPtr<nsILoadInfo> const& aLoadInfo, 621 RefPtr<HTTPSFirstDowngradeData> const& aHttpsFirstDowngradeData) { 622 using namespace mozilla::glean::httpsfirst; 623 if (aHttpsFirstDowngradeData) { 624 // Successfully downgraded load 625 626 auto downgradedMetric = aHttpsFirstDowngradeData->isSchemeless 627 ? downgraded_schemeless 628 : downgraded; 629 auto downgradedOnTimerMetric = aHttpsFirstDowngradeData->isSchemeless 630 ? downgraded_on_timer_schemeless 631 : downgraded_on_timer; 632 auto downgradeTimeMetric = aHttpsFirstDowngradeData->isSchemeless 633 ? downgrade_time_schemeless 634 : downgrade_time; 635 636 if (aHttpsFirstDowngradeData->isOnTimer) { 637 downgradedOnTimerMetric.AddToNumerator(); 638 } 639 downgradedMetric.Add(); 640 downgradeTimeMetric.AccumulateRawDuration( 641 aHttpsFirstDowngradeData->downgradeTime); 642 } else if (aLoadInfo->GetHttpsOnlyStatus() & 643 nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST) { 644 // Successfully upgraded load 645 646 if (GetUpgradeMode(aLoadInfo) == SCHEMELESS_HTTPS_FIRST_MODE) { 647 upgraded_schemeless.Add(); 648 } else { 649 upgraded.Add(); 650 } 651 } 652 } 653 654 /* static */ 655 bool nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(nsIChannel* aChannel, 656 nsresult aError) { 657 // If there is no failed channel, then there is nothing to do here. 658 if (!aChannel) { 659 return false; 660 } 661 662 // If HTTPS-Only Mode is not enabled, then there is nothing to do here. 663 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 664 if (GetUpgradeMode(loadInfo) != HTTPS_ONLY_MODE) { 665 return false; 666 } 667 668 // If the load is exempt or did not get upgraded, 669 // then there is nothing to do here. 670 uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus(); 671 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT || 672 httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) { 673 return false; 674 } 675 676 // If it's one of those errors, then most likely it's not a HTTPS-Only error 677 // (This list of errors is largely drawn from nsDocShell::DisplayLoadError()) 678 return !HttpsUpgradeUnrelatedErrorCode(aError); 679 } 680 681 /* static */ 682 bool nsHTTPSOnlyUtils::TestIfPrincipalIsExempt(nsIPrincipal* aPrincipal, 683 UpgradeMode aUpgradeMode) { 684 static nsCOMPtr<nsIPermissionManager> sPermMgr; 685 if (!sPermMgr) { 686 sPermMgr = mozilla::components::PermissionManager::Service(); 687 mozilla::ClearOnShutdown(&sPermMgr); 688 } 689 NS_ENSURE_TRUE(sPermMgr, false); 690 691 uint32_t perm; 692 nsresult rv = sPermMgr->TestExactPermissionFromPrincipal( 693 aPrincipal, "https-only-load-insecure"_ns, &perm); 694 NS_ENSURE_SUCCESS(rv, false); 695 696 bool checkForHTTPSFirst = aUpgradeMode == HTTPS_FIRST_MODE || 697 aUpgradeMode == SCHEMELESS_HTTPS_FIRST_MODE; 698 699 return perm == nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW || 700 perm == nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW_SESSION || 701 (checkForHTTPSFirst && 702 perm == nsIHttpsOnlyModePermission::HTTPSFIRST_LOAD_INSECURE_ALLOW); 703 } 704 705 /* static */ 706 void nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption( 707 nsIChannel* aChannel) { 708 NS_ENSURE_TRUE_VOID(aChannel); 709 710 // If HTTPS-Only or HTTPS-First Mode is not enabled, then there is nothing to 711 // do here. 712 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 713 UpgradeMode upgradeMode = GetUpgradeMode(loadInfo); 714 715 if (upgradeMode == NO_UPGRADE_MODE) { 716 return; 717 } 718 719 // if it's not a top-level load then there is nothing to here. 720 ExtContentPolicyType type = loadInfo->GetExternalContentPolicyType(); 721 if (type != ExtContentPolicy::TYPE_DOCUMENT) { 722 return; 723 } 724 725 // it it's not an http channel, then there is nothing to do here. 726 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel); 727 if (!httpChannel) { 728 return; 729 } 730 731 nsCOMPtr<nsIPrincipal> principal; 732 nsresult rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal( 733 aChannel, getter_AddRefs(principal)); 734 NS_ENSURE_SUCCESS_VOID(rv); 735 736 uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus(); 737 bool isPrincipalExempt = TestIfPrincipalIsExempt(principal, upgradeMode); 738 if (isPrincipalExempt) { 739 httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT; 740 } 741 loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus); 742 743 // For the telemetry we do not want downgrade values to be overwritten 744 // in the loadinfo. We only want e.g. a reload() or a back() click 745 // to carry the upgrade exception. 746 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) { 747 nsILoadInfo::HTTPSUpgradeTelemetryType httpsTelemetry = 748 nsILoadInfo::NOT_INITIALIZED; 749 loadInfo->GetHttpsUpgradeTelemetry(&httpsTelemetry); 750 if (httpsTelemetry != nsILoadInfo::HTTPS_ONLY_UPGRADE_DOWNGRADE && 751 httpsTelemetry != nsILoadInfo::HTTPS_FIRST_UPGRADE_DOWNGRADE && 752 httpsTelemetry != 753 nsILoadInfo::HTTPS_FIRST_SCHEMELESS_UPGRADE_DOWNGRADE) { 754 loadInfo->SetHttpsUpgradeTelemetry(nsILoadInfo::UPGRADE_EXCEPTION); 755 } 756 } 757 } 758 759 /* static */ 760 bool nsHTTPSOnlyUtils::IsSafeToAcceptCORSOrMixedContent( 761 nsILoadInfo* aLoadInfo) { 762 // Check if the request is exempt from upgrades 763 if ((aLoadInfo->GetHttpsOnlyStatus() & nsILoadInfo::HTTPS_ONLY_EXEMPT)) { 764 return false; 765 } 766 // Check if HTTPS-Only Mode is enabled for this request 767 return GetUpgradeMode(aLoadInfo) == HTTPS_ONLY_MODE; 768 } 769 770 /* static */ 771 bool nsHTTPSOnlyUtils::HttpsUpgradeUnrelatedErrorCode(nsresult aError) { 772 return NS_ERROR_UNKNOWN_PROTOCOL == aError || 773 NS_ERROR_FILE_NOT_FOUND == aError || 774 NS_ERROR_FILE_ACCESS_DENIED == aError || 775 NS_ERROR_UNKNOWN_HOST == aError || NS_ERROR_PHISHING_URI == aError || 776 NS_ERROR_MALWARE_URI == aError || NS_ERROR_UNWANTED_URI == aError || 777 NS_ERROR_HARMFUL_URI == aError || NS_ERROR_CONTENT_CRASHED == aError || 778 NS_ERROR_FRAME_CRASHED == aError; 779 } 780 781 /* ------ Logging ------ */ 782 783 /* static */ 784 void nsHTTPSOnlyUtils::LogLocalizedString(const char* aName, 785 const nsTArray<nsString>& aParams, 786 uint32_t aFlags, 787 nsILoadInfo* aLoadInfo, nsIURI* aURI, 788 bool aUseHttpsFirst) { 789 nsAutoString logMsg; 790 nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES, 791 aName, aParams, logMsg); 792 LogMessage(logMsg, aFlags, aLoadInfo, aURI, aUseHttpsFirst); 793 } 794 795 /* static */ 796 void nsHTTPSOnlyUtils::LogMessage(const nsAString& aMessage, uint32_t aFlags, 797 nsILoadInfo* aLoadInfo, nsIURI* aURI, 798 bool aUseHttpsFirst) { 799 // do not log to the console if the loadinfo says we should not! 800 uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus(); 801 if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE) { 802 return; 803 } 804 805 // Prepending HTTPS-Only to the outgoing console message 806 nsString message; 807 message.Append(aUseHttpsFirst ? u"HTTPS-First Mode: "_ns 808 : u"HTTPS-Only Mode: "_ns); 809 message.Append(aMessage); 810 811 // Allow for easy distinction in devtools code. 812 auto category = aUseHttpsFirst ? "HTTPSFirst"_ns : "HTTPSOnly"_ns; 813 814 uint64_t windowId = aLoadInfo->GetInnerWindowID(); 815 if (!windowId) { 816 windowId = aLoadInfo->GetTriggeringWindowId(); 817 } 818 if (windowId) { 819 // Send to content console 820 nsContentUtils::ReportToConsoleByWindowID( 821 message, aFlags, category, windowId, mozilla::SourceLocation(aURI)); 822 } else { 823 // Send to browser console 824 bool isPrivateWin = aLoadInfo->GetOriginAttributes().IsPrivateBrowsing(); 825 nsContentUtils::LogSimpleConsoleError(message, category, isPrivateWin, 826 true /* from chrome context */, 827 aFlags); 828 } 829 } 830 831 /* ------ Exceptions ------ */ 832 833 /* static */ 834 bool nsHTTPSOnlyUtils::OnionException(nsIURI* aURI) { 835 // Onion-host exception can get disabled with a pref 836 if (mozilla::StaticPrefs::dom_security_https_only_mode_upgrade_onion()) { 837 return false; 838 } 839 nsAutoCString host; 840 aURI->GetHost(host); 841 return StringEndsWith(host, ".onion"_ns); 842 } 843 844 /* static */ 845 bool nsHTTPSOnlyUtils::LoopbackOrLocalException(nsIURI* aURI) { 846 nsAutoCString asciiHost; 847 nsresult rv = aURI->GetAsciiHost(asciiHost); 848 NS_ENSURE_SUCCESS(rv, false); 849 850 // Let's make a quick check if the host matches these loopback strings 851 // before we do anything else 852 if (asciiHost.EqualsLiteral("localhost") || asciiHost.EqualsLiteral("::1")) { 853 return true; 854 } 855 856 mozilla::net::NetAddr addr; 857 if (NS_FAILED(addr.InitFromString(asciiHost))) { 858 return false; 859 } 860 // Loopback IPs are always exempt 861 if (addr.IsLoopbackAddr()) { 862 return true; 863 } 864 865 // Local IP exception can get disabled with a pref 866 bool upgradeLocal = 867 mozilla::StaticPrefs::dom_security_https_only_mode_upgrade_local(); 868 return (!upgradeLocal && addr.IsIPAddrLocal()); 869 } 870 871 /* static */ 872 bool nsHTTPSOnlyUtils::UnknownPublicSuffixException(nsIURI* aURI) { 873 nsCOMPtr<nsIEffectiveTLDService> tldService = 874 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); 875 NS_ENSURE_TRUE(tldService, false); 876 877 bool hasKnownPublicSuffix; 878 nsresult rv = tldService->HasKnownPublicSuffix(aURI, &hasKnownPublicSuffix); 879 NS_ENSURE_SUCCESS(rv, false); 880 881 return !hasKnownPublicSuffix; 882 } 883 884 /* static */ 885 bool nsHTTPSOnlyUtils::IsHttpDowngrade(nsIURI* aFromURI, nsIURI* aToURI) { 886 MOZ_ASSERT(aFromURI); 887 MOZ_ASSERT(aToURI); 888 889 if (!aFromURI || !aToURI) { 890 return false; 891 } 892 893 // 2. If the target URI is not http, then it's not a http downgrade 894 if (!aToURI->SchemeIs("http")) { 895 return false; 896 } 897 898 // 3. If the origin URI isn't https, then it's not a http downgrade either. 899 if (!aFromURI->SchemeIs("https")) { 900 return false; 901 } 902 903 // 4. Create a new target URI with 'https' instead of 'http' and compare it 904 // to the origin URI 905 int32_t port = 0; 906 nsresult rv = aToURI->GetPort(&port); 907 NS_ENSURE_SUCCESS(rv, false); 908 // a port of -1 indicates the default port, hence we upgrade from port 80 to 909 // port 443 910 // otherwise we keep the port. 911 if (port == -1) { 912 port = NS_GetDefaultPort("https"); 913 } 914 nsCOMPtr<nsIURI> newHTTPSchemeURI; 915 rv = NS_MutateURI(aToURI) 916 .SetScheme("https"_ns) 917 .SetPort(port) 918 .Finalize(newHTTPSchemeURI); 919 NS_ENSURE_SUCCESS(rv, false); 920 921 bool uriEquals = false; 922 if (NS_FAILED(aFromURI->EqualsExceptRef(newHTTPSchemeURI, &uriEquals))) { 923 return false; 924 } 925 926 return uriEquals; 927 } 928 929 /* static */ 930 nsresult nsHTTPSOnlyUtils::AddHTTPSFirstException( 931 nsCOMPtr<nsIURI> aURI, nsILoadInfo* const aLoadInfo) { 932 // We need to reconstruct a principal instead of taking one from the loadinfo, 933 // as the permission needs a http scheme, while the passed URL or principals 934 // on the loadinfo may have a https scheme. 935 nsresult rv = 936 NS_MutateURI(aURI).SetScheme("http"_ns).Finalize(getter_AddRefs(aURI)); 937 NS_ENSURE_SUCCESS(rv, rv); 938 939 mozilla::OriginAttributes oa = aLoadInfo->GetOriginAttributes(); 940 oa.SetFirstPartyDomain(true, aURI); 941 942 nsCOMPtr<nsIPermissionManager> permMgr = 943 mozilla::components::PermissionManager::Service(); 944 NS_ENSURE_TRUE(permMgr, nsresult::NS_ERROR_SERVICE_NOT_AVAILABLE); 945 946 nsCOMPtr<nsIPrincipal> principal = 947 mozilla::BasePrincipal::CreateContentPrincipal(aURI, oa); 948 949 nsCString host; 950 aURI->GetHost(host); 951 LogLocalizedString("HTTPSFirstAddingException", {NS_ConvertUTF8toUTF16(host)}, 952 nsIScriptError::warningFlag, aLoadInfo, aURI, true); 953 954 uint32_t lifetime = 955 mozilla::StaticPrefs::dom_security_https_first_exception_lifetime(); 956 int64_t expirationTime = (PR_Now() / PR_USEC_PER_MSEC) + lifetime; 957 rv = permMgr->AddFromPrincipal( 958 principal, "https-only-load-insecure"_ns, 959 nsIHttpsOnlyModePermission::HTTPSFIRST_LOAD_INSECURE_ALLOW, 960 nsIPermissionManager::EXPIRE_TIME, expirationTime); 961 NS_ENSURE_SUCCESS(rv, rv); 962 963 return NS_OK; 964 } 965 966 /* static */ 967 uint32_t nsHTTPSOnlyUtils::GetStatusForSubresourceLoad( 968 uint32_t aHttpsOnlyStatus) { 969 return aHttpsOnlyStatus & ~nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST; 970 } 971 972 ///////////////////////////////////////////////////////////////////// 973 // Implementation of TestHTTPAnswerRunnable 974 975 NS_IMPL_ISUPPORTS_INHERITED(TestHTTPAnswerRunnable, mozilla::Runnable, 976 nsIStreamListener, nsIInterfaceRequestor, 977 nsITimerCallback) 978 979 TestHTTPAnswerRunnable::TestHTTPAnswerRunnable( 980 nsIURI* aURI, mozilla::net::DocumentLoadListener* aDocumentLoadListener) 981 : mozilla::Runnable("TestHTTPAnswerRunnable"), 982 mURI(aURI), 983 mDocumentLoadListener(aDocumentLoadListener) {} 984 985 /* static */ 986 bool TestHTTPAnswerRunnable::IsBackgroundRequestRedirected( 987 nsIHttpChannel* aChannel) { 988 // If there is no background request (aChannel), then there is nothing 989 // to do here. 990 if (!aChannel) { 991 return false; 992 } 993 // If the request was not redirected, then there is nothing to do here. 994 nsCOMPtr<nsILoadInfo> loadinfo = aChannel->LoadInfo(); 995 if (loadinfo->RedirectChain().IsEmpty()) { 996 return false; 997 } 998 999 // If the final URI is not targeting an https scheme, then we definitely not 1000 // dealing with a 'same-origin' redirect. 1001 nsCOMPtr<nsIURI> finalURI; 1002 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI)); 1003 NS_ENSURE_SUCCESS(rv, false); 1004 if (!finalURI->SchemeIs("https")) { 1005 return false; 1006 } 1007 1008 // If the background request was not http, then there is nothing to do here. 1009 nsCOMPtr<nsIPrincipal> firstURIPrincipal; 1010 loadinfo->RedirectChain()[0]->GetPrincipal(getter_AddRefs(firstURIPrincipal)); 1011 if (!firstURIPrincipal || !firstURIPrincipal->SchemeIs("http")) { 1012 return false; 1013 } 1014 1015 // By now we have verified that the inital background request was http and 1016 // that the redirected scheme is https. We want to find the following case 1017 // where the background channel redirects to the https version of the 1018 // top-level request. 1019 // --> background channel: http://example.com 1020 // |--> redirects to: https://example.com 1021 // Now we have to check that the hosts are 'same-origin'. 1022 nsAutoCString redirectHost; 1023 nsAutoCString finalHost; 1024 firstURIPrincipal->GetAsciiHost(redirectHost); 1025 finalURI->GetAsciiHost(finalHost); 1026 return finalHost.Equals(redirectHost); 1027 } 1028 1029 NS_IMETHODIMP 1030 TestHTTPAnswerRunnable::OnStartRequest(nsIRequest* aRequest) { 1031 // If the request status is not OK, it means it encountered some 1032 // kind of error in which case we do not want to do anything. 1033 nsresult requestStatus; 1034 aRequest->GetStatus(&requestStatus); 1035 if (requestStatus != NS_OK) { 1036 return NS_OK; 1037 } 1038 1039 // Check if the original top-level channel which https-only is trying 1040 // to upgrade is already in progress or if the channel is an auth channel. 1041 // If it is in progress or Auth is in progress, then all good, if not 1042 // then let's cancel that channel so we can dispaly the exception page. 1043 nsCOMPtr<nsIChannel> docChannel = mDocumentLoadListener->GetChannel(); 1044 nsCOMPtr<nsIHttpChannel> httpsOnlyChannel = do_QueryInterface(docChannel); 1045 if (httpsOnlyChannel) { 1046 nsCOMPtr<nsILoadInfo> loadInfo = httpsOnlyChannel->LoadInfo(); 1047 uint32_t topLevelLoadInProgress = 1048 loadInfo->GetHttpsOnlyStatus() & 1049 nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS; 1050 1051 nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = 1052 do_QueryInterface(httpsOnlyChannel); 1053 bool isAuthChannel = false; 1054 (void)httpChannelInternal->GetIsAuthChannel(&isAuthChannel); 1055 // some server configurations need a long time to respond to an https 1056 // connection, but also redirect any http connection to the https version of 1057 // it. If the top-level load has not started yet, but the http background 1058 // request redirects to https, then do not show the error page, but keep 1059 // waiting for the https response of the upgraded top-level request. 1060 if (!topLevelLoadInProgress) { 1061 nsCOMPtr<nsIHttpChannel> backgroundHttpChannel = 1062 do_QueryInterface(aRequest); 1063 topLevelLoadInProgress = 1064 IsBackgroundRequestRedirected(backgroundHttpChannel); 1065 } 1066 if (!topLevelLoadInProgress && !isAuthChannel) { 1067 // Only really cancel the original top-level channel if it's 1068 // status is still NS_OK, otherwise it might have already 1069 // encountered some other error and was cancelled. 1070 nsresult httpsOnlyChannelStatus; 1071 httpsOnlyChannel->GetStatus(&httpsOnlyChannelStatus); 1072 if (httpsOnlyChannelStatus == NS_OK) { 1073 httpsOnlyChannel->Cancel(NS_ERROR_NET_TIMEOUT_EXTERNAL); 1074 } 1075 } 1076 } 1077 1078 // Cancel this http request because it has reached the end of it's 1079 // lifetime at this point. 1080 aRequest->Cancel(NS_ERROR_ABORT); 1081 return NS_ERROR_ABORT; 1082 } 1083 1084 NS_IMETHODIMP 1085 TestHTTPAnswerRunnable::OnDataAvailable(nsIRequest* aRequest, 1086 nsIInputStream* aStream, 1087 uint64_t aOffset, uint32_t aCount) { 1088 // TestHTTPAnswerRunnable only cares about ::OnStartRequest which 1089 // will also cancel the request, so we should in fact never even 1090 // get here. 1091 MOZ_ASSERT(false, "how come we get to ::OnDataAvailable"); 1092 return NS_OK; 1093 } 1094 1095 NS_IMETHODIMP 1096 TestHTTPAnswerRunnable::OnStopRequest(nsIRequest* aRequest, 1097 nsresult aStatusCode) { 1098 // TestHTTPAnswerRunnable only cares about ::OnStartRequest 1099 return NS_OK; 1100 } 1101 1102 NS_IMETHODIMP 1103 TestHTTPAnswerRunnable::GetInterface(const nsIID& aIID, void** aResult) { 1104 return QueryInterface(aIID, aResult); 1105 } 1106 1107 NS_IMETHODIMP 1108 TestHTTPAnswerRunnable::Run() { 1109 { 1110 // Before we start our timer we kick of a DNS request for HTTPS RR. If we 1111 // find a HTTPS RR we will not downgrade later. 1112 nsCOMPtr<nsIChannel> origChannel = mDocumentLoadListener->GetChannel(); 1113 mozilla::OriginAttributes originAttributes; 1114 mozilla::StoragePrincipalHelper::GetOriginAttributesForHTTPSRR( 1115 origChannel, originAttributes); 1116 RefPtr<nsDNSPrefetch> resolver = 1117 new nsDNSPrefetch(mURI, originAttributes, origChannel->GetTRRMode()); 1118 nsCOMPtr<nsIHttpChannelInternal> internalChannel = 1119 do_QueryInterface(origChannel); 1120 uint32_t caps; 1121 if (NS_SUCCEEDED(internalChannel->GetCaps(&caps))) { 1122 (void)resolver->FetchHTTPSSVC( 1123 caps & NS_HTTP_REFRESH_DNS, false, 1124 [self = RefPtr{this}](nsIDNSHTTPSSVCRecord* aRecord) { 1125 self->mHasHTTPSRR = (aRecord != nullptr); 1126 }); 1127 } 1128 } 1129 1130 // Wait N milliseconds to give the original https request a heads start 1131 // before firing up this http request in the background. If the https request 1132 // has not received any signal from the server during that time, than it's 1133 // almost certain the upgraded request will result in time out. 1134 uint32_t background_timer_ms = mozilla::StaticPrefs:: 1135 dom_security_https_only_fire_http_request_background_timer_ms(); 1136 1137 return NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, 1138 background_timer_ms, nsITimer::TYPE_ONE_SHOT); 1139 } 1140 1141 NS_IMETHODIMP 1142 TestHTTPAnswerRunnable::Notify(nsITimer* aTimer) { 1143 if (mTimer) { 1144 mTimer->Cancel(); 1145 mTimer = nullptr; 1146 } 1147 1148 // If the original channel has already started loading at this point 1149 // then there is no need to do the dance. 1150 nsCOMPtr<nsIChannel> origChannel = mDocumentLoadListener->GetChannel(); 1151 nsCOMPtr<nsILoadInfo> origLoadInfo = origChannel->LoadInfo(); 1152 uint32_t origHttpsOnlyStatus = origLoadInfo->GetHttpsOnlyStatus(); 1153 uint32_t topLevelLoadInProgress = 1154 origHttpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS; 1155 uint32_t downloadInProgress = 1156 origHttpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_DOWNLOAD_IN_PROGRESS; 1157 1158 // If the upgrade is caused by HSTS or HTTPS RR we do not allow downgrades 1159 // so we do not need to start a racing request. 1160 bool isClientRequestedUpgrade = 1161 origHttpsOnlyStatus & 1162 (nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED | 1163 nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED | 1164 nsILoadInfo::HTTPS_ONLY_UPGRADED_HTTPS_FIRST) && 1165 !mHasHTTPSRR; 1166 1167 if (topLevelLoadInProgress || downloadInProgress || 1168 !isClientRequestedUpgrade) { 1169 return NS_OK; 1170 } 1171 1172 mozilla::OriginAttributes attrs = origLoadInfo->GetOriginAttributes(); 1173 RefPtr<nsIPrincipal> nullPrincipal = mozilla::NullPrincipal::Create(attrs); 1174 1175 uint32_t loadFlags = 1176 nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING | 1177 nsIRequest::INHIBIT_PERSISTENT_CACHING | nsIRequest::LOAD_BYPASS_CACHE | 1178 nsIChannel::LOAD_BYPASS_SERVICE_WORKER; 1179 1180 // No need to connect to the URI including the path because we only care about 1181 // the round trip time if a server responds to an http request. 1182 nsCOMPtr<nsIURI> backgroundChannelURI; 1183 nsAutoCString prePathStr; 1184 nsresult rv = mURI->GetPrePath(prePathStr); 1185 if (NS_WARN_IF(NS_FAILED(rv))) { 1186 return rv; 1187 } 1188 rv = NS_NewURI(getter_AddRefs(backgroundChannelURI), prePathStr); 1189 if (NS_WARN_IF(NS_FAILED(rv))) { 1190 return rv; 1191 } 1192 1193 // we are using TYPE_OTHER because TYPE_DOCUMENT might have side effects 1194 nsCOMPtr<nsIChannel> testHTTPChannel; 1195 rv = NS_NewChannel(getter_AddRefs(testHTTPChannel), backgroundChannelURI, 1196 nullPrincipal, 1197 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL, 1198 nsIContentPolicy::TYPE_OTHER, nullptr, nullptr, nullptr, 1199 nullptr, loadFlags); 1200 1201 if (NS_WARN_IF(NS_FAILED(rv))) { 1202 return rv; 1203 } 1204 1205 // We have exempt that load from HTTPS-Only to avoid getting upgraded 1206 // to https as well. Additonally let's not log that request to the console 1207 // because it might confuse end users. 1208 nsCOMPtr<nsILoadInfo> loadInfo = testHTTPChannel->LoadInfo(); 1209 uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus(); 1210 httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT | 1211 nsILoadInfo::HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE | 1212 nsILoadInfo::HTTPS_ONLY_BYPASS_ORB; 1213 loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus); 1214 1215 testHTTPChannel->SetNotificationCallbacks(this); 1216 testHTTPChannel->AsyncOpen(this); 1217 return NS_OK; 1218 }