| // Copyright (c) 2012 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/profiles/profile_io_data.h" |
| |
| #include <stddef.h> |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/callback.h" |
| #include "base/command_line.h" |
| #include "base/compiler_specific.h" |
| #include "base/debug/alias.h" |
| #include "base/feature_list.h" |
| #include "base/files/file_util.h" |
| #include "base/logging.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/task/post_task.h" |
| #include "base/task/task_traits.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/content_settings/cookie_settings_factory.h" |
| #include "chrome/browser/content_settings/host_content_settings_map_factory.h" |
| #include "chrome/browser/custom_handlers/protocol_handler_registry.h" |
| #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" |
| #include "chrome/browser/net/chrome_network_delegate.h" |
| #include "chrome/browser/net/profile_network_context_service.h" |
| #include "chrome/browser/net/profile_network_context_service_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/common/buildflags.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_features.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "components/content_settings/core/browser/content_settings_provider.h" |
| #include "components/content_settings/core/browser/cookie_settings.h" |
| #include "components/content_settings/core/browser/host_content_settings_map.h" |
| #include "components/cookie_config/cookie_store_util.h" |
| #include "components/dom_distiller/core/url_constants.h" |
| #include "components/metrics/metrics_pref_names.h" |
| #include "components/metrics/metrics_service.h" |
| #include "components/net_log/chrome_net_log.h" |
| #include "components/policy/core/common/cloud/user_cloud_policy_manager.h" |
| #include "components/signin/public/base/signin_pref_names.h" |
| #include "components/sync/base/pref_names.h" |
| #include "components/url_formatter/url_fixer.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/devtools_network_transaction_factory.h" |
| #include "content/public/browser/network_service_instance.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/resource_context.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.h" |
| #include "extensions/buildflags/buildflags.h" |
| #include "net/ssl/client_cert_store.h" |
| #include "services/network/ignore_errors_cert_verifier.h" |
| #include "services/network/network_service.h" |
| #include "services/network/public/cpp/features.h" |
| #include "third_party/blink/public/public_buildflags.h" |
| |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| #include "extensions/browser/extension_protocols.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/browser/info_map.h" |
| #include "extensions/common/constants.h" |
| #endif // BUILDFLAG(ENABLE_EXTENSIONS) |
| |
| #if defined(OS_CHROMEOS) |
| #include "chrome/browser/chromeos/login/startup_utils.h" |
| #include "chrome/browser/chromeos/profiles/profile_helper.h" |
| #include "chrome/browser/chromeos/settings/cros_settings.h" |
| #include "chrome/browser/net/nss_context.h" |
| #include "chromeos/dbus/cryptohome/cryptohome_client.h" |
| #include "chromeos/dbus/dbus_thread_manager.h" |
| #include "chromeos/settings/cros_settings_names.h" |
| #include "chromeos/tpm/tpm_token_info_getter.h" |
| #include "components/user_manager/user.h" |
| #include "components/user_manager/user_manager.h" |
| #include "crypto/nss_util.h" |
| #include "crypto/nss_util_internal.h" |
| #include "services/network/cert_verifier_with_trust_anchors.h" |
| #include "services/network/cert_verify_proc_chromeos.h" |
| #endif // defined(OS_CHROMEOS) |
| |
| using content::BrowserContext; |
| using content::BrowserThread; |
| using content::ResourceContext; |
| |
| namespace { |
| |
| #if defined(OS_CHROMEOS) |
| // The following four functions are responsible for initializing NSS for each |
| // profile on ChromeOS, which has a separate NSS database and TPM slot |
| // per-profile. |
| // |
| // Initialization basically follows these steps: |
| // 1) Get some info from user_manager::UserManager about the User for this |
| // profile. |
| // 2) Tell nss_util to initialize the software slot for this profile. |
| // 3) Wait for the TPM module to be loaded by nss_util if it isn't already. |
| // 4) Ask CryptohomeClient which TPM slot id corresponds to this profile. |
| // 5) Tell nss_util to use that slot id on the TPM module. |
| // |
| // Some of these steps must happen on the UI thread, others must happen on the |
| // IO thread: |
| // UI thread IO Thread |
| // |
| // ProfileIOData::InitializeOnUIThread |
| // | |
| // ProfileHelper::Get()->GetUserByProfile() |
| // \---------------------------------------v |
| // StartNSSInitOnIOThread |
| // | |
| // crypto::InitializeNSSForChromeOSUser |
| // | |
| // crypto::IsTPMTokenReady |
| // | |
| // StartTPMSlotInitializationOnIOThread |
| // v---------------------------------------/ |
| // GetTPMInfoForUserOnUIThread |
| // | |
| // chromeos::TPMTokenInfoGetter::Start |
| // | |
| // DidGetTPMInfoForUserOnUIThread |
| // \---------------------------------------v |
| // crypto::InitializeTPMForChromeOSUser |
| |
| void DidGetTPMInfoForUserOnUIThread( |
| std::unique_ptr<chromeos::TPMTokenInfoGetter> getter, |
| const std::string& username_hash, |
| base::Optional<chromeos::CryptohomeClient::TpmTokenInfo> token_info) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (token_info.has_value() && token_info->slot != -1) { |
| DVLOG(1) << "Got TPM slot for " << username_hash << ": " |
| << token_info->slot; |
| base::PostTask(FROM_HERE, {BrowserThread::IO}, |
| base::BindOnce(&crypto::InitializeTPMForChromeOSUser, |
| username_hash, token_info->slot)); |
| } else { |
| NOTREACHED() << "TPMTokenInfoGetter reported invalid token."; |
| } |
| } |
| |
| void GetTPMInfoForUserOnUIThread(const AccountId& account_id, |
| const std::string& username_hash) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DVLOG(1) << "Getting TPM info from cryptohome for " |
| << " " << account_id.Serialize() << " " << username_hash; |
| std::unique_ptr<chromeos::TPMTokenInfoGetter> scoped_token_info_getter = |
| chromeos::TPMTokenInfoGetter::CreateForUserToken( |
| account_id, chromeos::CryptohomeClient::Get(), |
| base::ThreadTaskRunnerHandle::Get()); |
| chromeos::TPMTokenInfoGetter* token_info_getter = |
| scoped_token_info_getter.get(); |
| |
| // Bind |token_info_getter| to the callback to ensure it does not go away |
| // before TPM token info is fetched. |
| // TODO(tbarzic, pneubeck): Handle this in a nicer way when this logic is |
| // moved to a separate profile service. |
| token_info_getter->Start(base::BindOnce(&DidGetTPMInfoForUserOnUIThread, |
| std::move(scoped_token_info_getter), |
| username_hash)); |
| } |
| |
| void StartTPMSlotInitializationOnIOThread(const AccountId& account_id, |
| const std::string& username_hash) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| |
| base::PostTask( |
| FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce(&GetTPMInfoForUserOnUIThread, account_id, username_hash)); |
| } |
| |
| void StartNSSInitOnIOThread(const AccountId& account_id, |
| const std::string& username_hash, |
| const base::FilePath& path) { |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DVLOG(1) << "Starting NSS init for " << account_id.Serialize() |
| << " hash:" << username_hash; |
| |
| // Make sure NSS is initialized for the user. |
| crypto::InitializeNSSForChromeOSUser(username_hash, path); |
| |
| // Check if it's OK to initialize TPM for the user before continuing. This |
| // may not be the case if the TPM slot initialization was previously |
| // requested for the same user. |
| if (!crypto::ShouldInitializeTPMForChromeOSUser(username_hash)) |
| return; |
| |
| crypto::WillInitializeTPMForChromeOSUser(username_hash); |
| |
| if (crypto::IsTPMTokenEnabledForNSS()) { |
| if (crypto::IsTPMTokenReady( |
| base::Bind(&StartTPMSlotInitializationOnIOThread, account_id, |
| username_hash))) { |
| StartTPMSlotInitializationOnIOThread(account_id, username_hash); |
| } else { |
| DVLOG(1) << "Waiting for tpm ready ..."; |
| } |
| } else { |
| crypto::InitializePrivateSoftwareSlotForChromeOSUser(username_hash); |
| } |
| } |
| #endif // defined(OS_CHROMEOS) |
| |
| } // namespace |
| |
| void ProfileIOData::InitializeOnUIThread(Profile* profile) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| std::unique_ptr<ProfileParams> params(new ProfileParams); |
| params->path = profile->GetPath(); |
| |
| params->cookie_settings = CookieSettingsFactory::GetForProfile(profile); |
| params->host_content_settings_map = |
| HostContentSettingsMapFactory::GetForProfile(profile); |
| |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| params->extension_info_map = |
| extensions::ExtensionSystem::Get(profile)->info_map(); |
| #endif |
| |
| ProtocolHandlerRegistry* protocol_handler_registry = |
| ProtocolHandlerRegistryFactory::GetForBrowserContext(profile); |
| DCHECK(protocol_handler_registry); |
| |
| #if defined(OS_CHROMEOS) |
| 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 a 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()) { |
| params->username_hash = user->username_hash(); |
| DCHECK(!params->username_hash.empty()); |
| base::PostTask(FROM_HERE, {BrowserThread::IO}, |
| base::BindOnce(&StartNSSInitOnIOThread, user->GetAccountId(), |
| user->username_hash(), profile->GetPath())); |
| |
| if (user->IsAffiliated()) { |
| params->user_is_affiliated = true; |
| } |
| } |
| #endif |
| |
| params->profile = profile; |
| profile_params_ = std::move(params); |
| |
| scoped_refptr<base::SingleThreadTaskRunner> io_task_runner = |
| base::CreateSingleThreadTaskRunner({BrowserThread::IO}); |
| |
| // We need to make sure that content initializes its own data structures that |
| // are associated with each ResourceContext because we might post this |
| // object to the IO thread after this function. |
| BrowserContext::EnsureResourceContextInitialized(profile); |
| |
| base::PostTask(FROM_HERE, {BrowserThread::IO}, |
| base::BindOnce(&ProfileIOData::Init, base::Unretained(this))); |
| } |
| |
| ProfileIOData::ProfileParams::ProfileParams() = default; |
| |
| ProfileIOData::ProfileParams::~ProfileParams() = default; |
| |
| ProfileIOData::ProfileIOData() |
| : initialized_(false), |
| resource_context_(new ResourceContext(this)) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| } |
| |
| ProfileIOData::~ProfileIOData() { |
| if (BrowserThread::IsThreadInitialized(BrowserThread::IO)) |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| } |
| |
| // static |
| ProfileIOData* ProfileIOData::FromResourceContext( |
| content::ResourceContext* rc) { |
| return (static_cast<ResourceContext*>(rc))->io_data_; |
| } |
| |
| // static |
| bool ProfileIOData::IsHandledProtocol(const std::string& scheme) { |
| DCHECK_EQ(scheme, base::ToLowerASCII(scheme)); |
| static const char* const kProtocolList[] = { |
| url::kHttpScheme, |
| url::kHttpsScheme, |
| #if BUILDFLAG(ENABLE_WEBSOCKETS) |
| url::kWsScheme, |
| url::kWssScheme, |
| #endif // BUILDFLAG(ENABLE_WEBSOCKETS) |
| url::kFileScheme, |
| content::kChromeDevToolsScheme, |
| dom_distiller::kDomDistillerScheme, |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| extensions::kExtensionScheme, |
| #endif |
| content::kChromeUIScheme, |
| url::kDataScheme, |
| #if defined(OS_CHROMEOS) |
| content::kExternalFileScheme, |
| #endif // defined(OS_CHROMEOS) |
| #if defined(OS_ANDROID) |
| url::kContentScheme, |
| #endif // defined(OS_ANDROID) |
| url::kAboutScheme, |
| url::kBlobScheme, |
| url::kFileSystemScheme, |
| chrome::kChromeSearchScheme, |
| }; |
| for (const char* supported_protocol : kProtocolList) { |
| if (scheme == supported_protocol) |
| return true; |
| } |
| #if !BUILDFLAG(DISABLE_FTP_SUPPORT) |
| if (scheme == url::kFtpScheme && |
| base::FeatureList::IsEnabled(features::kFtpProtocol)) { |
| return true; |
| } |
| #endif // !BUILDFLAG(DISABLE_FTP_SUPPORT) |
| return false; |
| } |
| |
| // static |
| bool ProfileIOData::IsHandledURL(const GURL& url) { |
| if (!url.is_valid()) { |
| // We handle error cases. |
| return true; |
| } |
| |
| return IsHandledProtocol(url.scheme()); |
| } |
| |
| content::ResourceContext* ProfileIOData::GetResourceContext() const { |
| return resource_context_.get(); |
| } |
| |
| extensions::InfoMap* ProfileIOData::GetExtensionInfoMap() const { |
| DCHECK(initialized_) << "ExtensionSystem not initialized"; |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| return extension_info_map_.get(); |
| #else |
| return nullptr; |
| #endif |
| } |
| |
| content_settings::CookieSettings* ProfileIOData::GetCookieSettings() const { |
| // Allow either Init() or SetCookieSettingsForTesting() to initialize. |
| DCHECK(initialized_ || cookie_settings_.get()); |
| return cookie_settings_.get(); |
| } |
| |
| HostContentSettingsMap* ProfileIOData::GetHostContentSettingsMap() const { |
| DCHECK(initialized_); |
| return host_content_settings_map_.get(); |
| } |
| |
| ProfileIOData::ResourceContext::ResourceContext(ProfileIOData* io_data) |
| : io_data_(io_data) { |
| DCHECK(io_data); |
| } |
| |
| ProfileIOData::ResourceContext::~ResourceContext() {} |
| |
| void ProfileIOData::Init() const { |
| // The basic logic is implemented here. The specific initialization |
| // is done in InitializeInternal(), implemented by subtypes. Static helper |
| // functions have been provided to assist in common operations. |
| DCHECK_CURRENTLY_ON(BrowserThread::IO); |
| DCHECK(!initialized_); |
| DCHECK(profile_params_.get()); |
| |
| // Take ownership over these parameters. |
| cookie_settings_ = profile_params_->cookie_settings; |
| host_content_settings_map_ = profile_params_->host_content_settings_map; |
| #if BUILDFLAG(ENABLE_EXTENSIONS) |
| extension_info_map_ = profile_params_->extension_info_map; |
| #endif |
| |
| #if defined(OS_CHROMEOS) |
| username_hash_ = profile_params_->username_hash; |
| // If we're using the system slot for certificate management, we also must |
| // have access to the user's slots. |
| DCHECK(!(username_hash_.empty() && profile_params_->user_is_affiliated)); |
| // Use the device-wide system key slot only if the user is affiliated on |
| // the device. |
| if (profile_params_->user_is_affiliated) { |
| EnableNSSSystemKeySlotForResourceContext(resource_context_.get()); |
| } |
| #endif |
| |
| profile_params_.reset(); |
| initialized_ = true; |
| } |
| |
| void ProfileIOData::ShutdownOnUIThread() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| safe_browsing_enabled_.Destroy(); |
| |
| bool posted = base::DeleteSoon(FROM_HERE, {BrowserThread::IO}, this); |
| if (!posted) |
| delete this; |
| } |
| |
| void ProfileIOData::DestroyResourceContext() { |
| resource_context_.reset(); |
| } |