blob: 206e9c42fa7d166b5568fba71b4d44e2df4a863b [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/safe_browsing/safe_browsing_service.h"
#include <stddef.h>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/macros.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/task/post_task.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/safe_browsing/safe_browsing_navigation_observer_manager.h"
#include "chrome/browser/safe_browsing/ui_manager.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/safe_browsing/file_type_policies.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
#include "components/safe_browsing/browser/safe_browsing_network_context.h"
#include "components/safe_browsing/browser/safe_browsing_url_request_context_getter.h"
#include "components/safe_browsing/common/safebrowsing_constants.h"
#include "components/safe_browsing/db/database_manager.h"
#include "components/safe_browsing/ping_manager.h"
#include "components/safe_browsing/triggers/trigger_manager.h"
#include "components/safe_browsing/web_ui/safe_browsing_ui.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/resource_request_info.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/network/public/cpp/features.h"
#include "services/preferences/public/mojom/tracked_preference_validation_delegate.mojom.h"
#if defined(OS_WIN)
#include "chrome/install_static/install_util.h"
#endif
#if defined(FULL_SAFE_BROWSING)
#include "chrome/browser/safe_browsing/client_side_detection_service.h"
#include "chrome/browser/safe_browsing/download_protection/download_protection_service.h"
#include "chrome/browser/safe_browsing/incident_reporting/binary_integrity_analyzer.h"
#include "chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h"
#include "chrome/browser/safe_browsing/incident_reporting/resource_request_detector.h"
#include "components/safe_browsing/password_protection/password_protection_service.h"
#endif
using content::BrowserThread;
using content::NonNestable;
namespace safe_browsing {
#if defined(FULL_SAFE_BROWSING)
namespace {
// 50 was chosen as an arbitrary upper bound on the likely font sizes. For
// reference, the "Font size" dropdown in settings lets you select between 9,
// 12, 16, 20, or 24.
const int kMaximumTrackedFontSize = 50;
void RecordFontSizeMetrics(const PrefService& pref_service) {
UMA_HISTOGRAM_EXACT_LINEAR(
"SafeBrowsing.FontSize.Default",
pref_service.GetInteger(prefs::kWebKitDefaultFontSize),
kMaximumTrackedFontSize);
UMA_HISTOGRAM_EXACT_LINEAR(
"SafeBrowsing.FontSize.DefaultFixed",
pref_service.GetInteger(prefs::kWebKitDefaultFixedFontSize),
kMaximumTrackedFontSize);
UMA_HISTOGRAM_EXACT_LINEAR(
"SafeBrowsing.FontSize.Minimum",
pref_service.GetInteger(prefs::kWebKitMinimumFontSize),
kMaximumTrackedFontSize);
UMA_HISTOGRAM_EXACT_LINEAR(
"SafeBrowsing.FontSize.MinimumLogical",
pref_service.GetInteger(prefs::kWebKitMinimumLogicalFontSize),
kMaximumTrackedFontSize);
}
} // namespace
#endif
// static
base::FilePath SafeBrowsingService::GetCookieFilePathForTesting() {
return base::FilePath(SafeBrowsingService::GetBaseFilename().value() +
safe_browsing::kCookiesFile);
}
// static
base::FilePath SafeBrowsingService::GetBaseFilename() {
base::FilePath path;
bool result = base::PathService::Get(chrome::DIR_USER_DATA, &path);
DCHECK(result);
return path.Append(safe_browsing::kSafeBrowsingBaseFilename);
}
SafeBrowsingService::SafeBrowsingService()
: services_delegate_(ServicesDelegate::Create(this)),
estimated_extended_reporting_by_prefs_(SBER_LEVEL_OFF),
shutdown_(false),
enabled_(false),
enabled_by_prefs_(false) {}
SafeBrowsingService::~SafeBrowsingService() {
// We should have already been shut down. If we're still enabled, then the
// database isn't going to be closed properly, which could lead to corruption.
DCHECK(!enabled_);
}
void SafeBrowsingService::Initialize() {
// Ensure FileTypePolicies's Singleton is instantiated during startup.
// This guarantees we'll log UMA metrics about its state.
FileTypePolicies::GetInstance();
base::FilePath user_data_dir;
bool result = base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
DCHECK(result);
if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) {
url_request_context_getter_ = new SafeBrowsingURLRequestContextGetter(
g_browser_process->system_request_context(), user_data_dir);
}
network_context_ =
std::make_unique<safe_browsing::SafeBrowsingNetworkContext>(
url_request_context_getter_, user_data_dir,
base::BindRepeating(&SafeBrowsingService::CreateNetworkContextParams,
base::Unretained(this)));
WebUIInfoSingleton::GetInstance()->set_network_context(
network_context_.get());
ui_manager_ = CreateUIManager();
navigation_observer_manager_ = new SafeBrowsingNavigationObserverManager();
services_delegate_->Initialize();
services_delegate_->InitializeCsdService(GetURLLoaderFactory());
// Needs to happen after |ui_manager_| is created.
CreateTriggerManager();
// Track profile creation and destruction.
profiles_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
content::NotificationService::AllSources());
profiles_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
content::NotificationService::AllSources());
// Register all the delayed analysis to the incident reporting service.
RegisterAllDelayedAnalysis();
}
void SafeBrowsingService::ShutDown() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
shutdown_ = true;
// Remove Profile creation/destruction observers.
profiles_registrar_.RemoveAll();
// Delete the PrefChangeRegistrars, whose dtors also unregister |this| as an
// observer of the preferences.
prefs_map_.clear();
Stop(true);
services_delegate_->ShutdownServices();
// Make sure to destruct SafeBrowsingNetworkContext first before
// |url_request_context_getter_|, as they both post tasks to the IO thread. We
// want the underlying NetworkContext C++ class to be torn down first so that
// it destroys any URLLoaders in flight.
network_context_->ServiceShuttingDown();
proxy_config_monitor_.reset();
if (!url_request_context_getter_)
return;
// Since URLRequestContextGetters are refcounted, can't count on clearing
// |url_request_context_getter_| to delete it, so need to shut it down first,
// which will cancel any requests that are currently using it, and prevent
// new requests from using it as well.
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO, NonNestable()},
base::BindOnce(&SafeBrowsingURLRequestContextGetter::ServiceShuttingDown,
url_request_context_getter_));
// Release the URLRequestContextGetter after passing it to the IOThread. It
// has to be released now rather than in the destructor because it can only
// be deleted on the IOThread, and the SafeBrowsingService outlives the IO
// thread.
url_request_context_getter_ = nullptr;
}
scoped_refptr<net::URLRequestContextGetter>
SafeBrowsingService::url_request_context() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
CHECK(false);
// TODO(jam): remove this after
// chrome_browsing_data_remover_delegate_unittest.cc is converted to use the
// Network Service APIs instead of URLRequestContext directly.
// https://crbug.com/721398
return nullptr;
}
network::mojom::NetworkContext* SafeBrowsingService::GetNetworkContext() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return network_context_->GetNetworkContext();
}
scoped_refptr<network::SharedURLLoaderFactory>
SafeBrowsingService::GetURLLoaderFactory() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!network_context_)
return nullptr;
return network_context_->GetURLLoaderFactory();
}
void SafeBrowsingService::FlushNetworkInterfaceForTesting() {
if (network_context_)
network_context_->FlushForTesting();
}
scoped_refptr<network::SharedURLLoaderFactory>
SafeBrowsingService::GetURLLoaderFactoryOnIOThread() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!shared_url_loader_factory_on_io_) {
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(&SafeBrowsingService::CreateURLLoaderFactoryForIO, this,
MakeRequest(&url_loader_factory_on_io_)));
shared_url_loader_factory_on_io_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
url_loader_factory_on_io_.get());
}
return shared_url_loader_factory_on_io_;
}
const scoped_refptr<SafeBrowsingUIManager>& SafeBrowsingService::ui_manager()
const {
return ui_manager_;
}
const scoped_refptr<SafeBrowsingDatabaseManager>&
SafeBrowsingService::database_manager() const {
return services_delegate_->database_manager();
}
scoped_refptr<SafeBrowsingNavigationObserverManager>
SafeBrowsingService::navigation_observer_manager() {
return navigation_observer_manager_;
}
PingManager* SafeBrowsingService::ping_manager() const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return ping_manager_.get();
}
TriggerManager* SafeBrowsingService::trigger_manager() const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return trigger_manager_.get();
}
PasswordProtectionService* SafeBrowsingService::GetPasswordProtectionService(
Profile* profile) const {
if (profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled))
return services_delegate_->GetPasswordProtectionService(profile);
return nullptr;
}
std::unique_ptr<prefs::mojom::TrackedPreferenceValidationDelegate>
SafeBrowsingService::CreatePreferenceValidationDelegate(
Profile* profile) const {
return services_delegate_->CreatePreferenceValidationDelegate(profile);
}
void SafeBrowsingService::RegisterDelayedAnalysisCallback(
const DelayedAnalysisCallback& callback) {
services_delegate_->RegisterDelayedAnalysisCallback(callback);
}
void SafeBrowsingService::AddDownloadManager(
content::DownloadManager* download_manager) {
services_delegate_->AddDownloadManager(download_manager);
}
void SafeBrowsingService::OnResourceRequest(const net::URLRequest* request) {
#if defined(FULL_SAFE_BROWSING)
DCHECK_CURRENTLY_ON(BrowserThread::IO);
TRACE_EVENT1("loading", "SafeBrowsingService::OnResourceRequest", "url",
request->url().spec());
ResourceRequestInfo info = ResourceRequestDetector::GetRequestInfo(request);
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(&SafeBrowsingService::ProcessResourceRequest, this, info));
#endif
}
SafeBrowsingUIManager* SafeBrowsingService::CreateUIManager() {
return new SafeBrowsingUIManager(this);
}
void SafeBrowsingService::RegisterAllDelayedAnalysis() {
#if defined(FULL_SAFE_BROWSING)
RegisterBinaryIntegrityAnalysis();
#endif
}
V4ProtocolConfig SafeBrowsingService::GetV4ProtocolConfig() const {
base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
return ::safe_browsing::GetV4ProtocolConfig(
GetProtocolConfigClientName(),
cmdline->HasSwitch(::switches::kDisableBackgroundNetworking));
}
std::string SafeBrowsingService::GetProtocolConfigClientName() const {
std::string client_name;
// On Windows, get the safe browsing client name from the browser
// distribution classes in installer util. These classes don't yet have
// an analog on non-Windows builds so just keep the name specified here.
#if defined(OS_WIN)
client_name = install_static::GetSafeBrowsingName();
#else
#if defined(GOOGLE_CHROME_BUILD)
client_name = "googlechrome";
#else
client_name = "chromium";
#endif
// Mark client string to allow server to differentiate mobile.
#if defined(OS_ANDROID)
client_name.append("-a");
#endif
#endif // defined(OS_WIN)
return client_name;
}
void SafeBrowsingService::SetDatabaseManagerForTest(
SafeBrowsingDatabaseManager* database_manager) {
services_delegate_->SetDatabaseManagerForTest(database_manager);
}
void SafeBrowsingService::StartOnIOThread() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (enabled_)
return;
enabled_ = true;
V4ProtocolConfig v4_config = GetV4ProtocolConfig();
services_delegate_->StartOnIOThread(GetURLLoaderFactoryOnIOThread(),
v4_config);
}
void SafeBrowsingService::StopOnIOThread(bool shutdown) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
services_delegate_->StopOnIOThread(shutdown);
if (enabled_) {
enabled_ = false;
}
if (shared_url_loader_factory_on_io_)
shared_url_loader_factory_on_io_->Detach();
url_loader_factory_on_io_.reset();
}
void SafeBrowsingService::Start() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!ping_manager_) {
ping_manager_ =
PingManager::Create(GetURLLoaderFactory(), GetV4ProtocolConfig());
}
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(&SafeBrowsingService::StartOnIOThread, this));
}
void SafeBrowsingService::Stop(bool shutdown) {
ping_manager_.reset();
ui_manager_->Stop(shutdown);
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(&SafeBrowsingService::StopOnIOThread, this, shutdown));
}
void SafeBrowsingService::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case chrome::NOTIFICATION_PROFILE_CREATED: {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
Profile* profile = content::Source<Profile>(source).ptr();
services_delegate_->CreatePasswordProtectionService(profile);
services_delegate_->CreateTelemetryService(profile);
if (!profile->IsOffTheRecord())
AddPrefService(profile->GetPrefs());
break;
}
case chrome::NOTIFICATION_PROFILE_DESTROYED: {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
Profile* profile = content::Source<Profile>(source).ptr();
services_delegate_->RemovePasswordProtectionService(profile);
services_delegate_->RemoveTelemetryService();
if (!profile->IsOffTheRecord())
RemovePrefService(profile->GetPrefs());
break;
}
default:
NOTREACHED();
}
}
void SafeBrowsingService::AddPrefService(PrefService* pref_service) {
DCHECK(prefs_map_.find(pref_service) == prefs_map_.end());
std::unique_ptr<PrefChangeRegistrar> registrar =
std::make_unique<PrefChangeRegistrar>();
registrar->Init(pref_service);
registrar->Add(
prefs::kSafeBrowsingEnabled,
base::Bind(&SafeBrowsingService::RefreshState, base::Unretained(this)));
// ClientSideDetectionService will need to be refresh the models
// renderers have if extended-reporting changes.
registrar->Add(
prefs::kSafeBrowsingScoutReportingEnabled,
base::Bind(&SafeBrowsingService::RefreshState, base::Unretained(this)));
prefs_map_[pref_service] = std::move(registrar);
RefreshState();
// Record the current pref state.
UMA_HISTOGRAM_BOOLEAN("SafeBrowsing.Pref.General",
pref_service->GetBoolean(prefs::kSafeBrowsingEnabled));
// Extended Reporting metrics are handled together elsewhere.
RecordExtendedReportingMetrics(*pref_service);
#if defined(FULL_SAFE_BROWSING)
RecordFontSizeMetrics(*pref_service);
#endif
}
void SafeBrowsingService::RemovePrefService(PrefService* pref_service) {
if (prefs_map_.find(pref_service) != prefs_map_.end()) {
prefs_map_.erase(pref_service);
RefreshState();
} else {
NOTREACHED();
}
}
std::unique_ptr<SafeBrowsingService::StateSubscription>
SafeBrowsingService::RegisterStateCallback(
const base::Callback<void(void)>& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return state_callback_list_.Add(callback);
}
void SafeBrowsingService::RefreshState() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Check if any profile requires the service to be active.
enabled_by_prefs_ = false;
estimated_extended_reporting_by_prefs_ = SBER_LEVEL_OFF;
for (const auto& pref : prefs_map_) {
if (pref.first->GetBoolean(prefs::kSafeBrowsingEnabled)) {
enabled_by_prefs_ = true;
ExtendedReportingLevel erl =
safe_browsing::GetExtendedReportingLevel(*pref.first);
if (erl != SBER_LEVEL_OFF) {
estimated_extended_reporting_by_prefs_ = erl;
break;
}
}
}
if (enabled_by_prefs_)
Start();
else
Stop(false);
state_callback_list_.Notify();
services_delegate_->RefreshState(enabled_by_prefs_);
}
void SafeBrowsingService::SendSerializedDownloadReport(
const std::string& report) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (ping_manager())
ping_manager()->ReportThreatDetails(report);
}
void SafeBrowsingService::ProcessResourceRequest(
const ResourceRequestInfo& request) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
services_delegate_->ProcessResourceRequest(&request);
}
void SafeBrowsingService::CreateTriggerManager() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
trigger_manager_ = std::make_unique<TriggerManager>(
ui_manager_.get(), navigation_observer_manager_.get(),
g_browser_process->local_state());
}
void SafeBrowsingService::CreateURLLoaderFactoryForIO(
network::mojom::URLLoaderFactoryRequest request) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (shutdown_)
return; // We've been shut down already.
network::mojom::URLLoaderFactoryParamsPtr params =
network::mojom::URLLoaderFactoryParams::New();
params->process_id = network::mojom::kBrowserProcessId;
params->is_corb_enabled = false;
GetNetworkContext()->CreateURLLoaderFactory(std::move(request),
std::move(params));
}
network::mojom::NetworkContextParamsPtr
SafeBrowsingService::CreateNetworkContextParams() {
auto params = g_browser_process->system_network_context_manager()
->CreateDefaultNetworkContextParams();
if (!proxy_config_monitor_) {
proxy_config_monitor_ =
std::make_unique<ProxyConfigMonitor>(g_browser_process->local_state());
}
proxy_config_monitor_->AddToNetworkContextParams(params.get());
return params;
}
// The default SafeBrowsingServiceFactory. Global, made a singleton so we
// don't leak it.
class SafeBrowsingServiceFactoryImpl : public SafeBrowsingServiceFactory {
public:
// TODO(crbug/925153): Once callers of this function are no longer downcasting
// it to the SafeBrowsingService, we can make this a scoped_refptr.
SafeBrowsingServiceInterface* CreateSafeBrowsingService() override {
return new SafeBrowsingService();
}
private:
friend class base::NoDestructor<SafeBrowsingServiceFactoryImpl>;
SafeBrowsingServiceFactoryImpl() {}
DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceFactoryImpl);
};
SafeBrowsingServiceFactory* GetSafeBrowsingServiceFactory() {
static base::NoDestructor<SafeBrowsingServiceFactoryImpl> factory;
return factory.get();
}
} // namespace safe_browsing