| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/net/profile_network_context_service.h" |
| |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/feature_list.h" |
| #include "base/files/file_path.h" |
| #include "base/logging.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/domain_reliability/service_factory.h" |
| #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings.h" |
| #include "chrome/browser/net/spdyproxy/data_reduction_proxy_chrome_settings_factory.h" |
| #include "chrome/browser/net/system_network_context_manager.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_content_client.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/chrome_paths_internal.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/certificate_transparency/pref_names.h" |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "components/content_settings/core/common/pref_names.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" |
| #include "components/metrics/metrics_pref_names.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/network_service_instance.h" |
| #include "content/public/browser/shared_cors_origin_access_list.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/common/service_names.mojom.h" |
| #include "content/public/common/url_constants.h" |
| #include "mojo/public/cpp/bindings/associated_interface_ptr.h" |
| #include "net/http/http_util.h" |
| #include "net/net_buildflags.h" |
| #include "services/network/public/cpp/cors/origin_access_list.h" |
| #include "services/network/public/cpp/features.h" |
| |
| #if defined(OS_CHROMEOS) |
| #include "chrome/browser/chromeos/policy/policy_cert_service.h" |
| #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h" |
| #include "chrome/browser/chromeos/profiles/profile_helper.h" |
| #include "components/user_manager/user.h" |
| #endif |
| |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| #include "extensions/common/constants.h" |
| #endif |
| |
| namespace { |
| |
| bool* g_discard_domain_reliability_uploads_for_testing = nullptr; |
| |
| std::vector<std::string> TranslateStringArray(const base::ListValue* list) { |
| std::vector<std::string> strings; |
| for (const base::Value& value : *list) { |
| DCHECK(value.is_string()); |
| strings.push_back(value.GetString()); |
| } |
| return strings; |
| } |
| |
| std::string ComputeAcceptLanguageFromPref(const std::string& language_pref) { |
| std::string accept_languages_str = |
| base::FeatureList::IsEnabled(features::kUseNewAcceptLanguageHeader) |
| ? net::HttpUtil::ExpandLanguageList(language_pref) |
| : language_pref; |
| return net::HttpUtil::GenerateAcceptLanguageHeader(accept_languages_str); |
| } |
| |
| } // namespace |
| |
| ProfileNetworkContextService::ProfileNetworkContextService(Profile* profile) |
| : profile_(profile), proxy_config_monitor_(profile) { |
| PrefService* profile_prefs = profile->GetPrefs(); |
| quic_allowed_.Init( |
| prefs::kQuicAllowed, profile_prefs, |
| base::Bind(&ProfileNetworkContextService::DisableQuicIfNotAllowed, |
| base::Unretained(this))); |
| pref_accept_language_.Init( |
| prefs::kAcceptLanguages, profile_prefs, |
| base::BindRepeating(&ProfileNetworkContextService::UpdateAcceptLanguage, |
| base::Unretained(this))); |
| enable_referrers_.Init( |
| prefs::kEnableReferrers, profile_prefs, |
| base::BindRepeating(&ProfileNetworkContextService::UpdateReferrersEnabled, |
| base::Unretained(this))); |
| block_third_party_cookies_.Init( |
| prefs::kBlockThirdPartyCookies, profile_prefs, |
| base::BindRepeating( |
| &ProfileNetworkContextService::UpdateBlockThirdPartyCookies, |
| base::Unretained(this))); |
| DisableQuicIfNotAllowed(); |
| |
| // Observe content settings so they can be synced to the network service. |
| HostContentSettingsMapFactory::GetForProfile(profile_)->AddObserver(this); |
| |
| pref_change_registrar_.Init(profile_prefs); |
| |
| // When any of the following CT preferences change, we schedule an update |
| // to aggregate the actual update using a |ct_policy_update_timer_|. |
| pref_change_registrar_.Add( |
| certificate_transparency::prefs::kCTRequiredHosts, |
| base::BindRepeating(&ProfileNetworkContextService::ScheduleUpdateCTPolicy, |
| base::Unretained(this))); |
| pref_change_registrar_.Add( |
| certificate_transparency::prefs::kCTExcludedHosts, |
| base::BindRepeating(&ProfileNetworkContextService::ScheduleUpdateCTPolicy, |
| base::Unretained(this))); |
| pref_change_registrar_.Add( |
| certificate_transparency::prefs::kCTExcludedSPKIs, |
| base::BindRepeating(&ProfileNetworkContextService::ScheduleUpdateCTPolicy, |
| base::Unretained(this))); |
| pref_change_registrar_.Add( |
| certificate_transparency::prefs::kCTExcludedLegacySPKIs, |
| base::BindRepeating(&ProfileNetworkContextService::ScheduleUpdateCTPolicy, |
| base::Unretained(this))); |
| } |
| |
| ProfileNetworkContextService::~ProfileNetworkContextService() {} |
| |
| network::mojom::NetworkContextPtr |
| ProfileNetworkContextService::CreateNetworkContext( |
| bool in_memory, |
| const base::FilePath& relative_partition_path) { |
| network::mojom::NetworkContextPtr network_context; |
| PartitionInfo partition_info(in_memory, relative_partition_path); |
| |
| if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { |
| content::GetNetworkService()->CreateNetworkContext( |
| MakeRequest(&network_context), |
| CreateNetworkContextParams(in_memory, relative_partition_path)); |
| } else { |
| // The corresponding |profile_io_data_network_contexts_| may already be |
| // initialized if SetUpProfileIODataNetworkContext was called first. |
| auto iter = profile_io_data_network_contexts_.find(partition_info); |
| if (iter == profile_io_data_network_contexts_.end()) { |
| // If this is not the main network context, then this method is expected |
| // to be called after the URLRequestContext is configured. |
| DCHECK(relative_partition_path.empty()); |
| // If the NetworkContext has not been requested yet, go ahead and create a |
| // request for it. |
| profile_io_data_context_requests_[partition_info] = |
| mojo::MakeRequest(&network_context); |
| } else { |
| network_context = std::move(iter->second); |
| // This is not strictly necessary, since the network service can't crash, |
| // and NetworkContexts can't be destroyed without destroying the profile. |
| profile_io_data_network_contexts_.erase(iter); |
| } |
| } |
| |
| std::vector<network::mojom::NetworkContext*> contexts{network_context.get()}; |
| UpdateCTPolicyForContexts(contexts); |
| |
| return network_context; |
| } |
| |
| void ProfileNetworkContextService::SetUpProfileIODataNetworkContext( |
| bool in_memory, |
| const base::FilePath& relative_partition_path, |
| network::mojom::NetworkContextRequest* network_context_request, |
| network::mojom::NetworkContextParamsPtr* network_context_params) { |
| DCHECK(network_context_request); |
| DCHECK(network_context_params); |
| |
| PartitionInfo partition_info(in_memory, relative_partition_path); |
| |
| // This may be called either before or after CreateNetworkContext(). |
| auto iter = profile_io_data_context_requests_.find(partition_info); |
| if (iter == profile_io_data_context_requests_.end()) { |
| DCHECK(profile_io_data_network_contexts_.find(partition_info) == |
| profile_io_data_network_contexts_.end()); |
| *network_context_request = |
| mojo::MakeRequest(&profile_io_data_network_contexts_[partition_info]); |
| } else { |
| DCHECK(relative_partition_path.empty()); |
| |
| *network_context_request = std::move(iter->second); |
| // Not strictly necessary, since this should only be called once per storage |
| // partition. |
| profile_io_data_context_requests_.erase(iter); |
| } |
| |
| if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { |
| *network_context_params = |
| CreateNetworkContextParams(in_memory, relative_partition_path); |
| return; |
| } |
| |
| // Just use default if network service is enabled, to avoid the legacy |
| // in-process URLRequestContext from fighting with the NetworkService over |
| // ownership of on-disk files. |
| *network_context_params = network::mojom::NetworkContextParams::New(); |
| } |
| |
| #if defined(OS_CHROMEOS) |
| void ProfileNetworkContextService::UpdateAdditionalCertificates( |
| const net::CertificateList& all_additional_certificates, |
| const net::CertificateList& trust_anchors) { |
| content::BrowserContext::ForEachStoragePartition( |
| profile_, base::BindRepeating( |
| [](const net::CertificateList& all_additional_certificates, |
| const net::CertificateList& trust_anchors, |
| content::StoragePartition* storage_partition) { |
| auto additional_certificates = |
| network::mojom::AdditionalCertificates::New(); |
| additional_certificates->all_certificates = |
| all_additional_certificates; |
| additional_certificates->trust_anchors = trust_anchors; |
| storage_partition->GetNetworkContext() |
| ->UpdateAdditionalCertificates( |
| std::move(additional_certificates)); |
| }, |
| all_additional_certificates, trust_anchors)); |
| } |
| #endif |
| |
| void ProfileNetworkContextService::RegisterProfilePrefs( |
| user_prefs::PrefRegistrySyncable* registry) { |
| registry->RegisterBooleanPref(prefs::kQuicAllowed, true); |
| } |
| |
| void ProfileNetworkContextService::DisableQuicIfNotAllowed() { |
| if (!quic_allowed_.IsManaged()) |
| return; |
| |
| // If QUIC is allowed, do nothing (re-enabling QUIC is not supported). |
| if (quic_allowed_.GetValue()) |
| return; |
| |
| g_browser_process->system_network_context_manager()->DisableQuic(); |
| } |
| |
| void ProfileNetworkContextService::UpdateAcceptLanguage() { |
| content::BrowserContext::ForEachStoragePartition( |
| profile_, base::BindRepeating( |
| [](const std::string& accept_language, |
| content::StoragePartition* storage_partition) { |
| storage_partition->GetNetworkContext()->SetAcceptLanguage( |
| accept_language); |
| }, |
| ComputeAcceptLanguage())); |
| } |
| |
| void ProfileNetworkContextService::UpdateBlockThirdPartyCookies() { |
| content::BrowserContext::ForEachStoragePartition( |
| profile_, base::BindRepeating( |
| [](bool block_third_party_cookies, |
| content::StoragePartition* storage_partition) { |
| storage_partition->GetCookieManagerForBrowserProcess() |
| ->BlockThirdPartyCookies(block_third_party_cookies); |
| }, |
| block_third_party_cookies_.GetValue())); |
| } |
| |
| std::string ProfileNetworkContextService::ComputeAcceptLanguage() const { |
| return ComputeAcceptLanguageFromPref(pref_accept_language_.GetValue()); |
| } |
| |
| void ProfileNetworkContextService::UpdateReferrersEnabled() { |
| content::BrowserContext::ForEachStoragePartition( |
| profile_, |
| base::BindRepeating( |
| [](bool enable_referrers, |
| content::StoragePartition* storage_partition) { |
| storage_partition->GetNetworkContext()->SetEnableReferrers( |
| enable_referrers); |
| }, |
| enable_referrers_.GetValue())); |
| } |
| |
| void ProfileNetworkContextService::UpdateCTPolicyForContexts( |
| const std::vector<network::mojom::NetworkContext*>& contexts) { |
| auto* prefs = profile_->GetPrefs(); |
| const base::ListValue* ct_required = |
| prefs->GetList(certificate_transparency::prefs::kCTRequiredHosts); |
| const base::ListValue* ct_excluded = |
| prefs->GetList(certificate_transparency::prefs::kCTExcludedHosts); |
| const base::ListValue* ct_excluded_spkis = |
| prefs->GetList(certificate_transparency::prefs::kCTExcludedSPKIs); |
| const base::ListValue* ct_excluded_legacy_spkis = |
| prefs->GetList(certificate_transparency::prefs::kCTExcludedLegacySPKIs); |
| |
| std::vector<std::string> required(TranslateStringArray(ct_required)); |
| std::vector<std::string> excluded(TranslateStringArray(ct_excluded)); |
| std::vector<std::string> excluded_spkis( |
| TranslateStringArray(ct_excluded_spkis)); |
| std::vector<std::string> excluded_legacy_spkis( |
| TranslateStringArray(ct_excluded_legacy_spkis)); |
| |
| for (auto* context : contexts) { |
| context->SetCTPolicy(required, excluded, excluded_spkis, |
| excluded_legacy_spkis); |
| } |
| } |
| |
| void ProfileNetworkContextService::UpdateCTPolicy() { |
| std::vector<network::mojom::NetworkContext*> contexts; |
| content::BrowserContext::ForEachStoragePartition( |
| profile_, |
| base::BindRepeating( |
| [](std::vector<network::mojom::NetworkContext*>* contexts_ptr, |
| content::StoragePartition* storage_partition) { |
| contexts_ptr->push_back(storage_partition->GetNetworkContext()); |
| }, |
| &contexts)); |
| |
| UpdateCTPolicyForContexts(contexts); |
| } |
| |
| void ProfileNetworkContextService::ScheduleUpdateCTPolicy() { |
| ct_policy_update_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(0), |
| this, |
| &ProfileNetworkContextService::UpdateCTPolicy); |
| } |
| |
| void ProfileNetworkContextService::FlushProxyConfigMonitorForTesting() { |
| proxy_config_monitor_.FlushForTesting(); |
| } |
| |
| void ProfileNetworkContextService::SetDiscardDomainReliabilityUploadsForTesting( |
| bool value) { |
| g_discard_domain_reliability_uploads_for_testing = new bool(value); |
| } |
| |
| network::mojom::NetworkContextParamsPtr |
| ProfileNetworkContextService::CreateNetworkContextParams( |
| bool in_memory, |
| const base::FilePath& relative_partition_path) { |
| if (profile_->IsOffTheRecord()) |
| in_memory = true; |
| base::FilePath path = profile_->GetPath(); |
| bool is_main_partition = relative_partition_path.empty(); |
| if (!is_main_partition) |
| path = path.Append(relative_partition_path); |
| |
| network::mojom::NetworkContextParamsPtr network_context_params = |
| g_browser_process->system_network_context_manager() |
| ->CreateDefaultNetworkContextParams(); |
| |
| network_context_params->context_name = std::string("main"); |
| |
| network_context_params->accept_language = ComputeAcceptLanguage(); |
| network_context_params->enable_referrers = enable_referrers_.GetValue(); |
| |
| // Always enable the HTTP cache. |
| network_context_params->http_cache_enabled = true; |
| |
| network_context_params->cookie_manager_params = |
| network::mojom::CookieManagerParams::New(); |
| network_context_params->cookie_manager_params->block_third_party_cookies = |
| block_third_party_cookies_.GetValue(); |
| network_context_params->cookie_manager_params |
| ->secure_origin_cookies_allowed_schemes.push_back( |
| content::kChromeUIScheme); |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) { |
| network_context_params->cookie_manager_params |
| ->matching_scheme_cookies_allowed_schemes.push_back( |
| extensions::kExtensionScheme); |
| } |
| #endif |
| |
| ContentSettingsForOneType settings; |
| HostContentSettingsMapFactory::GetForProfile(profile_)->GetSettingsForOneType( |
| CONTENT_SETTINGS_TYPE_COOKIES, std::string(), &settings); |
| network_context_params->cookie_manager_params->settings = std::move(settings); |
| |
| // Configure on-disk storage for non-OTR profiles. OTR profiles just use |
| // default behavior (in memory storage, default sizes). |
| PrefService* prefs = profile_->GetPrefs(); |
| if (!in_memory) { |
| // Configure the HTTP cache path and size. |
| base::FilePath base_cache_path; |
| chrome::GetUserCacheDirectory(path, &base_cache_path); |
| base::FilePath disk_cache_dir = prefs->GetFilePath(prefs::kDiskCacheDir); |
| if (!disk_cache_dir.empty()) |
| base_cache_path = disk_cache_dir.Append(base_cache_path.BaseName()); |
| network_context_params->http_cache_path = |
| base_cache_path.Append(chrome::kCacheDirname); |
| network_context_params->http_cache_max_size = |
| prefs->GetInteger(prefs::kDiskCacheSize); |
| |
| // Currently this just contains HttpServerProperties, but that will likely |
| // change. |
| network_context_params->http_server_properties_path = |
| path.Append(chrome::kNetworkPersistentStateFilename); |
| |
| base::FilePath cookie_path = path; |
| cookie_path = cookie_path.Append(chrome::kCookieFilename); |
| network_context_params->cookie_path = cookie_path; |
| |
| base::FilePath channel_id_path = path; |
| channel_id_path = channel_id_path.Append(chrome::kChannelIDFilename); |
| network_context_params->channel_id_path = channel_id_path; |
| |
| if (is_main_partition) { |
| network_context_params->restore_old_session_cookies = |
| profile_->ShouldRestoreOldSessionCookies(); |
| network_context_params->persist_session_cookies = |
| profile_->ShouldPersistSessionCookies(); |
| } else { |
| // Copy behavior of ProfileImplIOData::InitializeAppRequestContext. |
| network_context_params->restore_old_session_cookies = false; |
| network_context_params->persist_session_cookies = false; |
| } |
| |
| network_context_params->transport_security_persister_path = path; |
| } |
| |
| // NOTE(mmenke): Keep these protocol handlers and |
| // ProfileIOData::SetUpJobFactoryDefaultsForBuilder in sync with |
| // ProfileIOData::IsHandledProtocol(). |
| // TODO(mmenke): Find a better way of handling tracking supported schemes. |
| network_context_params->enable_data_url_support = true; |
| // File support is needed for PAC scripts that use file or data URLs. |
| // TODO(crbug.com/839566): remove file support for all cases. |
| // It is disabled with the network service as it is not responsible for |
| // loading files. |
| if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) |
| network_context_params->enable_file_url_support = true; |
| #if !BUILDFLAG(DISABLE_FTP_SUPPORT) |
| network_context_params->enable_ftp_url_support = true; |
| #endif // !BUILDFLAG(DISABLE_FTP_SUPPORT) |
| |
| proxy_config_monitor_.AddToNetworkContextParams(network_context_params.get()); |
| |
| network_context_params->enable_certificate_reporting = true; |
| network_context_params->enable_expect_ct_reporting = true; |
| |
| if (domain_reliability::DomainReliabilityServiceFactory:: |
| ShouldCreateService()) { |
| network_context_params->enable_domain_reliability = true; |
| network_context_params->domain_reliability_upload_reporter = |
| domain_reliability::DomainReliabilityServiceFactory:: |
| kUploadReporterString; |
| network_context_params->discard_domain_reliablity_uploads = |
| g_discard_domain_reliability_uploads_for_testing |
| ? *g_discard_domain_reliability_uploads_for_testing |
| : !g_browser_process->local_state()->GetBoolean( |
| metrics::prefs::kMetricsReportingEnabled); |
| } |
| |
| if (data_reduction_proxy::params::IsEnabledWithNetworkService()) { |
| auto* drp_settings = |
| DataReductionProxyChromeSettingsFactory::GetForBrowserContext(profile_); |
| if (drp_settings) { |
| network::mojom::CustomProxyConfigClientPtrInfo config_client_info; |
| network_context_params->custom_proxy_config_client_request = |
| mojo::MakeRequest(&config_client_info); |
| drp_settings->SetCustomProxyConfigClient(std::move(config_client_info)); |
| } |
| } |
| |
| #if defined(OS_CHROMEOS) |
| user_manager::UserManager* user_manager = user_manager::UserManager::Get(); |
| if (base::FeatureList::IsEnabled(network::features::kNetworkService) && |
| user_manager && |
| policy::PolicyCertServiceFactory::CreateAndStartObservingForProfile( |
| profile_)) { |
| const user_manager::User* user = |
| chromeos::ProfileHelper::Get()->GetUserByProfile(profile_); |
| // No need to initialize NSS for users with empty username hash: |
| // Getters for a user's NSS slots always return NULL slot if the user's |
| // username hash is empty, even when the NSS is not initialized for the |
| // user. |
| if (user && !user->username_hash().empty()) { |
| network_context_params->username_hash = user->username_hash(); |
| network_context_params->nss_path = profile_->GetPath(); |
| |
| policy::PolicyCertService* service = |
| policy::PolicyCertServiceFactory::GetForProfile(profile_); |
| network_context_params->initial_additional_certificates = |
| network::mojom::AdditionalCertificates::New(); |
| network_context_params->initial_additional_certificates |
| ->all_certificates = service->all_server_and_authority_certs(); |
| network_context_params->initial_additional_certificates->trust_anchors = |
| service->trust_anchors(); |
| } |
| } |
| #endif |
| |
| if (base::FeatureList::IsEnabled(network::features::kNetworkService)) { |
| // Should be initialized with existing per-profile CORS access lists. |
| network_context_params->cors_origin_access_list = |
| content::BrowserContext::GetSharedCorsOriginAccessList(profile_) |
| ->GetOriginAccessList() |
| .CreateCorsOriginAccessPatternsList(); |
| } |
| |
| return network_context_params; |
| } |
| |
| void ProfileNetworkContextService::OnContentSettingChanged( |
| const ContentSettingsPattern& primary_pattern, |
| const ContentSettingsPattern& secondary_pattern, |
| ContentSettingsType content_type, |
| const std::string& resource_identifier) { |
| if (content_type != CONTENT_SETTINGS_TYPE_COOKIES && |
| content_type != CONTENT_SETTINGS_TYPE_DEFAULT) { |
| return; |
| } |
| |
| ContentSettingsForOneType settings; |
| HostContentSettingsMapFactory::GetForProfile(profile_)->GetSettingsForOneType( |
| CONTENT_SETTINGS_TYPE_COOKIES, std::string(), &settings); |
| content::BrowserContext::ForEachStoragePartition( |
| profile_, base::BindRepeating( |
| [](ContentSettingsForOneType settings, |
| content::StoragePartition* storage_partition) { |
| storage_partition->GetCookieManagerForBrowserProcess() |
| ->SetContentSettings(settings); |
| }, |
| settings)); |
| } |