2020-03-17 22:24:31 +03:00
|
|
|
/* -*- 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/. */
|
|
|
|
|
2020-08-12 15:57:22 +03:00
|
|
|
#include "mozilla/NullPrincipal.h"
|
2020-03-17 22:24:31 +03:00
|
|
|
#include "mozilla/StaticPrefs_dom.h"
|
2020-04-29 18:52:19 +03:00
|
|
|
#include "mozilla/net/DNS.h"
|
2020-03-17 22:24:31 +03:00
|
|
|
#include "nsContentUtils.h"
|
2020-05-26 14:45:21 +03:00
|
|
|
#include "nsHTTPSOnlyUtils.h"
|
2020-03-17 22:24:31 +03:00
|
|
|
#include "nsIConsoleService.h"
|
2020-08-10 11:51:44 +03:00
|
|
|
#include "nsIHttpChannel.h"
|
2020-08-12 15:57:22 +03:00
|
|
|
#include "nsIHttpChannel.h"
|
2020-07-08 15:06:02 +03:00
|
|
|
#include "nsIHttpsOnlyModePermission.h"
|
|
|
|
#include "nsIPermissionManager.h"
|
2020-08-12 15:57:22 +03:00
|
|
|
#include "nsIPrincipal.h"
|
2020-03-17 22:24:31 +03:00
|
|
|
#include "nsIScriptError.h"
|
2020-04-29 18:52:19 +03:00
|
|
|
#include "prnetdb.h"
|
|
|
|
|
2020-08-12 15:57:22 +03:00
|
|
|
// Set the timer to 3 seconds. If the https request has not received
|
|
|
|
// any signal from the server during that time, than we it's almost
|
|
|
|
// certain the request will time out.
|
|
|
|
#define FIRE_HTTP_REQUEST_BACKGROUND_TIMER_MS 3000
|
|
|
|
|
2020-07-06 11:52:02 +03:00
|
|
|
/* static */
|
|
|
|
bool nsHTTPSOnlyUtils::IsHttpsOnlyModeEnabled(bool aFromPrivateWindow) {
|
|
|
|
// if the general pref is set to true, then we always return
|
|
|
|
if (mozilla::StaticPrefs::dom_security_https_only_mode()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// otherwise we check if executing in private browsing mode and return true
|
|
|
|
// if the PBM pref for HTTPS-Only is set.
|
|
|
|
if (aFromPrivateWindow &&
|
|
|
|
mozilla::StaticPrefs::dom_security_https_only_mode_pbm()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2020-03-17 22:24:31 +03:00
|
|
|
|
2020-08-12 15:57:22 +03:00
|
|
|
/* static */
|
|
|
|
void nsHTTPSOnlyUtils::PotentiallyFireHttpRequestToShortenTimout(
|
|
|
|
mozilla::net::DocumentLoadListener* aDocumentLoadListener) {
|
|
|
|
nsCOMPtr<nsIChannel> channel = aDocumentLoadListener->GetChannel();
|
|
|
|
if (!channel) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
|
|
|
|
bool isPrivateWin = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
|
|
|
|
|
|
|
// if https-only mode is not even enabled, then there is nothing to do here.
|
|
|
|
if (!IsHttpsOnlyModeEnabled(isPrivateWin)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if we are not dealing with a top-level load, then there is nothing to do
|
|
|
|
// here.
|
|
|
|
if (loadInfo->GetExternalContentPolicyType() !=
|
|
|
|
nsIContentPolicy::TYPE_DOCUMENT) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the load is exempt, then there is nothing to do here.
|
|
|
|
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
|
|
|
|
if (httpsOnlyStatus & nsILoadInfo::nsILoadInfo::HTTPS_ONLY_EXEMPT) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if it's not an http channel, then there is nothing to do here.
|
|
|
|
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
|
|
|
|
if (!httpChannel) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if it's not a GET method, then there is nothing to do here either.
|
|
|
|
nsAutoCString method;
|
|
|
|
Unused << httpChannel->GetRequestMethod(method);
|
|
|
|
if (!method.EqualsLiteral("GET")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if it's already an https channel, then there is nothing to do here.
|
|
|
|
nsCOMPtr<nsIURI> channelURI;
|
|
|
|
channel->GetURI(getter_AddRefs(channelURI));
|
|
|
|
if (channelURI->SchemeIs("https")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<nsIRunnable> task =
|
|
|
|
new TestHTTPAnswerRunnable(channelURI, aDocumentLoadListener);
|
|
|
|
NS_DispatchToMainThread(task.forget());
|
|
|
|
}
|
|
|
|
|
2020-03-17 22:24:31 +03:00
|
|
|
/* static */
|
|
|
|
bool nsHTTPSOnlyUtils::ShouldUpgradeRequest(nsIURI* aURI,
|
|
|
|
nsILoadInfo* aLoadInfo) {
|
2020-04-29 18:52:19 +03:00
|
|
|
// 1. Check if the HTTPS-Only Mode is even enabled, before we do anything else
|
2020-07-06 11:52:02 +03:00
|
|
|
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
|
|
|
if (!IsHttpsOnlyModeEnabled(isPrivateWin)) {
|
2020-03-17 22:24:31 +03:00
|
|
|
return false;
|
|
|
|
}
|
2020-04-29 18:52:19 +03:00
|
|
|
|
|
|
|
// 2. Check for general exceptions
|
|
|
|
if (OnionException(aURI) || LoopbackOrLocalException(aURI)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. Check if NoUpgrade-flag is set in LoadInfo
|
2020-03-27 20:09:15 +03:00
|
|
|
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
|
|
|
|
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
|
|
|
|
AutoTArray<nsString, 1> params = {
|
2020-03-17 22:24:31 +03:00
|
|
|
NS_ConvertUTF8toUTF16(aURI->GetSpecOrDefault())};
|
2020-07-06 11:52:02 +03:00
|
|
|
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyNoUpgradeException", params,
|
2020-08-14 03:22:59 +03:00
|
|
|
nsIScriptError::infoFlag, aLoadInfo,
|
|
|
|
aURI);
|
2020-03-17 22:24:31 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-04-29 18:52:19 +03:00
|
|
|
// We can upgrade the request - let's log it to the console
|
|
|
|
// Appending an 's' to the scheme for the logging. (http -> https)
|
2020-03-17 22:24:31 +03:00
|
|
|
nsAutoCString scheme;
|
|
|
|
aURI->GetScheme(scheme);
|
|
|
|
scheme.AppendLiteral("s");
|
|
|
|
NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault());
|
|
|
|
NS_ConvertUTF8toUTF16 reportScheme(scheme);
|
|
|
|
|
|
|
|
AutoTArray<nsString, 2> params = {reportSpec, reportScheme};
|
2020-08-14 03:22:59 +03:00
|
|
|
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyUpgradeRequest", params,
|
|
|
|
nsIScriptError::warningFlag, aLoadInfo,
|
|
|
|
aURI);
|
2020-03-17 22:24:31 +03:00
|
|
|
|
2020-03-27 20:09:15 +03:00
|
|
|
// 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);
|
|
|
|
}
|
2020-04-29 18:52:19 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
bool nsHTTPSOnlyUtils::ShouldUpgradeWebSocket(nsIURI* aURI,
|
2020-08-11 14:19:28 +03:00
|
|
|
nsILoadInfo* aLoadInfo) {
|
2020-04-29 18:52:19 +03:00
|
|
|
// 1. Check if the HTTPS-Only Mode is even enabled, before we do anything else
|
2020-08-11 14:19:28 +03:00
|
|
|
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
|
|
|
if (!IsHttpsOnlyModeEnabled(isPrivateWin)) {
|
2020-04-29 18:52:19 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2. Check for general exceptions
|
|
|
|
if (OnionException(aURI) || LoopbackOrLocalException(aURI)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. Check if NoUpgrade-flag is set in LoadInfo
|
2020-08-11 14:19:28 +03:00
|
|
|
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
|
|
|
|
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT) {
|
2020-04-29 18:52:19 +03:00
|
|
|
// Let's log to the console, that we didn't upgrade this request
|
|
|
|
AutoTArray<nsString, 1> params = {
|
|
|
|
NS_ConvertUTF8toUTF16(aURI->GetSpecOrDefault())};
|
2020-08-11 14:19:28 +03:00
|
|
|
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyNoUpgradeException", params,
|
2020-08-14 03:22:59 +03:00
|
|
|
nsIScriptError::infoFlag, aLoadInfo,
|
|
|
|
aURI);
|
2020-04-29 18:52:19 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We can upgrade the request - let's log it to the console
|
|
|
|
// Appending an 's' to the scheme for the logging. (ws -> wss)
|
|
|
|
nsAutoCString scheme;
|
|
|
|
aURI->GetScheme(scheme);
|
|
|
|
scheme.AppendLiteral("s");
|
|
|
|
NS_ConvertUTF8toUTF16 reportSpec(aURI->GetSpecOrDefault());
|
|
|
|
NS_ConvertUTF8toUTF16 reportScheme(scheme);
|
|
|
|
|
|
|
|
AutoTArray<nsString, 2> params = {reportSpec, reportScheme};
|
2020-08-11 14:19:28 +03:00
|
|
|
nsHTTPSOnlyUtils::LogLocalizedString("HTTPSOnlyUpgradeRequest", params,
|
2020-08-14 03:22:59 +03:00
|
|
|
nsIScriptError::warningFlag, aLoadInfo,
|
|
|
|
aURI);
|
2020-03-17 22:24:31 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-05-26 14:45:21 +03:00
|
|
|
/* static */
|
2020-08-05 18:18:01 +03:00
|
|
|
bool nsHTTPSOnlyUtils::CouldBeHttpsOnlyError(nsIChannel* aChannel,
|
|
|
|
nsresult aError) {
|
|
|
|
// If there is no failed channel, then there is nothing to do here.
|
|
|
|
if (!aChannel) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If HTTPS-Only Mode is not enabled, then there is nothing to do here.
|
|
|
|
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
|
|
|
|
bool isPrivateWin = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
|
|
|
if (!IsHttpsOnlyModeEnabled(isPrivateWin)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-26 18:22:40 +03:00
|
|
|
// httpsOnlyStatus is reset to it's default value in the child-process after
|
|
|
|
// our forced timeout. Until we figure out why it's reset (bug 1661275) we
|
|
|
|
// have this workaround:
|
2020-08-12 15:57:22 +03:00
|
|
|
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
|
2020-08-26 18:22:40 +03:00
|
|
|
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED &&
|
|
|
|
!XRE_IsParentProcess() && aError == NS_ERROR_NET_TIMEOUT) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the load is exempt or did not get upgraded,
|
|
|
|
// then there is nothing to do here.
|
|
|
|
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_EXEMPT ||
|
|
|
|
httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_UNINITIALIZED) {
|
2020-08-05 18:18:01 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If it's one of those errors, then most likely it's not a HTTPS-Only error
|
|
|
|
// (This list of errors is largely drawn from nsDocShell::DisplayLoadError())
|
2020-05-26 14:45:21 +03:00
|
|
|
return !(NS_ERROR_UNKNOWN_PROTOCOL == aError ||
|
|
|
|
NS_ERROR_FILE_NOT_FOUND == aError ||
|
|
|
|
NS_ERROR_FILE_ACCESS_DENIED == aError ||
|
|
|
|
NS_ERROR_UNKNOWN_HOST == aError || NS_ERROR_PHISHING_URI == aError ||
|
|
|
|
NS_ERROR_MALWARE_URI == aError || NS_ERROR_UNWANTED_URI == aError ||
|
|
|
|
NS_ERROR_HARMFUL_URI == aError ||
|
|
|
|
NS_ERROR_CONTENT_CRASHED == aError ||
|
|
|
|
NS_ERROR_FRAME_CRASHED == aError);
|
|
|
|
}
|
|
|
|
|
2020-07-08 15:06:02 +03:00
|
|
|
/* static */
|
2020-08-10 11:51:44 +03:00
|
|
|
void nsHTTPSOnlyUtils::TestSitePermissionAndPotentiallyAddExemption(
|
|
|
|
nsIChannel* aChannel) {
|
|
|
|
NS_ENSURE_TRUE_VOID(aChannel);
|
|
|
|
|
|
|
|
// if https-only mode is not enabled, then there is nothing to do here.
|
|
|
|
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
|
|
|
|
bool isPrivateWin = loadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
|
|
|
if (!IsHttpsOnlyModeEnabled(isPrivateWin)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if it's not a top-level load then there is nothing to here.
|
|
|
|
nsContentPolicyType type = loadInfo->GetExternalContentPolicyType();
|
|
|
|
if (type != nsIContentPolicy::TYPE_DOCUMENT) {
|
|
|
|
return;
|
2020-08-07 15:50:31 +03:00
|
|
|
}
|
|
|
|
|
2020-08-10 11:51:44 +03:00
|
|
|
// it it's not an http channel, then there is nothing to do here.
|
|
|
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
|
|
|
|
if (!httpChannel) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPrincipal> principal;
|
|
|
|
nsresult rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
|
|
|
|
aChannel, getter_AddRefs(principal));
|
|
|
|
NS_ENSURE_SUCCESS_VOID(rv);
|
|
|
|
|
2020-07-08 15:06:02 +03:00
|
|
|
nsCOMPtr<nsIPermissionManager> permMgr =
|
|
|
|
mozilla::services::GetPermissionManager();
|
2020-08-10 11:51:44 +03:00
|
|
|
NS_ENSURE_TRUE_VOID(permMgr);
|
2020-07-08 15:06:02 +03:00
|
|
|
|
|
|
|
uint32_t perm;
|
2020-08-10 11:51:44 +03:00
|
|
|
rv = permMgr->TestExactPermissionFromPrincipal(
|
|
|
|
principal, "https-only-load-insecure"_ns, &perm);
|
|
|
|
NS_ENSURE_SUCCESS_VOID(rv);
|
2020-07-08 15:06:02 +03:00
|
|
|
|
2020-08-10 11:51:44 +03:00
|
|
|
bool isHttpsOnlyExempt =
|
|
|
|
perm == nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW ||
|
|
|
|
perm == nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW_SESSION;
|
|
|
|
|
|
|
|
// We explicitly add or also remove the exemption flag, because this
|
|
|
|
// function is also consulted after redirects.
|
|
|
|
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
|
|
|
|
if (isHttpsOnlyExempt) {
|
|
|
|
httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT;
|
|
|
|
} else {
|
|
|
|
httpsOnlyStatus &= ~nsILoadInfo::HTTPS_ONLY_EXEMPT;
|
|
|
|
}
|
|
|
|
loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
|
2020-07-08 15:06:02 +03:00
|
|
|
}
|
|
|
|
|
2020-08-17 19:35:09 +03:00
|
|
|
/* static */
|
|
|
|
bool nsHTTPSOnlyUtils::IsSafeToAcceptCORSOrMixedContent(
|
|
|
|
nsILoadInfo* aLoadInfo) {
|
|
|
|
// Check if the request is exempt from upgrades
|
|
|
|
if ((aLoadInfo->GetHttpsOnlyStatus() & nsILoadInfo::HTTPS_ONLY_EXEMPT)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check if HTTPS-Only Mode is enabled for this request
|
|
|
|
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
|
|
|
return nsHTTPSOnlyUtils::IsHttpsOnlyModeEnabled(isPrivateWin);
|
|
|
|
}
|
|
|
|
|
2020-04-29 18:52:19 +03:00
|
|
|
/* ------ Logging ------ */
|
2020-03-17 22:24:31 +03:00
|
|
|
|
|
|
|
/* static */
|
2020-08-14 03:22:59 +03:00
|
|
|
void nsHTTPSOnlyUtils::LogLocalizedString(const char* aName,
|
|
|
|
const nsTArray<nsString>& aParams,
|
|
|
|
uint32_t aFlags,
|
|
|
|
nsILoadInfo* aLoadInfo,
|
|
|
|
nsIURI* aURI) {
|
2020-03-17 22:24:31 +03:00
|
|
|
nsAutoString logMsg;
|
|
|
|
nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES,
|
|
|
|
aName, aParams, logMsg);
|
2020-08-14 03:22:59 +03:00
|
|
|
LogMessage(logMsg, aFlags, aLoadInfo, aURI);
|
2020-03-17 22:24:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
void nsHTTPSOnlyUtils::LogMessage(const nsAString& aMessage, uint32_t aFlags,
|
2020-08-14 03:22:59 +03:00
|
|
|
nsILoadInfo* aLoadInfo, nsIURI* aURI) {
|
|
|
|
// do not log to the console if the loadinfo says we should not!
|
|
|
|
uint32_t httpsOnlyStatus = aLoadInfo->GetHttpsOnlyStatus();
|
|
|
|
if (httpsOnlyStatus & nsILoadInfo::HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-17 22:24:31 +03:00
|
|
|
// Prepending HTTPS-Only to the outgoing console message
|
|
|
|
nsString message;
|
|
|
|
message.AppendLiteral(u"HTTPS-Only Mode: ");
|
|
|
|
message.Append(aMessage);
|
|
|
|
|
|
|
|
// Allow for easy distinction in devtools code.
|
|
|
|
nsCString category("HTTPSOnly");
|
|
|
|
|
2020-08-14 03:22:59 +03:00
|
|
|
uint32_t innerWindowId = aLoadInfo->GetInnerWindowID();
|
|
|
|
if (innerWindowId > 0) {
|
2020-03-17 22:24:31 +03:00
|
|
|
// Send to content console
|
|
|
|
nsContentUtils::ReportToConsoleByWindowID(message, aFlags, category,
|
2020-08-14 03:22:59 +03:00
|
|
|
innerWindowId, aURI);
|
2020-03-17 22:24:31 +03:00
|
|
|
} else {
|
|
|
|
// Send to browser console
|
2020-08-14 03:22:59 +03:00
|
|
|
bool isPrivateWin = aLoadInfo->GetOriginAttributes().mPrivateBrowsingId > 0;
|
|
|
|
nsContentUtils::LogSimpleConsoleError(message, category.get(), isPrivateWin,
|
|
|
|
true /* from chrome context */,
|
|
|
|
aFlags);
|
2020-03-17 22:24:31 +03:00
|
|
|
}
|
|
|
|
}
|
2020-04-29 18:52:19 +03:00
|
|
|
|
|
|
|
/* ------ Exceptions ------ */
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
bool nsHTTPSOnlyUtils::OnionException(nsIURI* aURI) {
|
|
|
|
// Onion-host exception can get disabled with a pref
|
|
|
|
if (mozilla::StaticPrefs::dom_security_https_only_mode_upgrade_onion()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
nsAutoCString host;
|
|
|
|
aURI->GetHost(host);
|
2020-07-01 11:29:29 +03:00
|
|
|
return StringEndsWith(host, ".onion"_ns);
|
2020-04-29 18:52:19 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* static */
|
|
|
|
bool nsHTTPSOnlyUtils::LoopbackOrLocalException(nsIURI* aURI) {
|
|
|
|
nsAutoCString asciiHost;
|
|
|
|
nsresult rv = aURI->GetAsciiHost(asciiHost);
|
|
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
|
2020-08-05 18:18:01 +03:00
|
|
|
// Let's make a quick check if the host matches these loopback strings
|
|
|
|
// before we do anything else
|
2020-04-29 18:52:19 +03:00
|
|
|
if (asciiHost.EqualsLiteral("localhost") || asciiHost.EqualsLiteral("::1")) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The local-ip and loopback checks expect a NetAddr struct. We only have a
|
|
|
|
// host-string but can convert it to a NetAddr by first converting it to
|
|
|
|
// PRNetAddr.
|
|
|
|
PRNetAddr tempAddr;
|
|
|
|
memset(&tempAddr, 0, sizeof(PRNetAddr));
|
|
|
|
// PR_StringToNetAddr does not properly initialize the output buffer in the
|
|
|
|
// case of IPv6 input. See bug 223145.
|
|
|
|
if (PR_StringToNetAddr(asciiHost.get(), &tempAddr) != PR_SUCCESS) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-09-01 10:22:14 +03:00
|
|
|
mozilla::net::NetAddr addr(&tempAddr);
|
2020-04-29 18:52:19 +03:00
|
|
|
// Loopback IPs are always exempt
|
|
|
|
if (IsLoopBackAddress(&addr)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Local IP exception can get disabled with a pref
|
|
|
|
bool upgradeLocal =
|
|
|
|
mozilla::StaticPrefs::dom_security_https_only_mode_upgrade_local();
|
|
|
|
return (!upgradeLocal && IsIPAddrLocal(&addr));
|
|
|
|
}
|
2020-08-12 15:57:22 +03:00
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////
|
|
|
|
// Implementation of TestHTTPAnswerRunnable
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS_INHERITED(TestHTTPAnswerRunnable, mozilla::Runnable,
|
|
|
|
nsIStreamListener, nsIInterfaceRequestor,
|
|
|
|
nsITimerCallback)
|
|
|
|
|
|
|
|
TestHTTPAnswerRunnable::TestHTTPAnswerRunnable(
|
|
|
|
nsIURI* aURI, mozilla::net::DocumentLoadListener* aDocumentLoadListener)
|
|
|
|
: mozilla::Runnable("TestHTTPAnswerRunnable"),
|
|
|
|
mURI(aURI),
|
|
|
|
mDocumentLoadListener(aDocumentLoadListener) {}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TestHTTPAnswerRunnable::OnStartRequest(nsIRequest* aRequest) {
|
|
|
|
// If the request status is not OK, it means it encountered some
|
|
|
|
// kind of error in which case we do not want to do anything.
|
|
|
|
nsresult requestStatus;
|
|
|
|
aRequest->GetStatus(&requestStatus);
|
|
|
|
if (requestStatus != NS_OK) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the original top-level channel which https-only is trying
|
|
|
|
// to upgrade is already in progress. If it is, then all good, if not
|
|
|
|
// then let's cancel that channel so we can dispaly the exception page.
|
|
|
|
nsCOMPtr<nsIChannel> httpsOnlyChannel = mDocumentLoadListener->GetChannel();
|
|
|
|
if (httpsOnlyChannel) {
|
|
|
|
nsCOMPtr<nsILoadInfo> loadInfo = httpsOnlyChannel->LoadInfo();
|
|
|
|
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
|
|
|
|
if (!(httpsOnlyStatus &
|
|
|
|
nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS)) {
|
|
|
|
// Only really cancel the original top-level channel if it's
|
|
|
|
// status is still NS_OK, otherwise it might have already
|
|
|
|
// encountered some other error and was cancelled.
|
|
|
|
nsresult httpsOnlyChannelStatus;
|
|
|
|
httpsOnlyChannel->GetStatus(&httpsOnlyChannelStatus);
|
|
|
|
if (httpsOnlyChannelStatus == NS_OK) {
|
|
|
|
mDocumentLoadListener->Cancel(NS_ERROR_NET_TIMEOUT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cancel this http request because it has reached the end of it's
|
|
|
|
// lifetime at this point.
|
|
|
|
aRequest->Cancel(NS_ERROR_ABORT);
|
|
|
|
return NS_ERROR_ABORT;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TestHTTPAnswerRunnable::OnDataAvailable(nsIRequest* aRequest,
|
|
|
|
nsIInputStream* aStream,
|
|
|
|
uint64_t aOffset, uint32_t aCount) {
|
|
|
|
// TestHTTPAnswerRunnable only cares about ::OnStartRequest which
|
|
|
|
// will also cancel the request, so we should in fact never even
|
|
|
|
// get here.
|
|
|
|
MOZ_ASSERT(false, "how come we get to ::OnDataAvailable");
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TestHTTPAnswerRunnable::OnStopRequest(nsIRequest* aRequest,
|
|
|
|
nsresult aStatusCode) {
|
|
|
|
// TestHTTPAnswerRunnable only cares about ::OnStartRequest
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TestHTTPAnswerRunnable::GetInterface(const nsIID& aIID, void** aResult) {
|
|
|
|
return QueryInterface(aIID, aResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TestHTTPAnswerRunnable::Run() {
|
|
|
|
// Wait N milliseconds to give the original https request a heads start
|
|
|
|
// before firing up this http request in the background.
|
|
|
|
return NS_NewTimerWithCallback(getter_AddRefs(mTimer), this,
|
|
|
|
FIRE_HTTP_REQUEST_BACKGROUND_TIMER_MS,
|
|
|
|
nsITimer::TYPE_ONE_SHOT);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TestHTTPAnswerRunnable::Notify(nsITimer* aTimer) {
|
|
|
|
if (mTimer) {
|
|
|
|
mTimer->Cancel();
|
|
|
|
mTimer = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the original channel has already started loading at this point
|
|
|
|
// then there is no need to do the dance.
|
|
|
|
nsCOMPtr<nsIChannel> origChannel = mDocumentLoadListener->GetChannel();
|
|
|
|
nsCOMPtr<nsILoadInfo> origLoadInfo = origChannel->LoadInfo();
|
|
|
|
uint32_t origHttpsOnlyStatus = origLoadInfo->GetHttpsOnlyStatus();
|
|
|
|
if ((origHttpsOnlyStatus &
|
|
|
|
nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS)) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
OriginAttributes attrs = origLoadInfo->GetOriginAttributes();
|
|
|
|
RefPtr<nsIPrincipal> nullPrincipal =
|
|
|
|
mozilla::NullPrincipal::CreateWithInheritedAttributes(attrs);
|
|
|
|
|
|
|
|
uint32_t loadFlags =
|
|
|
|
nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
|
|
|
|
nsIRequest::INHIBIT_PERSISTENT_CACHING | nsIRequest::LOAD_BYPASS_CACHE |
|
|
|
|
nsIChannel::LOAD_BYPASS_SERVICE_WORKER;
|
|
|
|
|
|
|
|
// we are using TYPE_OTHER because TYPE_DOCUMENT might have side effects
|
|
|
|
nsCOMPtr<nsIChannel> testHTTPChannel;
|
|
|
|
nsresult rv =
|
|
|
|
NS_NewChannel(getter_AddRefs(testHTTPChannel), mURI, nullPrincipal,
|
|
|
|
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
|
|
|
|
nsIContentPolicy::TYPE_OTHER, nullptr, nullptr, nullptr,
|
|
|
|
nullptr, loadFlags);
|
|
|
|
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have exempt that load from HTTPS-Only to avoid getting upgraded
|
2020-08-14 03:22:59 +03:00
|
|
|
// to https as well. Additonally let's not log that request to the console
|
|
|
|
// because it might confuse end users.
|
2020-08-12 15:57:22 +03:00
|
|
|
nsCOMPtr<nsILoadInfo> loadInfo = testHTTPChannel->LoadInfo();
|
|
|
|
uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
|
2020-08-14 03:22:59 +03:00
|
|
|
httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_EXEMPT |
|
|
|
|
nsILoadInfo::HTTPS_ONLY_DO_NOT_LOG_TO_CONSOLE;
|
2020-08-12 15:57:22 +03:00
|
|
|
loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
|
|
|
|
|
|
|
|
testHTTPChannel->SetNotificationCallbacks(this);
|
|
|
|
testHTTPChannel->AsyncOpen(this);
|
|
|
|
return NS_OK;
|
|
|
|
}
|