| // 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 "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/callback.h" |
| #include "base/command_line.h" |
| #include "base/debug/leak_tracker.h" |
| #include "base/lazy_instance.h" |
| #include "base/path_service.h" |
| #include "base/prefs/pref_change_registrar.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_util.h" |
| #include "base/threading/thread.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/metrics/metrics_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/profiles/profile_manager.h" |
| #include "chrome/browser/safe_browsing/client_side_detection_service.h" |
| #include "chrome/browser/safe_browsing/database_manager.h" |
| #include "chrome/browser/safe_browsing/download_protection_service.h" |
| #include "chrome/browser/safe_browsing/malware_details.h" |
| #include "chrome/browser/safe_browsing/ping_manager.h" |
| #include "chrome/browser/safe_browsing/protocol_manager.h" |
| #include "chrome/browser/safe_browsing/safe_browsing_database.h" |
| #include "chrome/browser/safe_browsing/ui_manager.h" |
| #include "chrome/common/chrome_constants.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/startup_metric_utils.h" |
| #include "chrome/common/url_constants.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/cookie_store_factory.h" |
| #include "content/public/browser/notification_service.h" |
| #include "net/cookies/cookie_monster.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_context_getter.h" |
| |
| #if defined(OS_WIN) |
| #include "chrome/installer/util/browser_distribution.h" |
| #endif |
| |
| using content::BrowserThread; |
| |
| namespace { |
| |
| // Filename suffix for the cookie database. |
| const base::FilePath::CharType kCookiesFile[] = FILE_PATH_LITERAL(" Cookies"); |
| |
| // The default URL prefix where browser fetches chunk updates, hashes, |
| // and reports safe browsing hits and malware details. |
| const char* const kSbDefaultURLPrefix = |
| "https://safebrowsing.google.com/safebrowsing"; |
| |
| // The backup URL prefix used when there are issues establishing a connection |
| // with the server at the primary URL. |
| const char* const kSbBackupConnectErrorURLPrefix = |
| "https://alt1-safebrowsing.google.com/safebrowsing"; |
| |
| // The backup URL prefix used when there are HTTP-specific issues with the |
| // server at the primary URL. |
| const char* const kSbBackupHttpErrorURLPrefix = |
| "https://alt2-safebrowsing.google.com/safebrowsing"; |
| |
| // The backup URL prefix used when there are local network specific issues. |
| const char* const kSbBackupNetworkErrorURLPrefix = |
| "https://alt3-safebrowsing.google.com/safebrowsing"; |
| |
| base::FilePath CookieFilePath() { |
| return base::FilePath( |
| SafeBrowsingService::GetBaseFilename().value() + kCookiesFile); |
| } |
| |
| } // namespace |
| |
| class SafeBrowsingURLRequestContextGetter |
| : public net::URLRequestContextGetter { |
| public: |
| explicit SafeBrowsingURLRequestContextGetter( |
| SafeBrowsingService* sb_service_); |
| |
| // Implementation for net::UrlRequestContextGetter. |
| virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE; |
| virtual scoped_refptr<base::SingleThreadTaskRunner> |
| GetNetworkTaskRunner() const OVERRIDE; |
| |
| protected: |
| virtual ~SafeBrowsingURLRequestContextGetter(); |
| |
| private: |
| SafeBrowsingService* const sb_service_; // Owned by BrowserProcess. |
| scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; |
| |
| base::debug::LeakTracker<SafeBrowsingURLRequestContextGetter> leak_tracker_; |
| }; |
| |
| SafeBrowsingURLRequestContextGetter::SafeBrowsingURLRequestContextGetter( |
| SafeBrowsingService* sb_service) |
| : sb_service_(sb_service), |
| network_task_runner_( |
| BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)) { |
| } |
| |
| SafeBrowsingURLRequestContextGetter::~SafeBrowsingURLRequestContextGetter() {} |
| |
| net::URLRequestContext* |
| SafeBrowsingURLRequestContextGetter::GetURLRequestContext() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| DCHECK(sb_service_->url_request_context_.get()); |
| |
| return sb_service_->url_request_context_.get(); |
| } |
| |
| scoped_refptr<base::SingleThreadTaskRunner> |
| SafeBrowsingURLRequestContextGetter::GetNetworkTaskRunner() const { |
| return network_task_runner_; |
| } |
| |
| // static |
| SafeBrowsingServiceFactory* SafeBrowsingService::factory_ = NULL; |
| |
| // The default SafeBrowsingServiceFactory. Global, made a singleton so we |
| // don't leak it. |
| class SafeBrowsingServiceFactoryImpl : public SafeBrowsingServiceFactory { |
| public: |
| virtual SafeBrowsingService* CreateSafeBrowsingService() OVERRIDE { |
| return new SafeBrowsingService(); |
| } |
| |
| private: |
| friend struct base::DefaultLazyInstanceTraits<SafeBrowsingServiceFactoryImpl>; |
| |
| SafeBrowsingServiceFactoryImpl() { } |
| |
| DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceFactoryImpl); |
| }; |
| |
| static base::LazyInstance<SafeBrowsingServiceFactoryImpl> |
| g_safe_browsing_service_factory_impl = LAZY_INSTANCE_INITIALIZER; |
| |
| // static |
| base::FilePath SafeBrowsingService::GetCookieFilePathForTesting() { |
| return CookieFilePath(); |
| } |
| |
| // static |
| base::FilePath SafeBrowsingService::GetBaseFilename() { |
| base::FilePath path; |
| bool result = PathService::Get(chrome::DIR_USER_DATA, &path); |
| DCHECK(result); |
| return path.Append(chrome::kSafeBrowsingBaseFilename); |
| } |
| |
| |
| // static |
| SafeBrowsingService* SafeBrowsingService::CreateSafeBrowsingService() { |
| if (!factory_) |
| factory_ = g_safe_browsing_service_factory_impl.Pointer(); |
| return factory_->CreateSafeBrowsingService(); |
| } |
| |
| SafeBrowsingService::SafeBrowsingService() |
| : protocol_manager_(NULL), |
| ping_manager_(NULL), |
| enabled_(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() { |
| startup_metric_utils::ScopedSlowStartupUMA |
| scoped_timer("Startup.SlowStartupSafeBrowsingServiceInitialize"); |
| |
| url_request_context_getter_ = |
| new SafeBrowsingURLRequestContextGetter(this); |
| |
| ui_manager_ = CreateUIManager(); |
| |
| database_manager_ = CreateDatabaseManager(); |
| |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind( |
| &SafeBrowsingService::InitURLRequestContextOnIOThread, this, |
| make_scoped_refptr(g_browser_process->system_request_context()))); |
| |
| #if defined(FULL_SAFE_BROWSING) |
| if (!CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableClientSidePhishingDetection)) { |
| csd_service_.reset(safe_browsing::ClientSideDetectionService::Create( |
| url_request_context_getter_.get())); |
| } |
| download_service_.reset(new safe_browsing::DownloadProtectionService( |
| this, url_request_context_getter_.get())); |
| #endif |
| |
| // Track the safe browsing preference of existing profiles. |
| // The SafeBrowsingService will be started if any existing profile has the |
| // preference enabled. It will also listen for updates to the preferences. |
| ProfileManager* profile_manager = g_browser_process->profile_manager(); |
| if (profile_manager) { |
| std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles(); |
| for (size_t i = 0; i < profiles.size(); ++i) { |
| if (profiles[i]->IsOffTheRecord()) |
| continue; |
| AddPrefService(profiles[i]->GetPrefs()); |
| } |
| } |
| |
| // Track profile creation and destruction. |
| prefs_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED, |
| content::NotificationService::AllSources()); |
| prefs_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED, |
| content::NotificationService::AllSources()); |
| } |
| |
| void SafeBrowsingService::ShutDown() { |
| // Deletes the PrefChangeRegistrars, whose dtors also unregister |this| as an |
| // observer of the preferences. |
| STLDeleteValues(&prefs_map_); |
| |
| // Remove Profile creation/destruction observers. |
| prefs_registrar_.RemoveAll(); |
| |
| Stop(true); |
| // The IO thread is going away, so make sure the ClientSideDetectionService |
| // dtor executes now since it may call the dtor of URLFetcher which relies |
| // on it. |
| csd_service_.reset(); |
| download_service_.reset(); |
| |
| url_request_context_getter_ = NULL; |
| BrowserThread::PostNonNestableTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&SafeBrowsingService::DestroyURLRequestContextOnIOThread, |
| this)); |
| } |
| |
| // Binhash verification is only enabled for UMA users for now. |
| bool SafeBrowsingService::DownloadBinHashNeeded() const { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| #if defined(FULL_SAFE_BROWSING) |
| return (database_manager_->download_protection_enabled() && |
| ui_manager_->CanReportStats()) || |
| (download_protection_service() && |
| download_protection_service()->enabled()); |
| #else |
| return false; |
| #endif |
| } |
| |
| net::URLRequestContextGetter* SafeBrowsingService::url_request_context() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| return url_request_context_getter_.get(); |
| } |
| |
| const scoped_refptr<SafeBrowsingUIManager>& |
| SafeBrowsingService::ui_manager() const { |
| return ui_manager_; |
| } |
| |
| const scoped_refptr<SafeBrowsingDatabaseManager>& |
| SafeBrowsingService::database_manager() const { |
| return database_manager_; |
| } |
| |
| SafeBrowsingProtocolManager* SafeBrowsingService::protocol_manager() const { |
| return protocol_manager_; |
| } |
| |
| SafeBrowsingPingManager* SafeBrowsingService::ping_manager() const { |
| return ping_manager_; |
| } |
| |
| SafeBrowsingUIManager* SafeBrowsingService::CreateUIManager() { |
| return new SafeBrowsingUIManager(this); |
| } |
| |
| SafeBrowsingDatabaseManager* SafeBrowsingService::CreateDatabaseManager() { |
| |
| #if defined(FULL_SAFE_BROWSING) |
| return new SafeBrowsingDatabaseManager(this); |
| #else |
| return NULL; |
| #endif |
| } |
| |
| void SafeBrowsingService::InitURLRequestContextOnIOThread( |
| net::URLRequestContextGetter* system_url_request_context_getter) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| DCHECK(!url_request_context_.get()); |
| |
| scoped_refptr<net::CookieStore> cookie_store( |
| content::CreatePersistentCookieStore( |
| CookieFilePath(), |
| false, |
| NULL, |
| NULL)); |
| |
| url_request_context_.reset(new net::URLRequestContext); |
| // |system_url_request_context_getter| may be NULL during tests. |
| if (system_url_request_context_getter) { |
| url_request_context_->CopyFrom( |
| system_url_request_context_getter->GetURLRequestContext()); |
| } |
| url_request_context_->set_cookie_store(cookie_store.get()); |
| } |
| |
| void SafeBrowsingService::DestroyURLRequestContextOnIOThread() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| url_request_context_->AssertNoURLRequests(); |
| |
| // Need to do the CheckForLeaks on IOThread instead of in ShutDown where |
| // url_request_context_getter_ is cleared, since the URLRequestContextGetter |
| // will PostTask to IOTread to delete itself. |
| using base::debug::LeakTracker; |
| LeakTracker<SafeBrowsingURLRequestContextGetter>::CheckForLeaks(); |
| |
| url_request_context_.reset(); |
| } |
| |
| void SafeBrowsingService::StartOnIOThread() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| if (enabled_) |
| return; |
| enabled_ = true; |
| |
| SafeBrowsingProtocolConfig config; |
| // 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) |
| BrowserDistribution* dist = BrowserDistribution::GetDistribution(); |
| config.client_name = dist->GetSafeBrowsingName(); |
| #else |
| #if defined(GOOGLE_CHROME_BUILD) |
| config.client_name = "googlechrome"; |
| #else |
| config.client_name = "chromium"; |
| #endif |
| #endif |
| CommandLine* cmdline = CommandLine::ForCurrentProcess(); |
| config.disable_auto_update = |
| cmdline->HasSwitch(switches::kSbDisableAutoUpdate) || |
| cmdline->HasSwitch(switches::kDisableBackgroundNetworking); |
| if (cmdline->HasSwitch(switches::kSbURLPrefix)) { |
| config.url_prefix = cmdline->GetSwitchValueASCII(switches::kSbURLPrefix); |
| } else { |
| config.url_prefix = kSbDefaultURLPrefix; |
| config.backup_connect_error_url_prefix = kSbBackupConnectErrorURLPrefix; |
| config.backup_http_error_url_prefix = kSbBackupHttpErrorURLPrefix; |
| config.backup_network_error_url_prefix = kSbBackupNetworkErrorURLPrefix; |
| } |
| |
| #if defined(FULL_SAFE_BROWSING) |
| DCHECK(database_manager_.get()); |
| database_manager_->StartOnIOThread(); |
| |
| DCHECK(!protocol_manager_); |
| protocol_manager_ = SafeBrowsingProtocolManager::Create( |
| database_manager_.get(), url_request_context_getter_.get(), config); |
| protocol_manager_->Initialize(); |
| #endif |
| |
| DCHECK(!ping_manager_); |
| ping_manager_ = SafeBrowsingPingManager::Create( |
| url_request_context_getter_.get(), config); |
| } |
| |
| void SafeBrowsingService::StopOnIOThread(bool shutdown) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| |
| #if defined(FULL_SAFE_BROWSING) |
| database_manager_->StopOnIOThread(shutdown); |
| #endif |
| ui_manager_->StopOnIOThread(shutdown); |
| |
| if (enabled_) { |
| enabled_ = false; |
| |
| #if defined(FULL_SAFE_BROWSING) |
| // This cancels all in-flight GetHash requests. Note that database_manager_ |
| // relies on the protocol_manager_ so if the latter is destroyed, the |
| // former must be stopped. |
| delete protocol_manager_; |
| protocol_manager_ = NULL; |
| #endif |
| delete ping_manager_; |
| ping_manager_ = NULL; |
| } |
| } |
| |
| void SafeBrowsingService::Start() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&SafeBrowsingService::StartOnIOThread, this)); |
| } |
| |
| void SafeBrowsingService::Stop(bool shutdown) { |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&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(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| Profile* profile = content::Source<Profile>(source).ptr(); |
| if (!profile->IsOffTheRecord()) |
| AddPrefService(profile->GetPrefs()); |
| break; |
| } |
| case chrome::NOTIFICATION_PROFILE_DESTROYED: { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| Profile* profile = content::Source<Profile>(source).ptr(); |
| if (!profile->IsOffTheRecord()) |
| RemovePrefService(profile->GetPrefs()); |
| break; |
| } |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void SafeBrowsingService::AddPrefService(PrefService* pref_service) { |
| DCHECK(prefs_map_.find(pref_service) == prefs_map_.end()); |
| PrefChangeRegistrar* registrar = new PrefChangeRegistrar(); |
| registrar->Init(pref_service); |
| registrar->Add(prefs::kSafeBrowsingEnabled, |
| base::Bind(&SafeBrowsingService::RefreshState, |
| base::Unretained(this))); |
| prefs_map_[pref_service] = registrar; |
| RefreshState(); |
| } |
| |
| void SafeBrowsingService::RemovePrefService(PrefService* pref_service) { |
| if (prefs_map_.find(pref_service) != prefs_map_.end()) { |
| delete prefs_map_[pref_service]; |
| prefs_map_.erase(pref_service); |
| RefreshState(); |
| } else { |
| NOTREACHED(); |
| } |
| } |
| |
| void SafeBrowsingService::RefreshState() { |
| // Check if any profile requires the service to be active. |
| bool enable = false; |
| std::map<PrefService*, PrefChangeRegistrar*>::iterator iter; |
| for (iter = prefs_map_.begin(); iter != prefs_map_.end(); ++iter) { |
| if (iter->first->GetBoolean(prefs::kSafeBrowsingEnabled)) { |
| enable = true; |
| break; |
| } |
| } |
| |
| if (enable) |
| Start(); |
| else |
| Stop(false); |
| |
| #if defined(FULL_SAFE_BROWSING) |
| if (csd_service_.get()) |
| csd_service_->SetEnabledAndRefreshState(enable); |
| if (download_service_.get()) { |
| download_service_->SetEnabled( |
| enable && !CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kDisableImprovedDownloadProtection)); |
| } |
| #endif |
| } |