From: uazo Date: Thu, 16 Mar 2023 14:17:22 +0000 Subject: Partition HSTS cache by NAK License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html --- .../browser/ssl/https_upgrades_interceptor.cc | 1 + .../webui/net_internals/net_internals_ui.cc | 14 ++- .../core/browser/hsts_query.cc | 1 + .../web_package/signed_exchange_handler.cc | 7 +- .../web_package/signed_exchange_handler.h | 2 + .../web_package/signed_exchange_loader.cc | 2 +- net/http/transport_security_state.cc | 106 ++++++++++++------ net/http/transport_security_state.h | 38 ++++--- net/quic/crypto/proof_verifier_chromium.cc | 5 +- net/quic/quic_chromium_client_session.cc | 2 +- net/socket/ssl_client_socket_impl.cc | 5 +- net/spdy/spdy_session.cc | 11 +- net/spdy/spdy_session.h | 3 +- .../url_request_context_builder.cc | 14 --- net/url_request/url_request_http_job.cc | 7 +- services/network/network_context.cc | 31 +++-- services/network/network_context.h | 13 ++- .../public/mojom/network_context.mojom | 9 +- 18 files changed, 170 insertions(+), 101 deletions(-) diff --git a/chrome/browser/ssl/https_upgrades_interceptor.cc b/chrome/browser/ssl/https_upgrades_interceptor.cc --- a/chrome/browser/ssl/https_upgrades_interceptor.cc +++ b/chrome/browser/ssl/https_upgrades_interceptor.cc @@ -362,6 +362,7 @@ void HttpsUpgradesInterceptor::MaybeCreateLoader( network::mojom::NetworkContext* network_context = profile->GetDefaultStoragePartition()->GetNetworkContext(); network_context->IsHSTSActiveForHost( + tentative_resource_request.trusted_params->isolation_info.network_anonymization_key(), tentative_resource_request.url.host(), mojo::WrapCallbackWithDefaultInvokeIfNotRun( std::move(query_complete_callback), diff --git a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc --- a/chrome/browser/ui/webui/net_internals/net_internals_ui.cc +++ b/chrome/browser/ui/webui/net_internals/net_internals_ui.cc @@ -407,7 +407,10 @@ void NetInternalsMessageHandler::OnDomainSecurityPolicyDelete( // There cannot be a unicode entry in the HSTS set. return; } + url::Origin unsafe_origin = url::Origin::CreateFromNormalizedTuple( + "https", *domain, 443); GetNetworkContext()->DeleteDynamicDataForHost( + net::IsolationInfo::CreateForInternalRequest(unsafe_origin).network_anonymization_key(), *domain, base::BindOnce(&IgnoreBoolCallback)); } @@ -417,7 +420,10 @@ void NetInternalsMessageHandler::OnHSTSQuery(const base::Value::List& list) { DCHECK(callback_id && domain); AllowJavascript(); + url::Origin unsafe_origin = url::Origin::CreateFromNormalizedTuple( + "https", *domain, 443); GetNetworkContext()->GetHSTSState( + net::IsolationInfo::CreateForInternalRequest(unsafe_origin).network_anonymization_key(), *domain, base::BindOnce(&NetInternalsMessageHandler::ResolveCallbackWithResult, weak_factory_.GetWeakPtr(), *callback_id)); @@ -443,8 +449,12 @@ void NetInternalsMessageHandler::OnHSTSAdd(const base::Value::List& list) { const bool sts_include_subdomains = list[1].GetBool(); base::Time expiry = base::Time::Now() + base::Days(1000); - GetNetworkContext()->AddHSTS(*domain, expiry, sts_include_subdomains, - base::DoNothing()); + url::Origin unsafe_origin = url::Origin::CreateFromNormalizedTuple( + "https", *domain, 443); + GetNetworkContext()->AddHSTS( + net::IsolationInfo::CreateForInternalRequest(unsafe_origin).network_anonymization_key(), + *domain, expiry, sts_include_subdomains, + base::DoNothing()); } void NetInternalsMessageHandler::OnFlushSocketPools( diff --git a/components/password_manager/core/browser/hsts_query.cc b/components/password_manager/core/browser/hsts_query.cc --- a/components/password_manager/core/browser/hsts_query.cc +++ b/components/password_manager/core/browser/hsts_query.cc @@ -58,6 +58,7 @@ void PostHSTSQueryForHostAndNetworkContext( scoped_refptr callback_helper = base::MakeRefCounted(std::move(callback)); network_context->IsHSTSActiveForHost( + net::IsolationInfo::CreateForInternalRequest(origin).network_anonymization_key(), origin.host(), mojo::WrapCallbackWithDropHandler( base::BindOnce(&HSTSCallbackHelper::ReportResult, callback_helper), diff --git a/content/browser/web_package/signed_exchange_handler.cc b/content/browser/web_package/signed_exchange_handler.cc --- a/content/browser/web_package/signed_exchange_handler.cc +++ b/content/browser/web_package/signed_exchange_handler.cc @@ -88,6 +88,7 @@ using VerifyCallback = void VerifyCert(const scoped_refptr& certificate, const GURL& url, + const net::NetworkAnonymizationKey& network_anonymization_key, const std::string& ocsp_result, const std::string& sct_list, int frame_tree_node_id, @@ -109,7 +110,7 @@ void VerifyCert(const scoped_refptr& certificate, } network_context->VerifyCertForSignedExchange( - certificate, url, ocsp_result, sct_list, std::move(wrapped_callback)); + certificate, url, network_anonymization_key, ocsp_result, sct_list, std::move(wrapped_callback)); } std::string OCSPErrorToString(const bssl::OCSPVerifyResult& ocsp_result) { @@ -172,6 +173,7 @@ SignedExchangeHandler::SignedExchangeHandler( std::unique_ptr body, ExchangeHeadersCallback headers_callback, std::unique_ptr cert_fetcher_factory, + const net::NetworkAnonymizationKey& network_anonymization_key, const std::optional outer_request_isolation_info, int load_flags, const net::IPEndPoint& remote_endpoint, @@ -185,6 +187,7 @@ SignedExchangeHandler::SignedExchangeHandler( source_(std::move(body)), cert_fetcher_factory_(std::move(cert_fetcher_factory)), devtools_proxy_(std::move(devtools_proxy)), + network_anonymization_key_(network_anonymization_key), outer_request_isolation_info_(std::move(outer_request_isolation_info)), load_flags_(load_flags), remote_endpoint_(remote_endpoint), @@ -528,7 +531,7 @@ void SignedExchangeHandler::OnCertReceived( // property, or const std::string& stapled_ocsp_response = unverified_cert_chain_->ocsp(); - VerifyCert(certificate, url, stapled_ocsp_response, sct_list_from_cert_cbor, + VerifyCert(certificate, url, network_anonymization_key_, stapled_ocsp_response, sct_list_from_cert_cbor, frame_tree_node_id_, base::BindOnce(&SignedExchangeHandler::OnVerifyCert, weak_factory_.GetWeakPtr())); diff --git a/content/browser/web_package/signed_exchange_handler.h b/content/browser/web_package/signed_exchange_handler.h --- a/content/browser/web_package/signed_exchange_handler.h +++ b/content/browser/web_package/signed_exchange_handler.h @@ -102,6 +102,7 @@ class CONTENT_EXPORT SignedExchangeHandler { std::unique_ptr body, ExchangeHeadersCallback headers_callback, std::unique_ptr cert_fetcher_factory, + const net::NetworkAnonymizationKey& network_anonymization_key, const std::optional outer_request_isolation_info, int load_flags, const net::IPEndPoint& remote_endpoint, @@ -191,6 +192,7 @@ class CONTENT_EXPORT SignedExchangeHandler { // `cert_fetcher_` borrows reference from `devtools_proxy_`, so it needs to be // declared last, so that it is destroyed first. std::unique_ptr cert_fetcher_; + const net::NetworkAnonymizationKey network_anonymization_key_; std::optional outer_request_isolation_info_; const int load_flags_ = 0; const net::IPEndPoint remote_endpoint_; diff --git a/content/browser/web_package/signed_exchange_loader.cc b/content/browser/web_package/signed_exchange_loader.cc --- a/content/browser/web_package/signed_exchange_loader.cc +++ b/content/browser/web_package/signed_exchange_loader.cc @@ -119,7 +119,7 @@ SignedExchangeLoader::SignedExchangeLoader( std::move(outer_response_body)), base::BindOnce(&SignedExchangeLoader::OnHTTPExchangeFound, weak_factory_.GetWeakPtr()), - std::move(cert_fetcher_factory), + std::move(cert_fetcher_factory), network_anonymization_key, outer_request_.trusted_params ? std::make_optional(outer_request_.trusted_params->isolation_info) : std::nullopt, diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc --- a/net/http/transport_security_state.cc +++ b/net/http/transport_security_state.cc @@ -86,7 +86,7 @@ bool AddHash(const char* sha256_hash, HashValueVector* out) { // Converts |hostname| from dotted form ("www.google.com") to the form // used in DNS: "\x03www\x06google\x03com", lowercases that, and returns // the result. -std::vector CanonicalizeHost(const std::string& host) { +std::vector CanonicalizeHostWithoutNak(const std::string& host) { // We cannot perform the operations as detailed in the spec here as `host` // has already undergone IDN processing before it reached us. Thus, we // lowercase the input (probably redudnant since most input here has been @@ -107,6 +107,29 @@ std::vector CanonicalizeHost(const std::string& host) { return new_host.value(); } +std::vector CanonicalizeHost(const NetworkAnonymizationKey& nak, + const std::string& host) { + std::vector hostname = CanonicalizeHostWithoutNak(host); + if (hostname.empty()) { + return hostname; + } + + // esclude opaque or transient nak + if (!nak.IsFullyPopulated() || nak.IsTransient()) + return std::vector(); + + std::string lowered_host = base::ToLowerASCII( + nak.ToDebugString() + " " + host); + std::vector vector = + std::vector(lowered_host.begin(), lowered_host.end()); + if (vector.size() > 254) + return std::vector(); + + vector.emplace(vector.begin(), vector.size()); + vector.emplace(vector.end(), 0); + return vector; +} + // PreloadResult is the result of resolving a specific name in the preloaded // data. struct PreloadResult { @@ -198,7 +221,7 @@ bool DecodeHSTSPreload(const std::string& search_hostname, PreloadResult* out) { // Ensure that |search_hostname| is a valid hostname before // processing. - if (CanonicalizeHost(search_hostname).empty()) { + if (CanonicalizeHostWithoutNak(search_hostname).empty()) { return false; } // Normalize any trailing '.' used for DNS suffix searches. @@ -264,18 +287,19 @@ TransportSecurityState::TransportSecurityState( // Both HSTS and HPKP cause fatal SSL errors, so return true if a // host has either. -bool TransportSecurityState::ShouldSSLErrorsBeFatal(const std::string& host) { +bool TransportSecurityState::ShouldSSLErrorsBeFatal(const NetworkAnonymizationKey& nak, const std::string& host) { STSState unused_sts; PKPState unused_pkp; - return GetSTSState(host, &unused_sts) || GetPKPState(host, &unused_pkp); + return GetSTSState(nak, host, &unused_sts) || GetPKPState(nak, host, &unused_pkp); } base::Value::Dict TransportSecurityState::NetLogUpgradeToSSLParam( + const NetworkAnonymizationKey& nak, const std::string& host) { STSState sts_state; base::Value::Dict dict; dict.Set("host", host); - dict.Set("get_sts_state_result", GetSTSState(host, &sts_state)); + dict.Set("get_sts_state_result", GetSTSState(nak, host, &sts_state)); dict.Set("should_upgrade_to_ssl", sts_state.ShouldUpgradeToSSL()); dict.Set("host_found_in_hsts_bypass_list", hsts_host_bypass_list_.find(host) != hsts_host_bypass_list_.end()); @@ -283,31 +307,33 @@ base::Value::Dict TransportSecurityState::NetLogUpgradeToSSLParam( } bool TransportSecurityState::ShouldUpgradeToSSL( + const NetworkAnonymizationKey& nak, const std::string& host, const NetLogWithSource& net_log) { STSState sts_state; net_log.AddEvent( NetLogEventType::TRANSPORT_SECURITY_STATE_SHOULD_UPGRADE_TO_SSL, - [&] { return NetLogUpgradeToSSLParam(host); }); - return GetSTSState(host, &sts_state) && sts_state.ShouldUpgradeToSSL(); + [&] { return NetLogUpgradeToSSLParam(nak, host); }); + return GetSTSState(nak, host, &sts_state) && sts_state.ShouldUpgradeToSSL(); } TransportSecurityState::PKPStatus TransportSecurityState::CheckPublicKeyPins( const HostPortPair& host_port_pair, bool is_issued_by_known_root, - const HashValueVector& public_key_hashes) { + const HashValueVector& public_key_hashes, + const NetworkAnonymizationKey& network_anonymization_key) { // Perform pin validation only if the server actually has public key pins. - if (!HasPublicKeyPins(host_port_pair.host())) { + if (!HasPublicKeyPins(network_anonymization_key, host_port_pair.host())) { return PKPStatus::OK; } return CheckPublicKeyPinsImpl(host_port_pair, is_issued_by_known_root, - public_key_hashes); + public_key_hashes, network_anonymization_key); } -bool TransportSecurityState::HasPublicKeyPins(const std::string& host) { +bool TransportSecurityState::HasPublicKeyPins(const NetworkAnonymizationKey& nak, const std::string& host) { PKPState pkp_state; - return GetPKPState(host, &pkp_state) && pkp_state.HasPublicKeyPins(); + return GetPKPState(nak, host, &pkp_state) && pkp_state.HasPublicKeyPins(); } TransportSecurityState::CTRequirementsStatus @@ -392,12 +418,13 @@ void TransportSecurityState::UpdatePinList( } void TransportSecurityState::AddHSTSInternal( + const NetworkAnonymizationKey& nak, const std::string& host, TransportSecurityState::STSState::UpgradeMode upgrade_mode, const base::Time& expiry, bool include_subdomains) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - const std::vector canonicalized_host = CanonicalizeHost(host); + const std::vector canonicalized_host = CanonicalizeHost(nak, host); if (canonicalized_host.empty()) return; @@ -421,13 +448,14 @@ void TransportSecurityState::AddHSTSInternal( DirtyNotify(); } -void TransportSecurityState::AddHPKPInternal(const std::string& host, +void TransportSecurityState::AddHPKPInternal(const NetworkAnonymizationKey& nak, + const std::string& host, const base::Time& last_observed, const base::Time& expiry, bool include_subdomains, const HashValueVector& hashes) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - const std::vector canonicalized_host = CanonicalizeHost(host); + const std::vector canonicalized_host = CanonicalizeHost(nak, host); if (canonicalized_host.empty()) return; @@ -472,10 +500,12 @@ TransportSecurityState::PKPStatus TransportSecurityState::CheckPins( return PKPStatus::VIOLATED; } -bool TransportSecurityState::DeleteDynamicDataForHost(const std::string& host) { +bool TransportSecurityState::DeleteDynamicDataForHost( + const NetworkAnonymizationKey& nak, + const std::string& host) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - const std::vector canonicalized_host = CanonicalizeHost(host); + const std::vector canonicalized_host = CanonicalizeHost(nak, host); if (canonicalized_host.empty()) return false; @@ -552,7 +582,8 @@ void TransportSecurityState::DirtyNotify() { delegate_->StateIsDirty(this); } -bool TransportSecurityState::AddHSTSHeader(const std::string& host, +bool TransportSecurityState::AddHSTSHeader(const NetworkAnonymizationKey& nak, + const std::string& host, const std::string& value) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); @@ -571,23 +602,25 @@ bool TransportSecurityState::AddHSTSHeader(const std::string& host, upgrade_mode = STSState::MODE_FORCE_HTTPS; } - AddHSTSInternal(host, upgrade_mode, now + max_age, include_subdomains); + AddHSTSInternal(nak, host, upgrade_mode, now + max_age, include_subdomains); return true; } -void TransportSecurityState::AddHSTS(const std::string& host, +void TransportSecurityState::AddHSTS(const NetworkAnonymizationKey& nak, + const std::string& host, const base::Time& expiry, bool include_subdomains) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - AddHSTSInternal(host, STSState::MODE_FORCE_HTTPS, expiry, include_subdomains); + AddHSTSInternal(nak, host, STSState::MODE_FORCE_HTTPS, expiry, include_subdomains); } -void TransportSecurityState::AddHPKP(const std::string& host, +void TransportSecurityState::AddHPKP(const NetworkAnonymizationKey& nak, + const std::string& host, const base::Time& expiry, bool include_subdomains, const HashValueVector& hashes) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - AddHPKPInternal(host, base::Time::Now(), expiry, include_subdomains, hashes); + AddHPKPInternal(nak, host, base::Time::Now(), expiry, include_subdomains, hashes); } // static @@ -610,9 +643,10 @@ TransportSecurityState::PKPStatus TransportSecurityState::CheckPublicKeyPinsImpl( const HostPortPair& host_port_pair, bool is_issued_by_known_root, - const HashValueVector& hashes) { + const HashValueVector& hashes, + const NetworkAnonymizationKey& network_anonymization_key) { PKPState pkp_state; - bool found_state = GetPKPState(host_port_pair.host(), &pkp_state); + bool found_state = GetPKPState(network_anonymization_key, host_port_pair.host(), &pkp_state); // HasPublicKeyPins should have returned true in order for this method to have // been called. @@ -653,7 +687,7 @@ bool TransportSecurityState::GetStaticPKPState(const std::string& host, PreloadResult result; if (host_pins_.has_value()) { // Ensure that |host| is a valid hostname before processing. - if (CanonicalizeHost(host).empty()) { + if (CanonicalizeHostWithoutNak(host).empty()) { return false; } // Normalize any trailing '.' used for DNS suffix searches. @@ -736,21 +770,24 @@ bool TransportSecurityState::GetStaticPKPState(const std::string& host, return false; } -bool TransportSecurityState::GetSTSState(const std::string& host, +bool TransportSecurityState::GetSTSState(const NetworkAnonymizationKey& nak, + const std::string& host, STSState* result) { - return GetDynamicSTSState(host, result) || GetStaticSTSState(host, result); + return GetDynamicSTSState(nak, host, result) || GetStaticSTSState(host, result); } -bool TransportSecurityState::GetPKPState(const std::string& host, +bool TransportSecurityState::GetPKPState(const NetworkAnonymizationKey& nak, + const std::string& host, PKPState* result) { - return GetDynamicPKPState(host, result) || GetStaticPKPState(host, result); + return GetDynamicPKPState(nak, host, result) || GetStaticPKPState(host, result); } -bool TransportSecurityState::GetDynamicSTSState(const std::string& host, +bool TransportSecurityState::GetDynamicSTSState(const NetworkAnonymizationKey& nak, + const std::string& host, STSState* result) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - const std::vector canonicalized_host = CanonicalizeHost(host); + const std::vector canonicalized_host = CanonicalizeHost(nak, host); if (canonicalized_host.empty()) return false; @@ -787,11 +824,12 @@ bool TransportSecurityState::GetDynamicSTSState(const std::string& host, return false; } -bool TransportSecurityState::GetDynamicPKPState(const std::string& host, +bool TransportSecurityState::GetDynamicPKPState(const NetworkAnonymizationKey& nak, + const std::string& host, PKPState* result) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - const std::vector canonicalized_host = CanonicalizeHost(host); + const std::vector canonicalized_host = CanonicalizeHost(nak, host); if (canonicalized_host.empty()) return false; diff --git a/net/http/transport_security_state.h b/net/http/transport_security_state.h --- a/net/http/transport_security_state.h +++ b/net/http/transport_security_state.h @@ -285,13 +285,14 @@ class NET_EXPORT TransportSecurityState { // primary public interface; direct access to STS and PKP states is best // left to tests. The caller needs to handle the optional pinning override // when is_issued_by_known_root is false. - bool ShouldSSLErrorsBeFatal(const std::string& host); - bool ShouldUpgradeToSSL(const std::string& host, + bool ShouldSSLErrorsBeFatal(const NetworkAnonymizationKey& nak, const std::string& host); + bool ShouldUpgradeToSSL(const NetworkAnonymizationKey& nak, const std::string& host, const NetLogWithSource& net_log = NetLogWithSource()); PKPStatus CheckPublicKeyPins(const HostPortPair& host_port_pair, bool is_issued_by_known_root, - const HashValueVector& hashes); - bool HasPublicKeyPins(const std::string& host); + const HashValueVector& hashes, + const NetworkAnonymizationKey& network_anonymization_key); + bool HasPublicKeyPins(const NetworkAnonymizationKey& nak, const std::string& host); // Returns CT_REQUIREMENTS_NOT_MET if a connection violates CT policy // requirements: that is, if a connection to |host|, using the validated @@ -375,7 +376,7 @@ class NET_EXPORT TransportSecurityState { // // If an entry is deleted, the new state will be persisted through // the Delegate (if any). - bool DeleteDynamicDataForHost(const std::string& host); + bool DeleteDynamicDataForHost(const NetworkAnonymizationKey& nak, const std::string& host); // Returns true and updates |*result| if |host| has dynamic or static // HSTS/HPKP (respectively) state. If multiple entries match |host|, dynamic @@ -385,8 +386,8 @@ class NET_EXPORT TransportSecurityState { // // Note that these methods are not const because they opportunistically remove // entries that have expired. - bool GetSTSState(const std::string& host, STSState* sts_result); - bool GetPKPState(const std::string& host, PKPState* pkp_result); + bool GetSTSState(const NetworkAnonymizationKey& nak, const std::string& host, STSState* sts_result); + bool GetPKPState(const NetworkAnonymizationKey& nak, const std::string& host, PKPState* pkp_result); // Returns true and updates |*result| iff |host| has static HSTS/HPKP // (respectively) state. If multiple entries match |host|, the most specific @@ -400,22 +401,24 @@ class NET_EXPORT TransportSecurityState { // // Note that these methods are not const because they opportunistically remove // entries that have expired. - bool GetDynamicSTSState(const std::string& host, STSState* result); - bool GetDynamicPKPState(const std::string& host, PKPState* result); + bool GetDynamicSTSState(const NetworkAnonymizationKey& nak, const std::string& host, STSState* result); + bool GetDynamicPKPState(const NetworkAnonymizationKey& nak, const std::string& host, PKPState* result); // Processes an HSTS header value from the host, adding entries to // dynamic state if necessary. - bool AddHSTSHeader(const std::string& host, const std::string& value); + bool AddHSTSHeader(const NetworkAnonymizationKey& nak, const std::string& host, const std::string& value); // Adds explicitly-specified data as if it was processed from an // HSTS header (used for net-internals and unit tests). - void AddHSTS(const std::string& host, + void AddHSTS(const NetworkAnonymizationKey& nak, + const std::string& host, const base::Time& expiry, bool include_subdomains); // Adds explicitly-specified data as if it was processed from an HPKP header. // Note: dynamic PKP data is not persisted. - void AddHPKP(const std::string& host, + void AddHPKP(const NetworkAnonymizationKey& nak, + const std::string& host, const base::Time& expiry, bool include_subdomains, const HashValueVector& hashes); @@ -457,7 +460,7 @@ class NET_EXPORT TransportSecurityState { typedef std::map STSStateMap; typedef std::map PKPStateMap; - base::Value::Dict NetLogUpgradeToSSLParam(const std::string& host); + base::Value::Dict NetLogUpgradeToSSLParam(const NetworkAnonymizationKey& nak, const std::string& host); // IsBuildTimely returns true if the current build is new enough ensure that // built in security information (i.e. HSTS preloading and pinning @@ -467,7 +470,8 @@ class NET_EXPORT TransportSecurityState { // Helper method for actually checking pins. PKPStatus CheckPublicKeyPinsImpl(const HostPortPair& host_port_pair, bool is_issued_by_known_root, - const HashValueVector& hashes); + const HashValueVector& hashes, + const NetworkAnonymizationKey& network_anonymization_key); // If a Delegate is present, notify it that the internal state has // changed. @@ -477,11 +481,13 @@ class NET_EXPORT TransportSecurityState { // any previous state for the |host|, including static entries. // // The new state for |host| is persisted using the Delegate (if any). - void AddHSTSInternal(const std::string& host, + void AddHSTSInternal(const NetworkAnonymizationKey& nak, + const std::string& host, STSState::UpgradeMode upgrade_mode, const base::Time& expiry, bool include_subdomains); - void AddHPKPInternal(const std::string& host, + void AddHPKPInternal(const NetworkAnonymizationKey& nak, + const std::string& host, const base::Time& last_observed, const base::Time& expiry, bool include_subdomains, diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc --- a/net/quic/crypto/proof_verifier_chromium.cc +++ b/net/quic/crypto/proof_verifier_chromium.cc @@ -401,7 +401,8 @@ int ProofVerifierChromium::Job::DoVerifyCertComplete(int result) { transport_security_state_->CheckPublicKeyPins( HostPortPair(hostname_, port_), cert_verify_result.is_issued_by_known_root, - cert_verify_result.public_key_hashes); + cert_verify_result.public_key_hashes, + proof_verifier_->network_anonymization_key_); switch (pin_validity) { case TransportSecurityState::PKPStatus::VIOLATED: result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN; @@ -428,7 +429,7 @@ int ProofVerifierChromium::Job::DoVerifyCertComplete(int result) { verify_details_->is_fatal_cert_error = IsCertStatusError(cert_status) && result != ERR_CERT_KNOWN_INTERCEPTION_BLOCKED && - transport_security_state_->ShouldSSLErrorsBeFatal(hostname_); + transport_security_state_->ShouldSSLErrorsBeFatal(proof_verifier_->network_anonymization_key_, hostname_); if (result != OK) { std::string error_string = ErrorToString(result); diff --git a/net/quic/quic_chromium_client_session.cc b/net/quic/quic_chromium_client_session.cc --- a/net/quic/quic_chromium_client_session.cc +++ b/net/quic/quic_chromium_client_session.cc @@ -1407,7 +1407,7 @@ bool QuicChromiumClientSession::CanPool( return SpdySession::CanPool(transport_security_state_, ssl_info, *ssl_config_service_, session_key_.host(), - hostname); + hostname, session_key_.network_anonymization_key()); } bool QuicChromiumClientSession::ShouldCreateIncomingStream( diff --git a/net/socket/ssl_client_socket_impl.cc b/net/socket/ssl_client_socket_impl.cc --- a/net/socket/ssl_client_socket_impl.cc +++ b/net/socket/ssl_client_socket_impl.cc @@ -1300,7 +1300,8 @@ ssl_verify_result_t SSLClientSocketImpl::HandleVerifyResult() { TransportSecurityState::PKPStatus pin_validity = context_->transport_security_state()->CheckPublicKeyPins( host_and_port_, server_cert_verify_result_.is_issued_by_known_root, - server_cert_verify_result_.public_key_hashes); + server_cert_verify_result_.public_key_hashes, + ssl_config_.network_anonymization_key); switch (pin_validity) { case TransportSecurityState::PKPStatus::VIOLATED: server_cert_verify_result_.cert_status |= @@ -1322,7 +1323,7 @@ ssl_verify_result_t SSLClientSocketImpl::HandleVerifyResult() { IsCertStatusError(server_cert_verify_result_.cert_status) && result != ERR_CERT_KNOWN_INTERCEPTION_BLOCKED && context_->transport_security_state()->ShouldSSLErrorsBeFatal( - host_and_port_.host()); + ssl_config_.network_anonymization_key, host_and_port_.host()); if (IsCertificateError(result)) { if (!GetECHNameOverride().empty()) { diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -733,7 +733,8 @@ bool SpdySession::CanPool(TransportSecurityState* transport_security_state, const SSLInfo& ssl_info, const SSLConfigService& ssl_config_service, std::string_view old_hostname, - std::string_view new_hostname) { + std::string_view new_hostname, + const net::NetworkAnonymizationKey& network_anonymization_key) { // Pooling is prohibited if the server cert is not valid for the new domain, // and for connections on which client certs were sent. It is also prohibited // when channel ID was sent if the hosts are from different eTLDs+1. @@ -752,7 +753,7 @@ bool SpdySession::CanPool(TransportSecurityState* transport_security_state, // Port is left at 0 as it is never used. if (transport_security_state->CheckPublicKeyPins( HostPortPair(new_hostname, 0), ssl_info.is_issued_by_known_root, - ssl_info.public_key_hashes) == + ssl_info.public_key_hashes, network_anonymization_key) == TransportSecurityState::PKPStatus::VIOLATED) { return false; } @@ -973,7 +974,8 @@ bool SpdySession::VerifyDomainAuthentication(std::string_view domain) const { return true; // This is not a secure session, so all domains are okay. return CanPool(transport_security_state_, ssl_info, *ssl_config_service_, - host_port_pair().host(), domain); + host_port_pair().host(), domain, + spdy_session_key_.network_anonymization_key()); } void SpdySession::EnqueueStreamWrite( @@ -3010,7 +3012,8 @@ void SpdySession::OnAltSvc( return; } if (!CanPool(transport_security_state_, ssl_info, *ssl_config_service_, - host_port_pair().host(), gurl.host_piece())) { + host_port_pair().host(), gurl.host_piece(), + spdy_session_key_.network_anonymization_key())) { return; } scheme_host_port = url::SchemeHostPort(gurl); diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h @@ -320,7 +320,8 @@ class NET_EXPORT SpdySession const SSLInfo& ssl_info, const SSLConfigService& ssl_config_service, std::string_view old_hostname, - std::string_view new_hostname); + std::string_view new_hostname, + const net::NetworkAnonymizationKey& network_anonymization_key); // Create a new SpdySession. // |spdy_session_key| is the host/port that this session connects to, privacy diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc --- a/net/url_request/url_request_context_builder.cc +++ b/net/url_request/url_request_context_builder.cc @@ -388,20 +388,6 @@ std::unique_ptr URLRequestContextBuilder::Build() { context->set_transport_security_state( std::make_unique(hsts_policy_bypass_list_)); - if (!transport_security_persister_file_path_.empty()) { - // Use a low priority because saving this should not block anything - // user-visible. Block shutdown to ensure it does get persisted to disk, - // since it contains security-relevant information. - scoped_refptr task_runner( - base::ThreadPool::CreateSequencedTaskRunner( - {base::MayBlock(), base::TaskPriority::BEST_EFFORT, - base::TaskShutdownBehavior::BLOCK_SHUTDOWN})); - - context->set_transport_security_persister( - std::make_unique( - context->transport_security_state(), task_runner, - transport_security_persister_file_path_)); - } if (http_server_properties_) { context->set_http_server_properties(std::move(http_server_properties_)); diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc --- a/net/url_request/url_request_http_job.cc +++ b/net/url_request/url_request_http_job.cc @@ -274,7 +274,8 @@ std::unique_ptr URLRequestHttpJob::Create(URLRequest* request) { TransportSecurityState* hsts = request->context()->transport_security_state(); bool should_upgrade_to_ssl = - hsts && hsts->ShouldUpgradeToSSL(url.host(), request->net_log()); + hsts && hsts->ShouldUpgradeToSSL(request->isolation_info().network_anonymization_key(), + url.host(), request->net_log()); // Check for reasons not to return a URLRequestHttpJob. These don't apply to // https and wss requests. @@ -1085,7 +1086,7 @@ void URLRequestHttpJob::ProcessStrictTransportSecurityHeader() { HttpResponseHeaders* headers = GetResponseHeaders(); std::string value; if (headers->EnumerateHeader(nullptr, "Strict-Transport-Security", &value)) - security_state->AddHSTSHeader(request_info_.url.host(), value); + security_state->AddHSTSHeader(request_info_.network_anonymization_key, request_info_.url.host(), value); } void URLRequestHttpJob::OnStartCompleted(int result) { @@ -1155,7 +1156,7 @@ void URLRequestHttpJob::OnStartCompleted(int result) { TransportSecurityState* state = context->transport_security_state(); NotifySSLCertificateError( result, transaction_->GetResponseInfo()->ssl_info, - state->ShouldSSLErrorsBeFatal(request_info_.url.host()) && + state->ShouldSSLErrorsBeFatal(request_->isolation_info().network_anonymization_key(), request_info_.url.host()) && result != ERR_CERT_KNOWN_INTERCEPTION_BLOCKED); } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) { NotifyCertificateRequested( diff --git a/services/network/network_context.cc b/services/network/network_context.cc --- a/services/network/network_context.cc +++ b/services/network/network_context.cc @@ -1814,6 +1814,7 @@ void NetworkContext::CreateHostResolver( void NetworkContext::VerifyCertForSignedExchange( const scoped_refptr& certificate, const GURL& url, + const net::NetworkAnonymizationKey& network_anonymization_key, const std::string& ocsp_result, const std::string& sct_list, VerifyCertForSignedExchangeCallback callback) { @@ -1824,6 +1825,7 @@ void NetworkContext::VerifyCertForSignedExchange( pending_cert_verify->result = std::make_unique(); pending_cert_verify->certificate = certificate; pending_cert_verify->url = url; + pending_cert_verify->network_anonymization_key = network_anonymization_key; pending_cert_verify->ocsp_result = ocsp_result; pending_cert_verify->sct_list = sct_list; net::CertVerifier* cert_verifier = @@ -1868,17 +1870,19 @@ void NetworkContext::SetCorsOriginAccessListsForOrigin( std::move(callback).Run(); } -void NetworkContext::AddHSTS(const std::string& host, +void NetworkContext::AddHSTS(const net::NetworkAnonymizationKey& nak, + const std::string& host, base::Time expiry, bool include_subdomains, AddHSTSCallback callback) { net::TransportSecurityState* state = url_request_context_->transport_security_state(); - state->AddHSTS(host, expiry, include_subdomains); + state->AddHSTS(nak, host, expiry, include_subdomains); std::move(callback).Run(); } -void NetworkContext::IsHSTSActiveForHost(const std::string& host, +void NetworkContext::IsHSTSActiveForHost(const net::NetworkAnonymizationKey& nak, + const std::string& host, IsHSTSActiveForHostCallback callback) { net::TransportSecurityState* security_state = url_request_context_->transport_security_state(); @@ -1888,10 +1892,10 @@ void NetworkContext::IsHSTSActiveForHost(const std::string& host, return; } - std::move(callback).Run(security_state->ShouldUpgradeToSSL(host)); + std::move(callback).Run(security_state->ShouldUpgradeToSSL(nak, host)); } -void NetworkContext::GetHSTSState(const std::string& domain, +void NetworkContext::GetHSTSState(const net::NetworkAnonymizationKey& nak, const std::string& domain, GetHSTSStateCallback callback) { base::Value::Dict result; @@ -1929,10 +1933,10 @@ void NetworkContext::GetHSTSState(const std::string& domain, net::TransportSecurityState::STSState dynamic_sts_state; net::TransportSecurityState::PKPState dynamic_pkp_state; bool found_sts_dynamic = transport_security_state->GetDynamicSTSState( - domain, &dynamic_sts_state); + nak, domain, &dynamic_sts_state); bool found_pkp_dynamic = transport_security_state->GetDynamicPKPState( - domain, &dynamic_pkp_state); + nak, domain, &dynamic_pkp_state); if (found_sts_dynamic) { result.Set("dynamic_upgrade_mode", static_cast(dynamic_sts_state.upgrade_mode)); @@ -1970,6 +1974,7 @@ void NetworkContext::GetHSTSState(const std::string& domain, } void NetworkContext::DeleteDynamicDataForHost( + const net::NetworkAnonymizationKey& nak, const std::string& host, DeleteDynamicDataForHostCallback callback) { net::TransportSecurityState* transport_security_state = @@ -1980,7 +1985,7 @@ void NetworkContext::DeleteDynamicDataForHost( } std::move(callback).Run( - transport_security_state->DeleteDynamicDataForHost(host)); + transport_security_state->DeleteDynamicDataForHost(nak, host)); } void NetworkContext::EnableStaticKeyPinningForTesting( @@ -2021,7 +2026,7 @@ void NetworkContext::PreconnectSockets( DCHECK(!require_network_anonymization_key_ || !network_anonymization_key.IsEmpty()); - GURL url = GetHSTSRedirect(original_url); + GURL url = GetHSTSRedirect(network_anonymization_key, original_url); // |PreconnectSockets| may receive arguments from the renderer, which is not // guaranteed to validate them. @@ -2801,12 +2806,15 @@ void NetworkContext::OnConnectionError() { std::move(on_connection_close_callback_).Run(this); } -GURL NetworkContext::GetHSTSRedirect(const GURL& original_url) { +GURL NetworkContext::GetHSTSRedirect( + const net::NetworkAnonymizationKey& network_anonymization_key, + const GURL& original_url) { // TODO(lilyhoughton) This needs to be gotten rid of once explicit // construction with a URLRequestContext is no longer supported. if (!url_request_context_->transport_security_state() || !original_url.SchemeIs("http") || !url_request_context_->transport_security_state()->ShouldUpgradeToSSL( + network_anonymization_key, original_url.host())) { return original_url; } @@ -2854,7 +2862,8 @@ void NetworkContext::OnVerifyCertForSignedExchangeComplete( url_request_context_->transport_security_state()->CheckPublicKeyPins( net::HostPortPair::FromURL(pending_cert_verify->url), pending_cert_verify->result->is_issued_by_known_root, - pending_cert_verify->result->public_key_hashes); + pending_cert_verify->result->public_key_hashes, + pending_cert_verify->network_anonymization_key); switch (pin_validity) { case net::TransportSecurityState::PKPStatus::VIOLATED: pending_cert_verify->result->cert_status |= diff --git a/services/network/network_context.h b/services/network/network_context.h --- a/services/network/network_context.h +++ b/services/network/network_context.h @@ -407,18 +407,21 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext void VerifyCertForSignedExchange( const scoped_refptr& certificate, const GURL& url, + const net::NetworkAnonymizationKey& network_anonymization_key, const std::string& ocsp_result, const std::string& sct_list, VerifyCertForSignedExchangeCallback callback) override; - void AddHSTS(const std::string& host, + void AddHSTS(const net::NetworkAnonymizationKey& nak, + const std::string& host, base::Time expiry, bool include_subdomains, AddHSTSCallback callback) override; - void IsHSTSActiveForHost(const std::string& host, + void IsHSTSActiveForHost(const net::NetworkAnonymizationKey& nak, const std::string& host, IsHSTSActiveForHostCallback callback) override; - void GetHSTSState(const std::string& domain, + void GetHSTSState(const net::NetworkAnonymizationKey& nak, const std::string& domain, GetHSTSStateCallback callback) override; void DeleteDynamicDataForHost( + const net::NetworkAnonymizationKey& nak, const std::string& host, DeleteDynamicDataForHostCallback callback) override; void SetCorsOriginAccessListsForOrigin( @@ -712,7 +715,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext mojo::PendingRemote cookie_observer, net::FirstPartySetMetadata first_party_set_metadata); - GURL GetHSTSRedirect(const GURL& original_url); + GURL GetHSTSRedirect(const net::NetworkAnonymizationKey& network_anonymization_key, + const GURL& original_url); #if BUILDFLAG(IS_P2P_ENABLED) void DestroySocketManager(P2PSocketManager* socket_manager); @@ -906,6 +910,7 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) NetworkContext VerifyCertForSignedExchangeCallback callback; scoped_refptr certificate; GURL url; + net::NetworkAnonymizationKey network_anonymization_key; std::string ocsp_result; std::string sct_list; }; diff --git a/services/network/public/mojom/network_context.mojom b/services/network/public/mojom/network_context.mojom --- a/services/network/public/mojom/network_context.mojom +++ b/services/network/public/mojom/network_context.mojom @@ -1505,6 +1505,7 @@ interface NetworkContext { // implementation is currently specific for use by Signed Exchange. VerifyCertForSignedExchange(X509Certificate certificate, url.mojom.Url url, + NetworkAnonymizationKey network_anonymization_key, string ocsp_response, string sct_list) => (int32 error_code, CertVerifyResult cv_result, @@ -1512,16 +1513,16 @@ interface NetworkContext { // Adds explicitly-specified data as if it was processed from an // HSTS header. Used by tests and implementation of chrome://net-internals. - AddHSTS(string host, mojo_base.mojom.Time expiry, + AddHSTS(NetworkAnonymizationKey nak, string host, mojo_base.mojom.Time expiry, bool include_subdomains) => (); // Returns true if it is known that |host| has requested to always be // accessed via HTTPS. - IsHSTSActiveForHost(string host) => (bool result); + IsHSTSActiveForHost(NetworkAnonymizationKey nak, string host) => (bool result); // Retrieve values from the HSTS state from the associated contexts // transport security state. - GetHSTSState(string domain) + GetHSTSState(NetworkAnonymizationKey nak, string domain) => (mojo_base.mojom.DictionaryValue state); // Sets allowed and blocked origins respectively for the URLLoaderFactory @@ -1540,7 +1541,7 @@ interface NetworkContext { // Deletes any dynamic data stored for |host| from the transport // security state. Returns true iff an entry was deleted. // See net::TransportSecurityState::DeleteDynamicDataForHost for more detail. - DeleteDynamicDataForHost(string host) => (bool result); + DeleteDynamicDataForHost(NetworkAnonymizationKey nak, string host) => (bool result); // Sets whether the HTTP auth cache will be split the NetworkAnonymizationKey. // Only affects server (not proxy) credentials. Whenever the effective value --