diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/security/nsHTTPSOnlyStreamListener.cpp | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/security/nsHTTPSOnlyStreamListener.cpp')
-rw-r--r-- | dom/security/nsHTTPSOnlyStreamListener.cpp | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/dom/security/nsHTTPSOnlyStreamListener.cpp b/dom/security/nsHTTPSOnlyStreamListener.cpp new file mode 100644 index 0000000000..936cfd8cf6 --- /dev/null +++ b/dom/security/nsHTTPSOnlyStreamListener.cpp @@ -0,0 +1,275 @@ +/* -*- 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 "NSSErrorsService.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/dom/WindowGlobalParent.h" +#include "mozpkix/pkixnss.h" +#include "nsCOMPtr.h" +#include "nsHTTPSOnlyStreamListener.h" +#include "nsHTTPSOnlyUtils.h" +#include "nsIChannel.h" +#include "nsIRequest.h" +#include "nsITransportSecurityInfo.h" +#include "nsIURI.h" +#include "nsPrintfCString.h" +#include "secerr.h" +#include "sslerr.h" + +NS_IMPL_ISUPPORTS(nsHTTPSOnlyStreamListener, nsIStreamListener, + nsIRequestObserver) + +nsHTTPSOnlyStreamListener::nsHTTPSOnlyStreamListener( + nsIStreamListener* aListener, nsILoadInfo* aLoadInfo) + : mListener(aListener), mCreationStart(mozilla::TimeStamp::Now()) { + RefPtr<mozilla::dom::WindowGlobalParent> wgp = + mozilla::dom::WindowGlobalParent::GetByInnerWindowId( + aLoadInfo->GetInnerWindowID()); + // For Top-level document loads (which don't have a requesting window-context) + // we compute these flags once we create the Document in nsSecureBrowserUI. + if (wgp) { + wgp->TopWindowContext()->AddSecurityState( + nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED); + } +} + +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) { + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); + + // Note: CouldBeHttpsOnlyError also returns true if there was no error + if (nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(channel, aStatus)) { + RecordUpgradeTelemetry(request, aStatus); + LogUpgradeFailure(request, aStatus); + + // If the request failed and there is a requesting window-context, set + // HTTPS-Only state flag to indicate a failed upgrade. + // For Top-level document loads (which don't have a requesting + // window-context) we simply check in the UI code whether we landed on the + // HTTPS-Only error page. + if (NS_FAILED(aStatus)) { + nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); + RefPtr<mozilla::dom::WindowGlobalParent> wgp = + mozilla::dom::WindowGlobalParent::GetByInnerWindowId( + loadInfo->GetInnerWindowID()); + + if (wgp) { + wgp->TopWindowContext()->AddSecurityState( + nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED); + } + } + } + + return mListener->OnStopRequest(request, aStatus); +} + +void nsHTTPSOnlyStreamListener::RecordUpgradeTelemetry(nsIRequest* request, + nsresult aStatus) { + // 1. Get time between now and when the initial upgrade request started + int64_t duration = + (mozilla::TimeStamp::Now() - mCreationStart).ToMilliseconds(); + + // 2. Assemble the category string + // [!] All strings have to be present in Histograms.json + nsresult rv; + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv); + if (NS_FAILED(rv)) { + return; + } + + nsAutoCString category; + nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); + nsContentPolicyType internalType = loadInfo->InternalContentPolicyType(); + + if (internalType == nsIContentPolicy::TYPE_DOCUMENT) { + category.AppendLiteral("top_"); + } else { + category.AppendLiteral("sub_"); + } + + if (NS_SUCCEEDED(aStatus)) { + category.AppendLiteral("successful"); + } else { + int32_t code = -1 * NS_ERROR_GET_CODE(aStatus); + + if (aStatus == NS_ERROR_REDIRECT_LOOP) { + category.AppendLiteral("f_redirectloop"); + } else if (aStatus == NS_ERROR_NET_TIMEOUT || + aStatus == NS_ERROR_NET_TIMEOUT_EXTERNAL) { + category.AppendLiteral("f_timeout"); + } else if (aStatus == NS_BINDING_ABORTED) { + category.AppendLiteral("f_aborted"); + } else if (aStatus == NS_ERROR_CONNECTION_REFUSED) { + category.AppendLiteral("f_cxnrefused"); + } else if (mozilla::psm::IsNSSErrorCode(code)) { + switch (code) { + case mozilla::pkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT: + category.AppendLiteral("f_ssl_selfsignd"); + break; + case SSL_ERROR_BAD_CERT_DOMAIN: + category.AppendLiteral("f_ssl_badcertdm"); + break; + case SEC_ERROR_UNKNOWN_ISSUER: + category.AppendLiteral("f_ssl_unkwnissr"); + break; + default: + category.AppendLiteral("f_ssl_other"); + break; + } + } else { + category.AppendLiteral("f_other"); + } + } + mozilla::Telemetry::Accumulate( + mozilla::Telemetry::HTTPS_ONLY_MODE_UPGRADE_TIME_MS, category, duration); + + bool success = NS_SUCCEEDED(aStatus); + ExtContentPolicyType externalType = loadInfo->GetExternalContentPolicyType(); + auto typeKey = nsAutoCString("unknown"); + + if (externalType == ExtContentPolicy::TYPE_MEDIA) { + switch (internalType) { + case nsIContentPolicy::TYPE_INTERNAL_AUDIO: + case nsIContentPolicy::TYPE_INTERNAL_TRACK: + typeKey = "audio"_ns; + break; + + case nsIContentPolicy::TYPE_INTERNAL_VIDEO: + typeKey = "video"_ns; + break; + + default: + MOZ_ASSERT_UNREACHABLE(); + break; + } + } else { + switch (externalType) { + case ExtContentPolicy::TYPE_SCRIPT: + typeKey = "script"_ns; + break; + + case ExtContentPolicy::TYPE_OBJECT: + case ExtContentPolicy::TYPE_OBJECT_SUBREQUEST: + typeKey = "object"_ns; + break; + + case ExtContentPolicy::TYPE_DOCUMENT: + typeKey = "document"_ns; + break; + + case ExtContentPolicy::TYPE_SUBDOCUMENT: + typeKey = "subdocument"_ns; + break; + + case ExtContentPolicy::TYPE_XMLHTTPREQUEST: + typeKey = "xmlhttprequest"_ns; + break; + + case ExtContentPolicy::TYPE_IMAGE: + case ExtContentPolicy::TYPE_IMAGESET: + typeKey = "image"_ns; + break; + + case ExtContentPolicy::TYPE_DTD: + typeKey = "dtd"_ns; + break; + + case ExtContentPolicy::TYPE_FONT: + case ExtContentPolicy::TYPE_UA_FONT: + typeKey = "font"_ns; + break; + + case ExtContentPolicy::TYPE_FETCH: + typeKey = "fetch"_ns; + break; + + case ExtContentPolicy::TYPE_WEBSOCKET: + typeKey = "websocket"_ns; + break; + + case ExtContentPolicy::TYPE_STYLESHEET: + typeKey = "stylesheet"_ns; + break; + + case ExtContentPolicy::TYPE_CSP_REPORT: + typeKey = "cspreport"_ns; + break; + + case ExtContentPolicy::TYPE_WEB_MANIFEST: + typeKey = "webmanifest"_ns; + break; + + case ExtContentPolicy::TYPE_PING: + typeKey = "ping"_ns; + break; + + case ExtContentPolicy::TYPE_XSLT: + typeKey = "xslt"_ns; + break; + + case ExtContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA: + typeKey = "proxied-webrtc"_ns; + break; + + case ExtContentPolicy::TYPE_INVALID: + case ExtContentPolicy::TYPE_OTHER: + case ExtContentPolicy::TYPE_MEDIA: // already handled above + case ExtContentPolicy::TYPE_BEACON: + case ExtContentPolicy::TYPE_SAVEAS_DOWNLOAD: + case ExtContentPolicy::TYPE_SPECULATIVE: + break; + // Do not add default: so that compilers can catch the missing case. + } + } + + mozilla::Telemetry::Accumulate( + mozilla::Telemetry::HTTPS_ONLY_MODE_UPGRADE_TYPE, typeKey, success); +} + +void nsHTTPSOnlyStreamListener::LogUpgradeFailure(nsIRequest* request, + nsresult aStatus) { + // If the request failed we'll log it to the console with the error-code + if (NS_SUCCEEDED(aStatus)) { + return; + } + nsresult rv; + // Try to query for the channel-object + nsCOMPtr<nsIChannel> channel = do_QueryInterface(request, &rv); + if (NS_FAILED(rv)) { + return; + } + + nsCOMPtr<nsIURI> uri; + rv = channel->GetURI(getter_AddRefs(uri)); + if (NS_FAILED(rv)) { + return; + } + // Logging URI as well as Module- and Error-Code + AutoTArray<nsString, 2> params = { + NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()), + NS_ConvertUTF8toUTF16(nsPrintfCString("M%u-C%u", + NS_ERROR_GET_MODULE(aStatus), + NS_ERROR_GET_CODE(aStatus)))}; + + nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); + nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyFailedRequest", params, + nsIScriptError::errorFlag, loadInfo, + uri); +} |