blob: 09bf51b9162749083a78e531eaefbcc4f58d74d8 [file] [log] [blame]
// 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();
}