blob: 67e2af55131db6255fce5e83944c724159dbd1aa [file] [log] [blame] [edit]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ssl/ssl_config_service_manager.h"
#include <stdint.h>
#include <algorithm>
#include <string>
#include <string_view>
#include <vector>
#include "base/containers/to_vector.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "components/content_settings/core/browser/content_settings_utils.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_member.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/remote_set.h"
#include "net/cert/cert_verifier.h"
#include "net/ssl/ssl_cipher_suite_names.h"
#include "net/ssl/ssl_config_service.h"
#include "services/network/public/mojom/network_context.mojom.h"
#include "services/network/public/mojom/ssl_config.mojom.h"
#include "url/url_canon.h"
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
#include "net/cert/internal/trust_store_chrome.h"
#endif
namespace {
// Converts a `base::Value::List` of StringValues into a vector of strings. Any
// values which cannot be converted will be skipped.
std::vector<std::string> ValueListToStringVector(
const base::Value::List& list) {
std::vector<std::string> results;
results.reserve(list.size());
for (const auto& entry : list) {
const std::string* s = entry.GetIfString();
if (s)
results.push_back(*s);
}
return results;
}
// Parses a vector of cipher suite strings, returning a sorted vector
// containing the underlying SSL/TLS cipher suites. Unrecognized/invalid
// cipher suites will be ignored.
std::vector<uint16_t> ParseCipherSuites(
const std::vector<std::string>& cipher_strings) {
std::vector<uint16_t> cipher_suites;
cipher_suites.reserve(cipher_strings.size());
for (auto it = cipher_strings.begin(); it != cipher_strings.end(); ++it) {
uint16_t cipher_suite = 0;
if (!net::ParseSSLCipherString(*it, &cipher_suite)) {
LOG(ERROR) << "Ignoring unrecognized or unparsable cipher suite: " << *it;
continue;
}
cipher_suites.push_back(cipher_suite);
}
std::sort(cipher_suites.begin(), cipher_suites.end());
return cipher_suites;
}
// Writes the SSL protocol version represented by a string to |version|. Returns
// false if the string is not recognized.
bool SSLProtocolVersionFromString(const std::string& version_str,
network::mojom::SSLVersion* version) {
if (version_str == switches::kSSLVersionTLSv12) {
*version = network::mojom::SSLVersion::kTLS12;
return true;
}
if (version_str == switches::kSSLVersionTLSv13) {
*version = network::mojom::SSLVersion::kTLS13;
return true;
}
return false;
}
// Given a vector of hostname patterns |patterns|, returns a vector containing
// the canonical form. Any entries which cannot be parsed are skipped.
std::vector<std::string> CanonicalizeHostnamePatterns(
const std::vector<std::string>& patterns) {
std::vector<std::string> out;
out.reserve(patterns.size());
for (std::string_view pattern : patterns) {
std::string canon_pattern;
url::Component canon_component;
url::StdStringCanonOutput canon_output(&canon_pattern);
if (!url::CanonicalizeHost(pattern.data(),
url::Component(0, pattern.size()), &canon_output,
&canon_component)) {
continue;
}
canon_output.Complete();
out.push_back(canon_pattern);
}
return out;
}
} // namespace
SSLConfigServiceManager::SSLConfigServiceManager(PrefService* local_state) {
DCHECK(local_state);
PrefChangeRegistrar::NamedChangeCallback local_state_callback =
base::BindRepeating(&SSLConfigServiceManager::OnPreferenceChanged,
base::Unretained(this), local_state);
rev_checking_enabled_.Init(prefs::kCertRevocationCheckingEnabled, local_state,
local_state_callback);
rev_checking_required_local_anchors_.Init(
prefs::kCertRevocationCheckingRequiredLocalAnchors, local_state,
local_state_callback);
ssl_version_min_.Init(prefs::kSSLVersionMin, local_state,
local_state_callback);
ssl_version_max_.Init(prefs::kSSLVersionMax, local_state,
local_state_callback);
h2_client_cert_coalescing_host_patterns_.Init(
prefs::kH2ClientCertCoalescingHosts, local_state, local_state_callback);
post_quantum_enabled_.Init(prefs::kPostQuantumKeyAgreementEnabled,
local_state, local_state_callback);
#if BUILDFLAG(IS_CHROMEOS)
device_post_quantum_enabled_.Init(
prefs::kDevicePostQuantumKeyAgreementEnabled, local_state,
local_state_callback);
#endif
ech_enabled_.Init(prefs::kEncryptedClientHelloEnabled, local_state,
local_state_callback);
local_state_change_registrar_.Init(local_state);
local_state_change_registrar_.Add(prefs::kCipherSuiteBlacklist,
local_state_callback);
// Populate |disabled_cipher_suites_| with the initial pref value.
OnDisabledCipherSuitesChange(local_state);
}
SSLConfigServiceManager::~SSLConfigServiceManager() = default;
// static
void SSLConfigServiceManager::RegisterPrefs(PrefRegistrySimple* registry) {
net::CertVerifier::Config default_verifier_config;
registry->RegisterBooleanPref(prefs::kCertRevocationCheckingEnabled,
default_verifier_config.enable_rev_checking);
registry->RegisterBooleanPref(
prefs::kCertRevocationCheckingRequiredLocalAnchors,
default_verifier_config.require_rev_checking_local_anchors);
net::SSLContextConfig default_context_config;
registry->RegisterStringPref(prefs::kSSLVersionMin, std::string());
registry->RegisterStringPref(prefs::kSSLVersionMax, std::string());
registry->RegisterListPref(prefs::kCipherSuiteBlacklist);
registry->RegisterListPref(prefs::kH2ClientCertCoalescingHosts);
registry->RegisterBooleanPref(prefs::kEncryptedClientHelloEnabled,
default_context_config.ech_enabled);
// Default value for these prefs don't matter since they are only used when
// managed.
registry->RegisterBooleanPref(prefs::kPostQuantumKeyAgreementEnabled, false);
#if BUILDFLAG(IS_CHROMEOS)
registry->RegisterBooleanPref(prefs::kDevicePostQuantumKeyAgreementEnabled,
true);
#endif
}
void SSLConfigServiceManager::AddToNetworkContextParams(
network::mojom::NetworkContextParams* network_context_params) {
network_context_params->initial_ssl_config = GetNewSSLConfig();
mojo::Remote<network::mojom::SSLConfigClient> ssl_config_client;
network_context_params->ssl_config_client_receiver =
ssl_config_client.BindNewPipeAndPassReceiver();
ssl_config_client_set_.Add(std::move(ssl_config_client));
}
void SSLConfigServiceManager::UpdateTrustAnchorIDs(
std::vector<std::vector<uint8_t>> trust_anchor_ids) {
trust_anchor_ids_ = std::move(trust_anchor_ids);
network::mojom::SSLConfigPtr new_config = GetNewSSLConfig();
network::mojom::SSLConfig* raw_config = new_config.get();
for (const auto& client : ssl_config_client_set_) {
// Mojo calls consume all InterfacePtrs passed to them, so have to
// clone the config for each call.
client->OnSSLConfigUpdated(raw_config->Clone());
}
}
void SSLConfigServiceManager::FlushForTesting() {
ssl_config_client_set_.FlushForTesting();
}
void SSLConfigServiceManager::OnPreferenceChanged(
PrefService* prefs,
const std::string& pref_name_in) {
DCHECK(prefs);
if (pref_name_in == prefs::kCipherSuiteBlacklist)
OnDisabledCipherSuitesChange(prefs);
network::mojom::SSLConfigPtr new_config = GetNewSSLConfig();
network::mojom::SSLConfig* raw_config = new_config.get();
for (const auto& client : ssl_config_client_set_) {
// Mojo calls consume all InterfacePtrs passed to them, so have to
// clone the config for each call.
client->OnSSLConfigUpdated(raw_config->Clone());
}
}
network::mojom::SSLConfigPtr SSLConfigServiceManager::GetNewSSLConfig() const {
network::mojom::SSLConfigPtr config = network::mojom::SSLConfig::New();
// rev_checking_enabled was formerly a user-settable preference, but now
// it is managed-only.
if (rev_checking_enabled_.IsManaged())
config->rev_checking_enabled = rev_checking_enabled_.GetValue();
else
config->rev_checking_enabled = false;
config->rev_checking_required_local_anchors =
rev_checking_required_local_anchors_.GetValue();
std::string version_min_str = ssl_version_min_.GetValue();
std::string version_max_str = ssl_version_max_.GetValue();
network::mojom::SSLVersion version_min;
if (SSLProtocolVersionFromString(version_min_str, &version_min)) {
config->version_min = version_min;
}
network::mojom::SSLVersion version_max;
if (SSLProtocolVersionFromString(version_max_str, &version_max)) {
config->version_max = version_max;
}
config->disabled_cipher_suites = disabled_cipher_suites_;
config->client_cert_pooling_policy = CanonicalizeHostnamePatterns(
h2_client_cert_coalescing_host_patterns_.GetValue());
config->ech_enabled = ech_enabled_.GetValue();
if (post_quantum_enabled_.IsManaged()) {
config->post_quantum_key_agreement_enabled =
post_quantum_enabled_.GetValue();
}
#if BUILDFLAG(IS_CHROMEOS)
if (device_post_quantum_enabled_.IsManaged()) {
config->post_quantum_key_agreement_enabled =
device_post_quantum_enabled_.GetValue();
}
#endif
#if BUILDFLAG(CHROME_ROOT_STORE_SUPPORTED)
config->trust_anchor_ids =
trust_anchor_ids_.has_value()
? trust_anchor_ids_.value()
: net::TrustStoreChrome::GetTrustAnchorIDsFromCompiledInRootStore();
#endif
return config;
}
void SSLConfigServiceManager::OnDisabledCipherSuitesChange(
PrefService* local_state) {
const base::Value::List& list =
local_state->GetList(prefs::kCipherSuiteBlacklist);
disabled_cipher_suites_ = ParseCipherSuites(ValueListToStringVector(list));
}