From 39d5e413845169865d1c52c71a71f365d837316c Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 2 Nov 2015 11:27:00 -0500 Subject: [PATCH] Bug 1214305 - Part 1: Refactor the logic for querying whether a connection should go through a secure upgrade into NS_ShouldSecureUpgrade; r=mcmanus --- netwerk/base/nsNetUtil.cpp | 96 +++++++++++++++++++++++++ netwerk/base/nsNetUtil.h | 10 +++ netwerk/protocol/http/nsHttpChannel.cpp | 91 ++++------------------- 3 files changed, 121 insertions(+), 76 deletions(-) diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp index 1174f9d627a7..843a153ed162 100644 --- a/netwerk/base/nsNetUtil.cpp +++ b/netwerk/base/nsNetUtil.cpp @@ -4,6 +4,9 @@ * 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/. */ +// HttpLog.h should generally be included first +#include "HttpLog.h" + #include "mozilla/LoadContext.h" #include "mozilla/LoadInfo.h" #include "mozilla/BasePrincipal.h" @@ -57,6 +60,10 @@ #include "nsInterfaceRequestorAgg.h" #include "plstr.h" #include "nsINestedURI.h" +#include "mozilla/dom/nsCSPUtils.h" +#include "nsIScriptError.h" +#include "nsISiteSecurityService.h" +#include "nsHttpHandler.h" #ifdef MOZ_WIDGET_GONK #include "nsINetworkManager.h" @@ -2141,6 +2148,95 @@ NS_IsSrcdocChannel(nsIChannel *aChannel) return false; } +nsresult +NS_ShouldSecureUpgrade(nsIURI* aURI, + nsILoadInfo* aLoadInfo, + nsIPrincipal* aChannelResultPrincipal, + bool aPrivateBrowsing, + bool aAllowSTS, + bool& aShouldUpgrade) +{ + // Even if we're in private browsing mode, we still enforce existing STS + // data (it is read-only). + // if the connection is not using SSL and either the exact host matches or + // a superdomain wants to force HTTPS, do it. + bool isHttps = false; + nsresult rv = aURI->SchemeIs("https", &isHttps); + NS_ENSURE_SUCCESS(rv, rv); + + if (!isHttps) { + // If any of the documents up the chain to the root doucment makes use of + // the CSP directive 'upgrade-insecure-requests', then it's time to fulfill + // the promise to CSP and mixed content blocking to upgrade the channel + // from http to https. + if (aLoadInfo && aLoadInfo->GetUpgradeInsecureRequests()) { + // Please note that cross origin top level navigations are not subject + // to upgrade-insecure-requests, see: + // http://www.w3.org/TR/upgrade-insecure-requests/#examples + bool crossOriginNavigation = + (aLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) && + (!aChannelResultPrincipal->Equals(aLoadInfo->LoadingPrincipal())); + + if (!crossOriginNavigation) { + // let's log a message to the console that we are upgrading a request + nsAutoCString spec, scheme; + aURI->GetSpec(spec); + aURI->GetScheme(scheme); + // append the additional 's' for security to the scheme :-) + scheme.AppendASCII("s"); + NS_ConvertUTF8toUTF16 reportSpec(spec); + NS_ConvertUTF8toUTF16 reportScheme(scheme); + + const char16_t* params[] = { reportSpec.get(), reportScheme.get() }; + uint32_t innerWindowId = aLoadInfo->GetInnerWindowID(); + CSP_LogLocalizedStr(MOZ_UTF16("upgradeInsecureRequest"), + params, ArrayLength(params), + EmptyString(), // aSourceFile + EmptyString(), // aScriptSample + 0, // aLineNumber + 0, // aColumnNumber + nsIScriptError::warningFlag, "CSP", + innerWindowId); + + Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 4); + aShouldUpgrade = true; + return NS_OK; + } + } + + // enforce Strict-Transport-Security + nsISiteSecurityService* sss = gHttpHandler->GetSSService(); + NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY); + + bool isStsHost = false; + uint32_t flags = aPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; + rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, flags, + &isStsHost); + + // if the SSS check fails, it's likely because this load is on a + // malformed URI or something else in the setup is wrong, so any error + // should be reported. + NS_ENSURE_SUCCESS(rv, rv); + + if (isStsHost) { + LOG(("nsHttpChannel::Connect() STS permissions found\n")); + if (aAllowSTS) { + Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 3); + aShouldUpgrade = true; + return NS_OK; + } else { + Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 2); + } + } else { + Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 1); + } + } else { + Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 0); + } + aShouldUpgrade = false; + return NS_OK; +} + namespace mozilla { namespace net { diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h index 3bebd80ae759..776c24b64e66 100644 --- a/netwerk/base/nsNetUtil.h +++ b/netwerk/base/nsNetUtil.h @@ -965,6 +965,16 @@ bool NS_IsReasonableHTTPHeaderValue(const nsACString &aValue); */ bool NS_IsValidHTTPToken(const nsACString &aToken); +/** + * Return true if the given request must be upgraded to HTTPS. + */ +nsresult NS_ShouldSecureUpgrade(nsIURI* aURI, + nsILoadInfo* aLoadInfo, + nsIPrincipal* aChannelResultPrincipal, + bool aPrivateBrowsing, + bool aAllowSTS, + bool& aShouldUpgrade); + namespace mozilla { namespace net { diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 9ae5d3826661..ccad0d5e593e 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -7,7 +7,6 @@ // HttpLog.h should generally be included first #include "HttpLog.h" -#include "mozilla/dom/nsCSPUtils.h" #include "mozilla/dom/nsCSPContext.h" #include "nsHttp.h" #include "nsHttpChannel.h" @@ -56,7 +55,6 @@ #include "nsIClassOfService.h" #include "nsIPermissionManager.h" #include "nsIPrincipal.h" -#include "nsIScriptError.h" #include "nsIScriptSecurityManager.h" #include "nsISSLStatus.h" #include "nsISSLStatusProvider.h" @@ -319,83 +317,24 @@ nsHttpChannel::Connect() LOG(("nsHttpChannel::Connect [this=%p]\n", this)); - // Even if we're in private browsing mode, we still enforce existing STS - // data (it is read-only). - // if the connection is not using SSL and either the exact host matches or - // a superdomain wants to force HTTPS, do it. bool isHttps = false; rv = mURI->SchemeIs("https", &isHttps); NS_ENSURE_SUCCESS(rv,rv); - - if (!isHttps) { - // If any of the documents up the chain to the root doucment makes use of - // the CSP directive 'upgrade-insecure-requests', then it's time to fulfill - // the promise to CSP and mixed content blocking to upgrade the channel - // from http to https. - if (mLoadInfo && mLoadInfo->GetUpgradeInsecureRequests()) { - // Please note that cross origin top level navigations are not subject - // to upgrade-insecure-requests, see: - // http://www.w3.org/TR/upgrade-insecure-requests/#examples - nsCOMPtr resultPrincipal; - nsContentUtils::GetSecurityManager()-> - GetChannelResultPrincipal(this, getter_AddRefs(resultPrincipal)); - bool crossOriginNavigation = - (mLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DOCUMENT) && - (!resultPrincipal->Equals(mLoadInfo->LoadingPrincipal())); - - if (!crossOriginNavigation) { - // let's log a message to the console that we are upgrading a request - nsAutoCString spec, scheme; - mURI->GetSpec(spec); - mURI->GetScheme(scheme); - // append the additional 's' for security to the scheme :-) - scheme.AppendASCII("s"); - NS_ConvertUTF8toUTF16 reportSpec(spec); - NS_ConvertUTF8toUTF16 reportScheme(scheme); - - const char16_t* params[] = { reportSpec.get(), reportScheme.get() }; - uint32_t innerWindowId = mLoadInfo ? mLoadInfo->GetInnerWindowID() : 0; - CSP_LogLocalizedStr(MOZ_UTF16("upgradeInsecureRequest"), - params, ArrayLength(params), - EmptyString(), // aSourceFile - EmptyString(), // aScriptSample - 0, // aLineNumber - 0, // aColumnNumber - nsIScriptError::warningFlag, "CSP", - innerWindowId); - - Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 4); - return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps); - } - } - - // enforce Strict-Transport-Security - nsISiteSecurityService* sss = gHttpHandler->GetSSService(); - NS_ENSURE_TRUE(sss, NS_ERROR_OUT_OF_MEMORY); - - bool isStsHost = false; - uint32_t flags = mPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0; - rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, mURI, flags, - &isStsHost); - - // if the SSS check fails, it's likely because this load is on a - // malformed URI or something else in the setup is wrong, so any error - // should be reported. - NS_ENSURE_SUCCESS(rv, rv); - - if (isStsHost) { - LOG(("nsHttpChannel::Connect() STS permissions found\n")); - if (mAllowSTS) { - Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 3); - return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps); - } else { - Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 2); - } - } else { - Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 1); - } - } else { - Telemetry::Accumulate(Telemetry::HTTP_SCHEME_UPGRADE, 0); + nsCOMPtr resultPrincipal; + if (!isHttps && mLoadInfo && mLoadInfo->GetUpgradeInsecureRequests()) { + nsContentUtils::GetSecurityManager()-> + GetChannelResultPrincipal(this, getter_AddRefs(resultPrincipal)); + } + bool shouldUpgrade = false; + rv = NS_ShouldSecureUpgrade(mURI, + mLoadInfo, + resultPrincipal, + mPrivateBrowsing, + mAllowSTS, + shouldUpgrade); + NS_ENSURE_SUCCESS(rv, rv); + if (shouldUpgrade) { + return AsyncCall(&nsHttpChannel::HandleAsyncRedirectChannelToHttps); } // ensure that we are using a valid hostname