diff --git a/dom/locales/en-US/chrome/security/security.properties b/dom/locales/en-US/chrome/security/security.properties index 0aecee40cc81..4c4f4996fcfa 100644 --- a/dom/locales/en-US/chrome/security/security.properties +++ b/dom/locales/en-US/chrome/security/security.properties @@ -147,4 +147,6 @@ XFOSameOrigin = Load denied by X-Frame-Options: “%1$S” from “%2$S”, site # LOCALIZATION NOTE: %1$S is the URL of the upgraded request; %2$S is the upgraded scheme. HTTPSOnlyUpgradeRequest = Upgrading insecure request “%1$S” to use “%2$S”. # LOCALIZATION NOTE: %1$S is the URL of request. -HTTPSOnlyNoUpgrade = Request for “%1$S” was not upgraded because it had the NoUpgrade-flag. +HTTPSOnlyNoUpgradeException = Not upgrading insecure request “%1$S” because it is exempt. +# LOCALIZATION NOTE: %1$S is the URL of the failed request; %2$S is an error-code. +HTTPSOnlyFailedRequest = Upgrading insecure request “%1$S” failed. (%2$S) diff --git a/dom/security/moz.build b/dom/security/moz.build index a30add1c21d8..8789a64b023b 100644 --- a/dom/security/moz.build +++ b/dom/security/moz.build @@ -21,6 +21,7 @@ EXPORTS.mozilla.dom += [ 'nsCSPContext.h', 'nsCSPService.h', 'nsCSPUtils.h', + 'nsHTTPSOnlyStreamListener.h', 'nsHTTPSOnlyUtils.h', 'nsMixedContentBlocker.h', 'PolicyTokenizer.h', @@ -49,6 +50,7 @@ UNIFIED_SOURCES += [ 'nsCSPParser.cpp', 'nsCSPService.cpp', 'nsCSPUtils.cpp', + 'nsHTTPSOnlyStreamListener.cpp', 'nsHTTPSOnlyUtils.cpp', 'nsMixedContentBlocker.cpp', 'PolicyTokenizer.cpp', diff --git a/dom/security/nsHTTPSOnlyStreamListener.cpp b/dom/security/nsHTTPSOnlyStreamListener.cpp new file mode 100644 index 000000000000..8e7702bf51bc --- /dev/null +++ b/dom/security/nsHTTPSOnlyStreamListener.cpp @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsHTTPSOnlyStreamListener.h" +#include "nsHTTPSOnlyUtils.h" +#include "nsCOMPtr.h" +#include "nsIChannel.h" +#include "nsIRequest.h" +#include "nsITransportSecurityInfo.h" +#include "nsIURI.h" +#include "nsPrintfCString.h" + +NS_IMPL_ISUPPORTS(nsHTTPSOnlyStreamListener, nsIStreamListener, + nsIRequestObserver) + +nsHTTPSOnlyStreamListener::nsHTTPSOnlyStreamListener( + nsIStreamListener* aListener) { + mListener = aListener; +} + +NS_IMETHODIMP +nsHTTPSOnlyStreamListener::OnDataAvailable(nsIRequest* aRequest, + nsIInputStream* aInputStream, + uint64_t aOffset, uint32_t aCount) { + return mListener->OnDataAvailable(aRequest, aInputStream, aOffset, aCount); +} + +NS_IMETHODIMP +nsHTTPSOnlyStreamListener::OnStartRequest(nsIRequest* request) { + return mListener->OnStartRequest(request); +} + +NS_IMETHODIMP +nsHTTPSOnlyStreamListener::OnStopRequest(nsIRequest* request, + nsresult aStatus) { + // If the request failed we'll log it to the console with the error-code + if (NS_FAILED(aStatus)) { + nsresult rv; + // Try to query for the channel-object + nsCOMPtr channel = do_QueryInterface(request, &rv); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr loadInfo = channel->LoadInfo(); + uint32_t innerWindowId = loadInfo->GetInnerWindowID(); + + nsCOMPtr uri; + rv = channel->GetURI(getter_AddRefs(uri)); + if (NS_SUCCEEDED(rv)) { + // Logging URI as well as Module- and Error-Code + AutoTArray params = { + NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()), + NS_ConvertUTF8toUTF16(nsPrintfCString("M%u-C%u", + NS_ERROR_GET_MODULE(aStatus), + NS_ERROR_GET_CODE(aStatus)))}; + nsHTTPSOnlyUtils::LogLocalizedString( + "HTTPSOnlyFailedRequest", params, nsIScriptError::errorFlag, + innerWindowId, !!loadInfo->GetOriginAttributes().mPrivateBrowsingId, + uri); + } + } + } + + return mListener->OnStopRequest(request, aStatus); +} diff --git a/dom/security/nsHTTPSOnlyStreamListener.h b/dom/security/nsHTTPSOnlyStreamListener.h new file mode 100644 index 000000000000..b7598ab58eed --- /dev/null +++ b/dom/security/nsHTTPSOnlyStreamListener.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsHTTPSOnlyStreamListener_h___ +#define nsHTTPSOnlyStreamListener_h___ + +#include "nsIStreamListener.h" +#include "nsCOMPtr.h" + +/** + * This event listener gets registered for requests that have been upgraded + * using the HTTPS-only mode to log failed upgrades to the console. + */ +class nsHTTPSOnlyStreamListener : public nsIStreamListener { + public: + // nsISupports methods + NS_DECL_ISUPPORTS + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + + explicit nsHTTPSOnlyStreamListener(nsIStreamListener* aListener); + + private: + virtual ~nsHTTPSOnlyStreamListener() = default; + + nsCOMPtr mListener; +}; + +#endif /* nsHTTPSOnlyStreamListener_h___ */ diff --git a/dom/security/nsHTTPSOnlyUtils.cpp b/dom/security/nsHTTPSOnlyUtils.cpp index 39ac3e6c441f..ca5518c1f8b4 100644 --- a/dom/security/nsHTTPSOnlyUtils.cpp +++ b/dom/security/nsHTTPSOnlyUtils.cpp @@ -18,14 +18,16 @@ bool nsHTTPSOnlyUtils::ShouldUpgradeRequest(nsIURI* aURI, return false; } // 2. Check if NoUpgrade-flag is set in LoadInfo - if (aLoadInfo->GetHttpsOnlyNoUpgrade()) { + uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus(); + if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) { // Let's log to the console, that we didn't upgrade this request uint32_t innerWindowId = aLoadInfo->GetInnerWindowID(); - AutoTArray params = { + AutoTArray params = { NS_ConvertUTF8toUTF16(aURI->GetSpecOrDefault())}; nsHTTPSOnlyUtils::LogLocalizedString( - "HTTPSOnlyNoUpgrade", params, nsIScriptError::infoFlag, innerWindowId, - !!aLoadInfo->GetOriginAttributes().mPrivateBrowsingId, aURI); + "HTTPSOnlyNoUpgradeException", params, nsIScriptError::infoFlag, + innerWindowId, !!aLoadInfo->GetOriginAttributes().mPrivateBrowsingId, + aURI); return false; } @@ -46,6 +48,14 @@ bool nsHTTPSOnlyUtils::ShouldUpgradeRequest(nsIURI* aURI, innerWindowId, !!aLoadInfo->GetOriginAttributes().mPrivateBrowsingId, aURI); + // If the status was not determined before, we now indicate that the request + // will get upgraded, but no event-listener has been registered yet. + if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) { + httpsOnlyStatus ^= nsILoadInfo::HTTPS_ONLY_UNINITIALIZED; + httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED; + aLoadInfo->SetHttpsOnlyStatus(httpsOnlyStatus); + } + return true; } @@ -79,32 +89,8 @@ void nsHTTPSOnlyUtils::LogMessage(const nsAString& aMessage, uint32_t aFlags, aInnerWindowID, aURI); } else { // Send to browser console - LogSimpleConsoleError(message, category.get(), aFromPrivateWindow, - true /* from chrome context */, aFlags); + nsContentUtils::LogSimpleConsoleError( + message, category.get(), aFromPrivateWindow, + true /* from chrome context */, aFlags); } } - -/* static */ -void nsHTTPSOnlyUtils::LogSimpleConsoleError(const nsAString& aErrorText, - const char* aCategory, - bool aFromPrivateWindow, - bool aFromChromeContext, - uint32_t aErrorFlags) { - nsCOMPtr scriptError = - do_CreateInstance(NS_SCRIPTERROR_CONTRACTID); - if (!scriptError) { - return; - } - nsCOMPtr console = - do_GetService(NS_CONSOLESERVICE_CONTRACTID); - if (!console) { - return; - } - nsresult rv = scriptError->Init(aErrorText, EmptyString(), EmptyString(), 0, - 0, aErrorFlags, aCategory, aFromPrivateWindow, - aFromChromeContext); - if (NS_FAILED(rv)) { - return; - } - console->LogMessage(scriptError); -} diff --git a/dom/security/nsHTTPSOnlyUtils.h b/dom/security/nsHTTPSOnlyUtils.h index bcce3b68f31e..699521b483f6 100644 --- a/dom/security/nsHTTPSOnlyUtils.h +++ b/dom/security/nsHTTPSOnlyUtils.h @@ -46,19 +46,6 @@ class nsHTTPSOnlyUtils { static void LogMessage(const nsAString& aMessage, uint32_t aFlags, uint64_t aInnerWindowID, bool aFromPrivateWindow, nsIURI* aURI = nullptr); - - /** - * Report simple error message to the browser console - * @param aErrorText the error message - * @param aCategory Name of the module reporting error - * @param aFromPrivateWindow Whether from private window or not - * @param aFromChromeContext Whether from chrome context or not - * @param [aErrorFlags] See nsIScriptError. - */ - static void LogSimpleConsoleError( - const nsAString& aErrorText, const char* aCategory, - bool aFromPrivateWindow, bool aFromChromeContext, - uint32_t aErrorFlags = nsIScriptError::errorFlag); }; #endif /* nsHTTPSOnlyUtils_h___ */ diff --git a/ipc/glue/BackgroundUtils.cpp b/ipc/glue/BackgroundUtils.cpp index 9ab9aa12c643..4395f7541408 100644 --- a/ipc/glue/BackgroundUtils.cpp +++ b/ipc/glue/BackgroundUtils.cpp @@ -580,7 +580,7 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadInfo* aLoadInfo, aLoadInfo->GetDocumentHasLoaded(), aLoadInfo->GetAllowListFutureDocumentsCreatedFromThisRedirectChain(), cspNonce, aLoadInfo->GetSkipContentSniffing(), - aLoadInfo->GetHttpsOnlyNoUpgrade(), + aLoadInfo->GetHttpsOnlyStatus(), aLoadInfo->GetIsFromProcessingFrameAttributes(), cookieJarSettingsArgs, aLoadInfo->GetRequestBlockingReason(), maybeCspToInheritInfo)); @@ -778,7 +778,7 @@ nsresult LoadInfoArgsToLoadInfo( loadInfoArgs.documentHasLoaded(), loadInfoArgs.allowListFutureDocumentsCreatedFromThisRedirectChain(), loadInfoArgs.cspNonce(), loadInfoArgs.skipContentSniffing(), - loadInfoArgs.httpsOnlyNoUpgrade(), loadInfoArgs.requestBlockingReason(), + loadInfoArgs.httpsOnlyStatus(), loadInfoArgs.requestBlockingReason(), loadingContext); if (loadInfoArgs.isFromProcessingFrameAttributes()) { @@ -794,8 +794,8 @@ void LoadInfoToParentLoadInfoForwarder( if (!aLoadInfo) { *aForwarderArgsOut = ParentLoadInfoForwarderArgs( false, false, Nothing(), nsILoadInfo::TAINTING_BASIC, - false, // SkipContentSniffing - false, // HttpsOnlyNoUpgrade + false, // SkipContentSniffing + nsILoadInfo::HTTPS_ONLY_UNINITIALIZED, // httpsOnlyStatus false, // serviceWorkerTaintingSynthesized false, // documentHasUserInteracted false, // documentHasLoaded @@ -830,7 +830,7 @@ void LoadInfoToParentLoadInfoForwarder( *aForwarderArgsOut = ParentLoadInfoForwarderArgs( aLoadInfo->GetAllowInsecureRedirectToDataURI(), aLoadInfo->GetBypassCORSChecks(), ipcController, tainting, - aLoadInfo->GetSkipContentSniffing(), aLoadInfo->GetHttpsOnlyNoUpgrade(), + aLoadInfo->GetSkipContentSniffing(), aLoadInfo->GetHttpsOnlyStatus(), aLoadInfo->GetServiceWorkerTaintingSynthesized(), aLoadInfo->GetDocumentHasUserInteracted(), aLoadInfo->GetDocumentHasLoaded(), @@ -869,7 +869,7 @@ nsresult MergeParentLoadInfoForwarder( rv = aLoadInfo->SetSkipContentSniffing(aForwarderArgs.skipContentSniffing()); NS_ENSURE_SUCCESS(rv, rv); - rv = aLoadInfo->SetHttpsOnlyNoUpgrade(aForwarderArgs.httpsOnlyNoUpgrade()); + rv = aLoadInfo->SetHttpsOnlyStatus(aForwarderArgs.httpsOnlyStatus()); NS_ENSURE_SUCCESS(rv, rv); MOZ_ALWAYS_SUCCEEDS(aLoadInfo->SetDocumentHasUserInteracted( diff --git a/netwerk/base/LoadInfo.cpp b/netwerk/base/LoadInfo.cpp index e1dfc860af75..c989664d1d1e 100644 --- a/netwerk/base/LoadInfo.cpp +++ b/netwerk/base/LoadInfo.cpp @@ -103,7 +103,7 @@ LoadInfo::LoadInfo( mDocumentHasLoaded(false), mAllowListFutureDocumentsCreatedFromThisRedirectChain(false), mSkipContentSniffing(false), - mHttpsOnlyNoUpgrade(false), + mHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_UNINITIALIZED), mIsFromProcessingFrameAttributes(false) { MOZ_ASSERT(mLoadingPrincipal); MOZ_ASSERT(mTriggeringPrincipal); @@ -366,7 +366,7 @@ LoadInfo::LoadInfo(nsPIDOMWindowOuter* aOuterWindow, mDocumentHasLoaded(false), mAllowListFutureDocumentsCreatedFromThisRedirectChain(false), mSkipContentSniffing(false), - mHttpsOnlyNoUpgrade(false), + mHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_UNINITIALIZED), mIsFromProcessingFrameAttributes(false) { // Top-level loads are never third-party // Grab the information we can out of the window. @@ -467,7 +467,7 @@ LoadInfo::LoadInfo(dom::CanonicalBrowsingContext* aBrowsingContext, mDocumentHasLoaded(false), mAllowListFutureDocumentsCreatedFromThisRedirectChain(false), mSkipContentSniffing(false), - mHttpsOnlyNoUpgrade(false), + mHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_UNINITIALIZED), mIsFromProcessingFrameAttributes(false) { // Top-level loads are never third-party // Grab the information we can out of the window. @@ -568,7 +568,7 @@ LoadInfo::LoadInfo(const LoadInfo& rhs) rhs.mAllowListFutureDocumentsCreatedFromThisRedirectChain), mCspNonce(rhs.mCspNonce), mSkipContentSniffing(rhs.mSkipContentSniffing), - mHttpsOnlyNoUpgrade(rhs.mHttpsOnlyNoUpgrade), + mHttpsOnlyStatus(rhs.mHttpsOnlyStatus), mIsFromProcessingFrameAttributes(rhs.mIsFromProcessingFrameAttributes) {} LoadInfo::LoadInfo( @@ -606,7 +606,7 @@ LoadInfo::LoadInfo( bool aDocumentHasLoaded, bool aAllowListFutureDocumentsCreatedFromThisRedirectChain, const nsAString& aCspNonce, bool aSkipContentSniffing, - bool aHttpsOnlyNoUpgrade, uint32_t aRequestBlockingReason, + uint32_t aHttpsOnlyStatus, uint32_t aRequestBlockingReason, nsINode* aLoadingContext) : mLoadingPrincipal(aLoadingPrincipal), mTriggeringPrincipal(aTriggeringPrincipal), @@ -663,7 +663,7 @@ LoadInfo::LoadInfo( aAllowListFutureDocumentsCreatedFromThisRedirectChain), mCspNonce(aCspNonce), mSkipContentSniffing(aSkipContentSniffing), - mHttpsOnlyNoUpgrade(aHttpsOnlyNoUpgrade), + mHttpsOnlyStatus(aHttpsOnlyStatus), mIsFromProcessingFrameAttributes(false) { // Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal MOZ_ASSERT(mLoadingPrincipal || @@ -1470,14 +1470,14 @@ LoadInfo::SetSkipContentSniffing(bool aSkipContentSniffing) { } NS_IMETHODIMP -LoadInfo::GetHttpsOnlyNoUpgrade(bool* aHttpsOnlyNoUpgrade) { - *aHttpsOnlyNoUpgrade = mHttpsOnlyNoUpgrade; +LoadInfo::GetHttpsOnlyStatus(uint32_t* aHttpsOnlyStatus) { + *aHttpsOnlyStatus = mHttpsOnlyStatus; return NS_OK; } NS_IMETHODIMP -LoadInfo::SetHttpsOnlyNoUpgrade(bool aHttpsOnlyNoUpgrade) { - mHttpsOnlyNoUpgrade = aHttpsOnlyNoUpgrade; +LoadInfo::SetHttpsOnlyStatus(uint32_t aHttpsOnlyStatus) { + mHttpsOnlyStatus = aHttpsOnlyStatus; return NS_OK; } diff --git a/netwerk/base/LoadInfo.h b/netwerk/base/LoadInfo.h index 64651eacb12c..67776bd59c3d 100644 --- a/netwerk/base/LoadInfo.h +++ b/netwerk/base/LoadInfo.h @@ -163,7 +163,7 @@ class LoadInfo final : public nsILoadInfo { bool aDocumentHasUserInteracted, bool aDocumentHasLoaded, bool aAllowListFutureDocumentsCreatedFromThisRedirectChain, const nsAString& aCspNonce, bool aSkipContentSniffing, - bool aHttpsOnlyNoUpgrade, uint32_t aRequestBlockingReason, + uint32_t aHttpsOnlyStatus, uint32_t aRequestBlockingReason, nsINode* aLoadingContext); LoadInfo(const LoadInfo& rhs); @@ -259,7 +259,7 @@ class LoadInfo final : public nsILoadInfo { bool mAllowListFutureDocumentsCreatedFromThisRedirectChain; nsString mCspNonce; bool mSkipContentSniffing; - bool mHttpsOnlyNoUpgrade; + uint32_t mHttpsOnlyStatus; // Is true if this load was triggered by processing the attributes of the // browsing context container. diff --git a/netwerk/base/NetworkConnectivityService.cpp b/netwerk/base/NetworkConnectivityService.cpp index 885f5ba7870b..bea5c07d6451 100644 --- a/netwerk/base/NetworkConnectivityService.cpp +++ b/netwerk/base/NetworkConnectivityService.cpp @@ -209,7 +209,9 @@ static inline already_AddRefed SetupIPCheckChannel(bool ipv4) { { // Prevent HTTPS-Only Mode from upgrading the OCSP request. nsCOMPtr loadInfo = channel->LoadInfo(); - loadInfo->SetHttpsOnlyNoUpgrade(true); + uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus(); + httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT; + loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus); } NS_ENSURE_SUCCESS(rv, nullptr); diff --git a/netwerk/base/TRRLoadInfo.cpp b/netwerk/base/TRRLoadInfo.cpp index bce8f6d3024a..3565d9e3e933 100644 --- a/netwerk/base/TRRLoadInfo.cpp +++ b/netwerk/base/TRRLoadInfo.cpp @@ -587,12 +587,12 @@ already_AddRefed TRRLoadInfo::GetCspToInherit() { } NS_IMETHODIMP -TRRLoadInfo::GetHttpsOnlyNoUpgrade(bool* aHttpsOnlyNoUpgrade) { +TRRLoadInfo::GetHttpsOnlyStatus(uint32_t* aHttpsOnlyStatus) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP -TRRLoadInfo::SetHttpsOnlyNoUpgrade(bool aHttpsOnlyNoUpgrade) { +TRRLoadInfo::SetHttpsOnlyStatus(uint32_t aHttpsOnlyStatus) { return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/netwerk/base/nsILoadInfo.idl b/netwerk/base/nsILoadInfo.idl index f55122523687..d77b3a3aa575 100644 --- a/netwerk/base/nsILoadInfo.idl +++ b/netwerk/base/nsILoadInfo.idl @@ -416,10 +416,37 @@ interface nsILoadInfo : nsISupports [infallible] attribute boolean skipContentSniffing; /** - * If httpsOnlyNoUpgrade is true, the request won't get upgraded by the - * HTTPS-Only Mode. + * (default) If this flag is set, it has not yet been determined if the + * HTTPS-Only mode will upgrade the request. */ - [infallible] attribute boolean httpsOnlyNoUpgrade; + const unsigned long HTTPS_ONLY_UNINITIALIZED = (1 << 0); + + /** + * Indicates that the request will get upgraded, and the HTTPS-Only + * StreamListener got registered. + */ + const unsigned long HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED = (1 << 1); + + /** + * Indicates that this is the first time the request gets upgraded, and thus + * the HTTPS-Only StreamListener hasn't been registered yet. Even though there + * might be multiple channels per request that have to be upgraded (e.g., + * because of redirects), the StreamListener only has to be attached to one + * channel. + */ + const unsigned long HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED = (1 << 2); + + /** + * This flag can be manually set if the HTTPS-Only mode should exempt the + * request and not upgrade it. (e.g in the case of OCSP. + */ + const unsigned long HTTPS_ONLY_EXEMPT = (1 << 3); + + /** + * Upgrade state of HTTPS-Only Mode. The flag HTTPS_ONLY_EXEMPT can get + * set on requests that should be excempt from an upgrade. + */ + [infallible] attribute unsigned long httpsOnlyStatus; /** * True if this request is embedded in a context that can't be third-party diff --git a/netwerk/ipc/NeckoChannelParams.ipdlh b/netwerk/ipc/NeckoChannelParams.ipdlh index db6050f736ca..0ad6fa8b89f4 100644 --- a/netwerk/ipc/NeckoChannelParams.ipdlh +++ b/netwerk/ipc/NeckoChannelParams.ipdlh @@ -145,7 +145,7 @@ struct LoadInfoArgs bool allowListFutureDocumentsCreatedFromThisRedirectChain; nsString cspNonce; bool skipContentSniffing; - bool httpsOnlyNoUpgrade; + uint32_t httpsOnlyStatus; bool isFromProcessingFrameAttributes; CookieJarSettingsArgs cookieJarSettings; uint32_t requestBlockingReason; @@ -184,9 +184,8 @@ struct ParentLoadInfoForwarderArgs // that flag is set to true so we skip content sniffing for that browsing bool skipContentSniffing; - // If httpsOnlyNoUpgrade is true, the request won't get upgraded by the - // HTTPS-Only Mode. - bool httpsOnlyNoUpgrade; + // Upgrade state of HTTPS-Only Mode + uint32_t httpsOnlyStatus; // We must also note that the tainting value was explicitly set // by the service worker. diff --git a/netwerk/protocol/http/nsCORSListenerProxy.cpp b/netwerk/protocol/http/nsCORSListenerProxy.cpp index 7c43e0980084..a3e82fc631aa 100644 --- a/netwerk/protocol/http/nsCORSListenerProxy.cpp +++ b/netwerk/protocol/http/nsCORSListenerProxy.cpp @@ -885,7 +885,7 @@ nsresult nsCORSListenerProxy::UpdateChannel(nsIChannel* aChannel, // from the netwerk, hence we shouldn't require CORS in that specific case. if (CheckInsecureUpgradePreventsCORS(mRequestingPrincipal, aChannel)) { // Check if HTTPS-Only Mode is enabled - if (!loadInfo->GetHttpsOnlyNoUpgrade() && + if (!(loadInfo->GetHttpsOnlyStatus() & nsILoadInfo::HTTPS_ONLY_EXEMPT) && StaticPrefs::dom_security_https_only_mode()) { return NS_OK; } diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index ed64cd7a3350..c20f133bc8bf 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -118,6 +118,7 @@ #include "nsINetworkLinkService.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/ServiceWorkerUtils.h" +#include "mozilla/dom/nsHTTPSOnlyStreamListener.h" #include "mozilla/net/AsyncUrlChannelClassifier.h" #include "mozilla/net/CookieJarSettings.h" #include "mozilla/net/NeckoChannelParams.h" @@ -622,6 +623,20 @@ nsresult nsHttpChannel::OnBeforeConnect() { mPrivateBrowsing, mAllowSTS, originAttributes, shouldUpgrade, std::move(resultCallback), willCallback); + // If the request gets upgraded because of the HTTPS-Only mode, but no + // event listener has been registered so far, we want to do that here. + uint32_t httpOnlyStatus = mLoadInfo->GetHttpsOnlyStatus(); + if (httpOnlyStatus & + nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED) { + RefPtr httpsOnlyListener = + new nsHTTPSOnlyStreamListener(mListener); + mListener = httpsOnlyListener; + + httpOnlyStatus ^= + nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_NOT_REGISTERED; + httpOnlyStatus |= nsILoadInfo::HTTPS_ONLY_UPGRADED_LISTENER_REGISTERED; + mLoadInfo->SetHttpsOnlyStatus(httpOnlyStatus); + } LOG( ("nsHttpChannel::OnBeforeConnect " "[this=%p willCallback=%d rv=%" PRIx32 "]\n", diff --git a/security/manager/ssl/nsNSSCallbacks.cpp b/security/manager/ssl/nsNSSCallbacks.cpp index 8f38d1439fce..161d82016bf7 100644 --- a/security/manager/ssl/nsNSSCallbacks.cpp +++ b/security/manager/ssl/nsNSSCallbacks.cpp @@ -268,7 +268,9 @@ OCSPRequest::Run() { nsCOMPtr loadInfo = channel->LoadInfo(); // Prevent HTTPS-Only Mode from upgrading the OCSP request. - loadInfo->SetHttpsOnlyNoUpgrade(true); + uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus(); + httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT; + loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus); // For OCSP requests, only the first party domain and private browsing id // aspects of origin attributes are used. This means that: diff --git a/toolkit/components/captivedetect/CaptiveDetect.jsm b/toolkit/components/captivedetect/CaptiveDetect.jsm index 10f8687cc70f..7aa31db5ae39 100644 --- a/toolkit/components/captivedetect/CaptiveDetect.jsm +++ b/toolkit/components/captivedetect/CaptiveDetect.jsm @@ -38,7 +38,7 @@ function URLFetcher(url, timeout) { // We except this from being classified xhr.channel.loadFlags |= Ci.nsIChannel.LOAD_BYPASS_URL_CLASSIFIER; // Prevent HTTPS-Only Mode from upgrading the request. - xhr.channel.loadInfo.httpsOnlyNoUpgrade = true; + xhr.channel.loadInfo.httpsOnlyStatus |= Ci.nsILoadInfo.HTTPS_ONLY_EXEMPT; // We don't want to follow _any_ redirects xhr.channel.QueryInterface(Ci.nsIHttpChannel).redirectionLimit = 0; diff --git a/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm b/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm index 914d251bb3bb..5e345a0e1449 100644 --- a/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm +++ b/toolkit/mozapps/extensions/internal/ProductAddonChecker.jsm @@ -355,7 +355,10 @@ function downloadFile(url, options = { httpsOnlyNoUpgrade: false }) { xhr.responseType = "arraybuffer"; try { xhr.open("GET", url); - xhr.channel.loadInfo.httpsOnlyNoUpgrade = options.httpsOnlyNoUpgrade; + if (options.httpsOnlyNoUpgrade) { + xhr.channel.loadInfo.httpsOnlyStatus |= + Ci.nsILoadInfo.HTTPS_ONLY_EXEMPT; + } // Use conservative TLS settings. See bug 1325501. // TODO move to ServiceRequest. if (xhr.channel instanceof Ci.nsIHttpChannelInternal) {