Index: chrome/browser/safe_browsing/database_manager.cc |
diff --git a/chrome/browser/safe_browsing/database_manager.cc b/chrome/browser/safe_browsing/database_manager.cc |
deleted file mode 100644 |
index 20565e811a05d382519448acde8f7a2eebb71c67..0000000000000000000000000000000000000000 |
--- a/chrome/browser/safe_browsing/database_manager.cc |
+++ /dev/null |
@@ -1,1114 +0,0 @@ |
-// 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/database_manager.h" |
- |
-#include <algorithm> |
- |
-#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/metrics/histogram_macros.h" |
-#include "base/stl_util.h" |
-#include "base/strings/string_util.h" |
-#include "chrome/browser/browser_process.h" |
-#include "chrome/browser/chrome_notification_types.h" |
-#include "chrome/browser/prerender/prerender_field_trial.h" |
-#include "chrome/browser/safe_browsing/client_side_detection_service.h" |
-#include "chrome/browser/safe_browsing/download_protection_service.h" |
-#include "chrome/browser/safe_browsing/malware_details.h" |
-#include "chrome/browser/safe_browsing/protocol_manager.h" |
-#include "chrome/browser/safe_browsing/safe_browsing_database.h" |
-#include "chrome/browser/safe_browsing/safe_browsing_service.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 "components/startup_metric_utils/startup_metric_utils.h" |
-#include "content/public/browser/browser_thread.h" |
-#include "content/public/browser/notification_service.h" |
-#include "url/url_constants.h" |
- |
-using content::BrowserThread; |
- |
-namespace { |
- |
-// Timeout for match checks, e.g. download URLs, hashes. |
-const int kCheckTimeoutMs = 10000; |
- |
-// Records disposition information about the check. |hit| should be |
-// |true| if there were any prefix hits in |full_hashes|. |
-void RecordGetHashCheckStatus( |
- bool hit, |
- safe_browsing_util::ListType check_type, |
- const std::vector<SBFullHashResult>& full_hashes) { |
- SafeBrowsingProtocolManager::ResultType result; |
- if (full_hashes.empty()) { |
- result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_EMPTY; |
- } else if (hit) { |
- result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_HIT; |
- } else { |
- result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_MISS; |
- } |
- bool is_download = check_type == safe_browsing_util::BINURL; |
- SafeBrowsingProtocolManager::RecordGetHashResult(is_download, result); |
-} |
- |
-bool IsExpectedThreat( |
- const SBThreatType threat_type, |
- const std::vector<SBThreatType>& expected_threats) { |
- return expected_threats.end() != std::find(expected_threats.begin(), |
- expected_threats.end(), |
- threat_type); |
-} |
- |
-// Return the severest list id from the results in |full_hashes| which matches |
-// |hash|, or INVALID if none match. |
-safe_browsing_util::ListType GetHashSeverestThreatListType( |
- const SBFullHash& hash, |
- const std::vector<SBFullHashResult>& full_hashes, |
- size_t* index) { |
- safe_browsing_util::ListType pending_threat = safe_browsing_util::INVALID; |
- for (size_t i = 0; i < full_hashes.size(); ++i) { |
- if (SBFullHashEqual(hash, full_hashes[i].hash)) { |
- const safe_browsing_util::ListType threat = |
- static_cast<safe_browsing_util::ListType>(full_hashes[i].list_id); |
- switch (threat) { |
- case safe_browsing_util::INVALID: |
- // |full_hashes| should never contain INVALID as a |list_id|. |
- NOTREACHED(); |
- break; |
- case safe_browsing_util::MALWARE: // Falls through. |
- case safe_browsing_util::PHISH: // Falls through. |
- case safe_browsing_util::BINURL: // Falls through. |
- case safe_browsing_util::CSDWHITELIST: // Falls through. |
- case safe_browsing_util::DOWNLOADWHITELIST: // Falls through. |
- case safe_browsing_util::INCLUSIONWHITELIST: // Falls through. |
- case safe_browsing_util::EXTENSIONBLACKLIST: // Falls through. |
- case safe_browsing_util::IPBLACKLIST: |
- if (index) |
- *index = i; |
- return threat; |
- case safe_browsing_util::UNWANTEDURL: |
- // UNWANTEDURL is considered less severe than other threats, keep |
- // looking. |
- pending_threat = threat; |
- if (index) |
- *index = i; |
- break; |
- } |
- } |
- } |
- return pending_threat; |
-} |
- |
-// Given a URL, compare all the possible host + path full hashes to the set of |
-// provided full hashes. Returns the list id of the severest matching result |
-// from |full_hashes|, or INVALID if none match. |
-safe_browsing_util::ListType GetUrlSeverestThreatListType( |
- const GURL& url, |
- const std::vector<SBFullHashResult>& full_hashes, |
- size_t* index) { |
- if (full_hashes.empty()) |
- return safe_browsing_util::INVALID; |
- |
- std::vector<std::string> patterns; |
- safe_browsing_util::GeneratePatternsToCheck(url, &patterns); |
- |
- safe_browsing_util::ListType pending_threat = safe_browsing_util::INVALID; |
- for (size_t i = 0; i < patterns.size(); ++i) { |
- safe_browsing_util::ListType threat = GetHashSeverestThreatListType( |
- SBFullHashForString(patterns[i]), full_hashes, index); |
- switch (threat) { |
- case safe_browsing_util::INVALID: |
- // Ignore patterns with no matching threat. |
- break; |
- case safe_browsing_util::MALWARE: // Falls through. |
- case safe_browsing_util::PHISH: // Falls through. |
- case safe_browsing_util::BINURL: // Falls through. |
- case safe_browsing_util::CSDWHITELIST: // Falls through. |
- case safe_browsing_util::DOWNLOADWHITELIST: // Falls through. |
- case safe_browsing_util::INCLUSIONWHITELIST: // Falls through. |
- case safe_browsing_util::EXTENSIONBLACKLIST: // Falls through. |
- case safe_browsing_util::IPBLACKLIST: |
- return threat; |
- case safe_browsing_util::UNWANTEDURL: |
- // UNWANTEDURL is considered less severe than other threats, keep |
- // looking. |
- pending_threat = threat; |
- break; |
- } |
- } |
- return pending_threat; |
-} |
- |
-SBThreatType GetThreatTypeFromListType(safe_browsing_util::ListType list_type) { |
- switch (list_type) { |
- case safe_browsing_util::PHISH: |
- return SB_THREAT_TYPE_URL_PHISHING; |
- case safe_browsing_util::MALWARE: |
- return SB_THREAT_TYPE_URL_MALWARE; |
- case safe_browsing_util::UNWANTEDURL: |
- return SB_THREAT_TYPE_URL_UNWANTED; |
- case safe_browsing_util::BINURL: |
- return SB_THREAT_TYPE_BINARY_MALWARE_URL; |
- case safe_browsing_util::EXTENSIONBLACKLIST: |
- return SB_THREAT_TYPE_EXTENSION; |
- default: |
- DVLOG(1) << "Unknown safe browsing list id " << list_type; |
- return SB_THREAT_TYPE_SAFE; |
- } |
-} |
- |
-} // namespace |
- |
-// static |
-SBThreatType SafeBrowsingDatabaseManager::GetHashSeverestThreatType( |
- const SBFullHash& hash, |
- const std::vector<SBFullHashResult>& full_hashes) { |
- return GetThreatTypeFromListType( |
- GetHashSeverestThreatListType(hash, full_hashes, NULL)); |
-} |
- |
-// static |
-SBThreatType SafeBrowsingDatabaseManager::GetUrlSeverestThreatType( |
- const GURL& url, |
- const std::vector<SBFullHashResult>& full_hashes, |
- size_t* index) { |
- return GetThreatTypeFromListType( |
- GetUrlSeverestThreatListType(url, full_hashes, index)); |
-} |
- |
-SafeBrowsingDatabaseManager::SafeBrowsingCheck::SafeBrowsingCheck( |
- const std::vector<GURL>& urls, |
- const std::vector<SBFullHash>& full_hashes, |
- Client* client, |
- safe_browsing_util::ListType check_type, |
- const std::vector<SBThreatType>& expected_threats) |
- : urls(urls), |
- url_results(urls.size(), SB_THREAT_TYPE_SAFE), |
- url_metadata(urls.size()), |
- full_hashes(full_hashes), |
- full_hash_results(full_hashes.size(), SB_THREAT_TYPE_SAFE), |
- client(client), |
- need_get_hash(false), |
- check_type(check_type), |
- expected_threats(expected_threats) { |
- DCHECK_EQ(urls.empty(), !full_hashes.empty()) |
- << "Exactly one of urls and full_hashes must be set"; |
-} |
- |
-SafeBrowsingDatabaseManager::SafeBrowsingCheck::~SafeBrowsingCheck() {} |
- |
-void SafeBrowsingDatabaseManager::Client::OnSafeBrowsingResult( |
- const SafeBrowsingCheck& check) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- |
- DCHECK_EQ(check.urls.size(), check.url_results.size()); |
- DCHECK_EQ(check.full_hashes.size(), check.full_hash_results.size()); |
- if (!check.urls.empty()) { |
- DCHECK(check.full_hashes.empty()); |
- switch (check.check_type) { |
- case safe_browsing_util::MALWARE: |
- case safe_browsing_util::PHISH: |
- case safe_browsing_util::UNWANTEDURL: |
- DCHECK_EQ(1u, check.urls.size()); |
- OnCheckBrowseUrlResult( |
- check.urls[0], check.url_results[0], check.url_metadata[0]); |
- break; |
- case safe_browsing_util::BINURL: |
- DCHECK_EQ(check.urls.size(), check.url_results.size()); |
- OnCheckDownloadUrlResult( |
- check.urls, |
- *std::max_element(check.url_results.begin(), |
- check.url_results.end())); |
- break; |
- default: |
- NOTREACHED(); |
- } |
- } else if (!check.full_hashes.empty()) { |
- switch (check.check_type) { |
- case safe_browsing_util::EXTENSIONBLACKLIST: { |
- std::set<std::string> unsafe_extension_ids; |
- for (size_t i = 0; i < check.full_hashes.size(); ++i) { |
- std::string extension_id = |
- safe_browsing_util::SBFullHashToString(check.full_hashes[i]); |
- if (check.full_hash_results[i] == SB_THREAT_TYPE_EXTENSION) |
- unsafe_extension_ids.insert(extension_id); |
- } |
- OnCheckExtensionsResult(unsafe_extension_ids); |
- break; |
- } |
- default: |
- NOTREACHED(); |
- } |
- } else { |
- NOTREACHED(); |
- } |
-} |
- |
-SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager( |
- const scoped_refptr<SafeBrowsingService>& service) |
- : sb_service_(service), |
- database_(NULL), |
- enabled_(false), |
- enable_download_protection_(false), |
- enable_csd_whitelist_(false), |
- enable_download_whitelist_(false), |
- enable_extension_blacklist_(false), |
- enable_ip_blacklist_(false), |
- enable_unwanted_software_blacklist_(false), |
- update_in_progress_(false), |
- database_update_in_progress_(false), |
- closing_database_(false), |
- check_timeout_(base::TimeDelta::FromMilliseconds(kCheckTimeoutMs)) { |
- DCHECK_CURRENTLY_ON(BrowserThread::UI); |
- DCHECK(sb_service_.get() != NULL); |
- |
- // Android only supports a subset of FULL_SAFE_BROWSING. |
- // TODO(shess): This shouldn't be OS-driven <http://crbug.com/394379> |
-#if !defined(OS_ANDROID) |
- base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); |
- enable_download_protection_ = |
- !cmdline->HasSwitch(switches::kSbDisableDownloadProtection); |
- |
- // We only download the csd-whitelist if client-side phishing detection is |
- // enabled. |
- enable_csd_whitelist_ = |
- !cmdline->HasSwitch(switches::kDisableClientSidePhishingDetection); |
- |
- // TODO(noelutz): remove this boolean variable since it should always be true |
- // if SafeBrowsing is enabled. Unfortunately, we have no test data for this |
- // list right now. This means that we need to be able to disable this list |
- // for the SafeBrowsing test to pass. |
- enable_download_whitelist_ = enable_csd_whitelist_; |
- |
- // TODO(kalman): there really shouldn't be a flag for this. |
- enable_extension_blacklist_ = |
- !cmdline->HasSwitch(switches::kSbDisableExtensionBlacklist); |
- |
- // The client-side IP blacklist feature is tightly integrated with client-side |
- // phishing protection for now. |
- enable_ip_blacklist_ = enable_csd_whitelist_; |
- |
- // The UwS blacklist feature is controlled by a flag for M40. |
- enable_unwanted_software_blacklist_ = |
- safe_browsing_util::GetUnwantedTrialGroup() > safe_browsing_util::UWS_OFF; |
-#endif |
-} |
- |
-SafeBrowsingDatabaseManager::~SafeBrowsingDatabaseManager() { |
- // The DCHECK is disabled due to crbug.com/438754. |
- // DCHECK_CURRENTLY_ON(BrowserThread::UI); |
- |
- // 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_); |
-} |
- |
-bool SafeBrowsingDatabaseManager::CanCheckUrl(const GURL& url) const { |
- return url.SchemeIs(url::kFtpScheme) || |
- url.SchemeIs(url::kHttpScheme) || |
- url.SchemeIs(url::kHttpsScheme); |
-} |
- |
-bool SafeBrowsingDatabaseManager::CheckDownloadUrl( |
- const std::vector<GURL>& url_chain, |
- Client* client) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- if (!enabled_ || !enable_download_protection_) |
- return true; |
- |
- // We need to check the database for url prefix, and later may fetch the url |
- // from the safebrowsing backends. These need to be asynchronous. |
- SafeBrowsingCheck* check = |
- new SafeBrowsingCheck(url_chain, |
- std::vector<SBFullHash>(), |
- client, |
- safe_browsing_util::BINURL, |
- std::vector<SBThreatType>(1, |
- SB_THREAT_TYPE_BINARY_MALWARE_URL)); |
- std::vector<SBPrefix> prefixes; |
- SafeBrowsingDatabase::GetDownloadUrlPrefixes(url_chain, &prefixes); |
- StartSafeBrowsingCheck( |
- check, |
- base::Bind(&SafeBrowsingDatabaseManager::CheckDownloadUrlOnSBThread, this, |
- prefixes)); |
- return false; |
-} |
- |
-bool SafeBrowsingDatabaseManager::CheckExtensionIDs( |
- const std::set<std::string>& extension_ids, |
- Client* client) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- |
- if (!enabled_ || !enable_extension_blacklist_) |
- return true; |
- |
- std::vector<SBFullHash> extension_id_hashes; |
- std::transform(extension_ids.begin(), extension_ids.end(), |
- std::back_inserter(extension_id_hashes), |
- safe_browsing_util::StringToSBFullHash); |
- std::vector<SBPrefix> prefixes; |
- for (const SBFullHash& hash : extension_id_hashes) |
- prefixes.push_back(hash.prefix); |
- |
- SafeBrowsingCheck* check = new SafeBrowsingCheck( |
- std::vector<GURL>(), |
- extension_id_hashes, |
- client, |
- safe_browsing_util::EXTENSIONBLACKLIST, |
- std::vector<SBThreatType>(1, SB_THREAT_TYPE_EXTENSION)); |
- StartSafeBrowsingCheck( |
- check, |
- base::Bind(&SafeBrowsingDatabaseManager::CheckExtensionIDsOnSBThread, |
- this, prefixes)); |
- return false; |
-} |
- |
-bool SafeBrowsingDatabaseManager::MatchMalwareIP( |
- const std::string& ip_address) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- if (!enabled_ || !enable_ip_blacklist_ || !MakeDatabaseAvailable()) { |
- return false; // Fail open. |
- } |
- return database_->ContainsMalwareIP(ip_address); |
-} |
- |
-bool SafeBrowsingDatabaseManager::MatchCsdWhitelistUrl(const GURL& url) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- if (!enabled_ || !enable_csd_whitelist_ || !MakeDatabaseAvailable()) { |
- // There is something funky going on here -- for example, perhaps the user |
- // has not restarted since enabling metrics reporting, so we haven't |
- // enabled the csd whitelist yet. Just to be safe we return true in this |
- // case. |
- return true; |
- } |
- return database_->ContainsCsdWhitelistedUrl(url); |
-} |
- |
-bool SafeBrowsingDatabaseManager::MatchDownloadWhitelistUrl(const GURL& url) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- if (!enabled_ || !enable_download_whitelist_ || !MakeDatabaseAvailable()) { |
- return true; |
- } |
- return database_->ContainsDownloadWhitelistedUrl(url); |
-} |
- |
-bool SafeBrowsingDatabaseManager::MatchDownloadWhitelistString( |
- const std::string& str) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- if (!enabled_ || !enable_download_whitelist_ || !MakeDatabaseAvailable()) { |
- return true; |
- } |
- return database_->ContainsDownloadWhitelistedString(str); |
-} |
- |
-bool SafeBrowsingDatabaseManager::MatchInclusionWhitelistUrl(const GURL& url) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- if (!enabled_ || !MakeDatabaseAvailable()) |
- return true; |
- return database_->ContainsInclusionWhitelistedUrl(url); |
-} |
- |
-bool SafeBrowsingDatabaseManager::IsMalwareKillSwitchOn() { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- if (!enabled_ || !MakeDatabaseAvailable()) { |
- return true; |
- } |
- return database_->IsMalwareIPMatchKillSwitchOn(); |
-} |
- |
-bool SafeBrowsingDatabaseManager::IsCsdWhitelistKillSwitchOn() { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- if (!enabled_ || !MakeDatabaseAvailable()) { |
- return true; |
- } |
- return database_->IsCsdWhitelistKillSwitchOn(); |
-} |
- |
-bool SafeBrowsingDatabaseManager::CheckBrowseUrl(const GURL& url, |
- Client* client) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- if (!enabled_) |
- return true; |
- |
- if (!CanCheckUrl(url)) |
- return true; |
- |
- std::vector<SBThreatType> expected_threats; |
- expected_threats.push_back(SB_THREAT_TYPE_URL_MALWARE); |
- expected_threats.push_back(SB_THREAT_TYPE_URL_PHISHING); |
- expected_threats.push_back(SB_THREAT_TYPE_URL_UNWANTED); |
- |
- const base::TimeTicks start = base::TimeTicks::Now(); |
- if (!MakeDatabaseAvailable()) { |
- QueuedCheck queued_check(safe_browsing_util::MALWARE, // or PHISH |
- client, |
- url, |
- expected_threats, |
- start); |
- queued_checks_.push_back(queued_check); |
- return false; |
- } |
- |
- // Cache hits should, in general, be the same for both (ignoring potential |
- // cache evictions in the second call for entries that were just about to be |
- // evicted in the first call). |
- // TODO(gab): Refactor SafeBrowsingDatabase to avoid depending on this here. |
- std::vector<SBFullHashResult> cache_hits; |
- |
- std::vector<SBPrefix> browse_prefix_hits; |
- bool browse_prefix_match = database_->ContainsBrowseUrl( |
- url, &browse_prefix_hits, &cache_hits); |
- |
- std::vector<SBPrefix> unwanted_prefix_hits; |
- std::vector<SBFullHashResult> unused_cache_hits; |
- bool unwanted_prefix_match = database_->ContainsUnwantedSoftwareUrl( |
- url, &unwanted_prefix_hits, &unused_cache_hits); |
- |
- // Merge the two pre-sorted prefix hits lists. |
- // TODO(gab): Refactor SafeBrowsingDatabase for it to return this merged list |
- // by default rather than building it here. |
- std::vector<SBPrefix> prefix_hits(browse_prefix_hits.size() + |
- unwanted_prefix_hits.size()); |
- std::merge(browse_prefix_hits.begin(), |
- browse_prefix_hits.end(), |
- unwanted_prefix_hits.begin(), |
- unwanted_prefix_hits.end(), |
- prefix_hits.begin()); |
- prefix_hits.erase(std::unique(prefix_hits.begin(), prefix_hits.end()), |
- prefix_hits.end()); |
- |
- UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::TimeTicks::Now() - start); |
- |
- if (!browse_prefix_match && !unwanted_prefix_match) |
- return true; // URL is okay. |
- |
- // Needs to be asynchronous, since we could be in the constructor of a |
- // ResourceDispatcherHost event handler which can't pause there. |
- // This check will ping the Safe Browsing servers and get all lists which it |
- // matches. These lists will then be filtered against the |expected_threats| |
- // and the result callback for MALWARE (which is the same as for PHISH and |
- // UNWANTEDURL) will eventually be invoked with the final decision. |
- SafeBrowsingCheck* check = new SafeBrowsingCheck(std::vector<GURL>(1, url), |
- std::vector<SBFullHash>(), |
- client, |
- safe_browsing_util::MALWARE, |
- expected_threats); |
- check->need_get_hash = cache_hits.empty(); |
- check->prefix_hits.swap(prefix_hits); |
- check->cache_hits.swap(cache_hits); |
- checks_.insert(check); |
- |
- BrowserThread::PostTask( |
- BrowserThread::IO, FROM_HERE, |
- base::Bind(&SafeBrowsingDatabaseManager::OnCheckDone, this, check)); |
- |
- return false; |
-} |
- |
-void SafeBrowsingDatabaseManager::CancelCheck(Client* client) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) { |
- // We can't delete matching checks here because the db thread has a copy of |
- // the pointer. Instead, we simply NULL out the client, and when the db |
- // thread calls us back, we'll clean up the check. |
- if ((*i)->client == client) |
- (*i)->client = NULL; |
- } |
- |
- // Scan the queued clients store. Clients may be here if they requested a URL |
- // check before the database has finished loading. |
- for (std::deque<QueuedCheck>::iterator it(queued_checks_.begin()); |
- it != queued_checks_.end(); ) { |
- // In this case it's safe to delete matches entirely since nothing has a |
- // pointer to them. |
- if (it->client == client) |
- it = queued_checks_.erase(it); |
- else |
- ++it; |
- } |
-} |
- |
-void SafeBrowsingDatabaseManager::HandleGetHashResults( |
- SafeBrowsingCheck* check, |
- const std::vector<SBFullHashResult>& full_hashes, |
- const base::TimeDelta& cache_lifetime) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- |
- if (!enabled_) |
- return; |
- |
- // If the service has been shut down, |check| should have been deleted. |
- DCHECK(checks_.find(check) != checks_.end()); |
- |
- // |start| is set before calling |GetFullHash()|, which should be |
- // the only path which gets to here. |
- DCHECK(!check->start.is_null()); |
- UMA_HISTOGRAM_LONG_TIMES("SB2.Network", |
- base::TimeTicks::Now() - check->start); |
- |
- std::vector<SBPrefix> prefixes = check->prefix_hits; |
- OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here. |
- |
- // Cache the GetHash results. |
- if (cache_lifetime != base::TimeDelta() && MakeDatabaseAvailable()) |
- database_->CacheHashResults(prefixes, full_hashes, cache_lifetime); |
-} |
- |
-void SafeBrowsingDatabaseManager::GetChunks(GetChunksCallback callback) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- DCHECK(enabled_); |
- DCHECK(!callback.is_null()); |
- safe_browsing_task_runner_->PostTask( |
- FROM_HERE, |
- base::Bind(&SafeBrowsingDatabaseManager::GetAllChunksFromDatabase, this, |
- callback)); |
-} |
- |
-void SafeBrowsingDatabaseManager::AddChunks( |
- const std::string& list, |
- scoped_ptr<ScopedVector<SBChunkData> > chunks, |
- AddChunksCallback callback) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- DCHECK(enabled_); |
- DCHECK(!callback.is_null()); |
- safe_browsing_task_runner_->PostTask( |
- FROM_HERE, base::Bind(&SafeBrowsingDatabaseManager::AddDatabaseChunks, |
- this, list, base::Passed(&chunks), callback)); |
-} |
- |
-void SafeBrowsingDatabaseManager::DeleteChunks( |
- scoped_ptr<std::vector<SBChunkDelete> > chunk_deletes) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- DCHECK(enabled_); |
- safe_browsing_task_runner_->PostTask( |
- FROM_HERE, base::Bind(&SafeBrowsingDatabaseManager::DeleteDatabaseChunks, |
- this, base::Passed(&chunk_deletes))); |
-} |
- |
-void SafeBrowsingDatabaseManager::UpdateStarted() { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- DCHECK(enabled_); |
- DCHECK(!update_in_progress_); |
- update_in_progress_ = true; |
-} |
- |
-void SafeBrowsingDatabaseManager::UpdateFinished(bool update_succeeded) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- DCHECK(enabled_); |
- if (update_in_progress_) { |
- update_in_progress_ = false; |
- safe_browsing_task_runner_->PostTask( |
- FROM_HERE, |
- base::Bind(&SafeBrowsingDatabaseManager::DatabaseUpdateFinished, this, |
- update_succeeded)); |
- } |
-} |
- |
-void SafeBrowsingDatabaseManager::ResetDatabase() { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- DCHECK(enabled_); |
- safe_browsing_task_runner_->PostTask( |
- FROM_HERE, |
- base::Bind(&SafeBrowsingDatabaseManager::OnResetDatabase, this)); |
-} |
- |
-void SafeBrowsingDatabaseManager::StartOnIOThread() { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- if (enabled_) |
- return; |
- |
- // Only get a new task runner if there isn't one already. If the service has |
- // previously been started and stopped, a task runner could already exist. |
- if (!safe_browsing_task_runner_) { |
- base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool(); |
- safe_browsing_task_runner_ = |
- pool->GetSequencedTaskRunnerWithShutdownBehavior( |
- pool->GetSequenceToken(), |
- base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); |
- } |
- |
- enabled_ = true; |
- |
- MakeDatabaseAvailable(); |
-} |
- |
-void SafeBrowsingDatabaseManager::StopOnIOThread(bool shutdown) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- |
- DoStopOnIOThread(); |
- if (shutdown) { |
- sb_service_ = NULL; |
- } |
-} |
- |
-void SafeBrowsingDatabaseManager::NotifyDatabaseUpdateFinished( |
- bool update_succeeded) { |
- DCHECK_CURRENTLY_ON(BrowserThread::UI); |
- content::NotificationService::current()->Notify( |
- chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE, |
- content::Source<SafeBrowsingDatabaseManager>(this), |
- content::Details<bool>(&update_succeeded)); |
-} |
- |
-SafeBrowsingDatabaseManager::QueuedCheck::QueuedCheck( |
- const safe_browsing_util::ListType check_type, |
- Client* client, |
- const GURL& url, |
- const std::vector<SBThreatType>& expected_threats, |
- const base::TimeTicks& start) |
- : check_type(check_type), |
- client(client), |
- url(url), |
- expected_threats(expected_threats), |
- start(start) { |
-} |
- |
-SafeBrowsingDatabaseManager::QueuedCheck::~QueuedCheck() { |
-} |
- |
-void SafeBrowsingDatabaseManager::DoStopOnIOThread() { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- |
- if (!enabled_) |
- return; |
- |
- enabled_ = false; |
- |
- // Delete queued checks, calling back any clients with 'SB_THREAT_TYPE_SAFE'. |
- while (!queued_checks_.empty()) { |
- QueuedCheck queued = queued_checks_.front(); |
- if (queued.client) { |
- SafeBrowsingCheck sb_check(std::vector<GURL>(1, queued.url), |
- std::vector<SBFullHash>(), |
- queued.client, |
- queued.check_type, |
- queued.expected_threats); |
- queued.client->OnSafeBrowsingResult(sb_check); |
- } |
- queued_checks_.pop_front(); |
- } |
- |
- // Close the database. Cases to avoid: |
- // * If |closing_database_| is true, continuing will queue up a second |
- // request, |closing_database_| will be reset after handling the first |
- // request, and if any functions on the db thread recreate the database, we |
- // could start using it on the IO thread and then have the second request |
- // handler delete it out from under us. |
- // * If |database_| is NULL, then either no creation request is in flight, in |
- // which case we don't need to do anything, or one is in flight, in which |
- // case the database will be recreated before our deletion request is |
- // handled, and could be used on the IO thread in that time period, leading |
- // to the same problem as above. |
- // Checking DatabaseAvailable() avoids both of these. |
- if (DatabaseAvailable()) { |
- closing_database_ = true; |
- safe_browsing_task_runner_->PostTask( |
- FROM_HERE, |
- base::Bind(&SafeBrowsingDatabaseManager::OnCloseDatabase, this)); |
- } |
- |
- // Delete pending checks, calling back any clients with 'SB_THREAT_TYPE_SAFE'. |
- // We have to do this after the db thread returns because methods on it can |
- // have copies of these pointers, so deleting them might lead to accessing |
- // garbage. |
- for (CurrentChecks::iterator it = checks_.begin(); |
- it != checks_.end(); ++it) { |
- SafeBrowsingCheck* check = *it; |
- if (check->client) |
- check->client->OnSafeBrowsingResult(*check); |
- } |
- STLDeleteElements(&checks_); |
- |
- gethash_requests_.clear(); |
-} |
- |
-bool SafeBrowsingDatabaseManager::DatabaseAvailable() const { |
- base::AutoLock lock(database_lock_); |
- return !closing_database_ && (database_ != NULL); |
-} |
- |
-bool SafeBrowsingDatabaseManager::MakeDatabaseAvailable() { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- DCHECK(enabled_); |
- if (DatabaseAvailable()) |
- return true; |
- safe_browsing_task_runner_->PostTask( |
- FROM_HERE, |
- base::Bind(base::IgnoreResult(&SafeBrowsingDatabaseManager::GetDatabase), |
- this)); |
- return false; |
-} |
- |
-SafeBrowsingDatabase* SafeBrowsingDatabaseManager::GetDatabase() { |
- DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread()); |
- |
- if (database_) |
- return database_; |
- startup_metric_utils::ScopedSlowStartupUMA |
- scoped_timer("Startup.SlowStartupSafeBrowsingGetDatabase"); |
- const base::TimeTicks before = base::TimeTicks::Now(); |
- |
- SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create( |
- safe_browsing_task_runner_, enable_download_protection_, |
- enable_csd_whitelist_, enable_download_whitelist_, |
- enable_extension_blacklist_, enable_ip_blacklist_, |
- enable_unwanted_software_blacklist_); |
- |
- database->Init(SafeBrowsingService::GetBaseFilename()); |
- { |
- // Acquiring the lock here guarantees correct ordering between the writes to |
- // the new database object above, and the setting of |database_| below. |
- base::AutoLock lock(database_lock_); |
- database_ = database; |
- } |
- |
- BrowserThread::PostTask( |
- BrowserThread::IO, FROM_HERE, |
- base::Bind(&SafeBrowsingDatabaseManager::DatabaseLoadComplete, this)); |
- |
- UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", base::TimeTicks::Now() - before); |
- return database_; |
-} |
- |
-void SafeBrowsingDatabaseManager::OnCheckDone(SafeBrowsingCheck* check) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- |
- if (!enabled_) |
- return; |
- |
- // If the service has been shut down, |check| should have been deleted. |
- DCHECK(checks_.find(check) != checks_.end()); |
- |
- if (check->client && check->need_get_hash) { |
- // We have a partial match so we need to query Google for the full hash. |
- // Clean up will happen in HandleGetHashResults. |
- |
- // See if we have a GetHash request already in progress for this particular |
- // prefix. If so, we just append ourselves to the list of interested parties |
- // when the results arrive. We only do this for checks involving one prefix, |
- // since that is the common case (multiple prefixes will issue the request |
- // as normal). |
- if (check->prefix_hits.size() == 1) { |
- SBPrefix prefix = check->prefix_hits[0]; |
- GetHashRequests::iterator it = gethash_requests_.find(prefix); |
- if (it != gethash_requests_.end()) { |
- // There's already a request in progress. |
- it->second.push_back(check); |
- return; |
- } |
- |
- // No request in progress, so we're the first for this prefix. |
- GetHashRequestors requestors; |
- requestors.push_back(check); |
- gethash_requests_[prefix] = requestors; |
- } |
- |
- // Reset the start time so that we can measure the network time without the |
- // database time. |
- check->start = base::TimeTicks::Now(); |
- // Note: If |this| is deleted or stopped, the protocol_manager will |
- // be destroyed as well - hence it's OK to do unretained in this case. |
- bool is_download = check->check_type == safe_browsing_util::BINURL; |
- sb_service_->protocol_manager()->GetFullHash( |
- check->prefix_hits, |
- base::Bind(&SafeBrowsingDatabaseManager::HandleGetHashResults, |
- base::Unretained(this), |
- check), |
- is_download); |
- } else { |
- // We may have cached results for previous GetHash queries. Since |
- // this data comes from cache, don't histogram hits. |
- HandleOneCheck(check, check->cache_hits); |
- } |
-} |
- |
-void SafeBrowsingDatabaseManager::GetAllChunksFromDatabase( |
- GetChunksCallback callback) { |
- DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread()); |
- |
- bool database_error = true; |
- std::vector<SBListChunkRanges> lists; |
- DCHECK(!database_update_in_progress_); |
- database_update_in_progress_ = true; |
- GetDatabase(); // This guarantees that |database_| is non-NULL. |
- if (database_->UpdateStarted(&lists)) { |
- database_error = false; |
- } else { |
- database_->UpdateFinished(false); |
- } |
- |
- BrowserThread::PostTask( |
- BrowserThread::IO, FROM_HERE, |
- base::Bind(&SafeBrowsingDatabaseManager::OnGetAllChunksFromDatabase, |
- this, lists, database_error, callback)); |
-} |
- |
-void SafeBrowsingDatabaseManager::OnGetAllChunksFromDatabase( |
- const std::vector<SBListChunkRanges>& lists, bool database_error, |
- GetChunksCallback callback) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- if (enabled_) |
- callback.Run(lists, database_error); |
-} |
- |
-void SafeBrowsingDatabaseManager::OnAddChunksComplete( |
- AddChunksCallback callback) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- if (enabled_) |
- callback.Run(); |
-} |
- |
-void SafeBrowsingDatabaseManager::DatabaseLoadComplete() { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- if (!enabled_) |
- return; |
- |
- LOCAL_HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size()); |
- if (queued_checks_.empty()) |
- return; |
- |
- // If the database isn't already available, calling CheckUrl() in the loop |
- // below will add the check back to the queue, and we'll infinite-loop. |
- DCHECK(DatabaseAvailable()); |
- while (!queued_checks_.empty()) { |
- QueuedCheck check = queued_checks_.front(); |
- DCHECK(!check.start.is_null()); |
- LOCAL_HISTOGRAM_TIMES("SB.QueueDelay", |
- base::TimeTicks::Now() - check.start); |
- // If CheckUrl() determines the URL is safe immediately, it doesn't call the |
- // client's handler function (because normally it's being directly called by |
- // the client). Since we're not the client, we have to convey this result. |
- if (check.client && CheckBrowseUrl(check.url, check.client)) { |
- SafeBrowsingCheck sb_check(std::vector<GURL>(1, check.url), |
- std::vector<SBFullHash>(), |
- check.client, |
- check.check_type, |
- check.expected_threats); |
- check.client->OnSafeBrowsingResult(sb_check); |
- } |
- queued_checks_.pop_front(); |
- } |
-} |
- |
-void SafeBrowsingDatabaseManager::AddDatabaseChunks( |
- const std::string& list_name, |
- scoped_ptr<ScopedVector<SBChunkData> > chunks, |
- AddChunksCallback callback) { |
- DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread()); |
- if (chunks) |
- GetDatabase()->InsertChunks(list_name, chunks->get()); |
- BrowserThread::PostTask( |
- BrowserThread::IO, FROM_HERE, |
- base::Bind(&SafeBrowsingDatabaseManager::OnAddChunksComplete, this, |
- callback)); |
-} |
- |
-void SafeBrowsingDatabaseManager::DeleteDatabaseChunks( |
- scoped_ptr<std::vector<SBChunkDelete> > chunk_deletes) { |
- DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread()); |
- if (chunk_deletes) |
- GetDatabase()->DeleteChunks(*chunk_deletes); |
-} |
- |
-void SafeBrowsingDatabaseManager::DatabaseUpdateFinished( |
- bool update_succeeded) { |
- DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread()); |
- GetDatabase()->UpdateFinished(update_succeeded); |
- DCHECK(database_update_in_progress_); |
- database_update_in_progress_ = false; |
- BrowserThread::PostTask( |
- BrowserThread::UI, FROM_HERE, |
- base::Bind(&SafeBrowsingDatabaseManager::NotifyDatabaseUpdateFinished, |
- this, update_succeeded)); |
-} |
- |
-void SafeBrowsingDatabaseManager::OnCloseDatabase() { |
- DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread()); |
- DCHECK(closing_database_); |
- |
- // Because |closing_database_| is true, nothing on the IO thread will be |
- // accessing the database, so it's safe to delete and then NULL the pointer. |
- delete database_; |
- database_ = NULL; |
- |
- // Acquiring the lock here guarantees correct ordering between the resetting |
- // of |database_| above and of |closing_database_| below, which ensures there |
- // won't be a window during which the IO thread falsely believes the database |
- // is available. |
- base::AutoLock lock(database_lock_); |
- closing_database_ = false; |
-} |
- |
-void SafeBrowsingDatabaseManager::OnResetDatabase() { |
- DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread()); |
- |
- GetDatabase()->ResetDatabase(); |
-} |
- |
-void SafeBrowsingDatabaseManager::OnHandleGetHashResults( |
- SafeBrowsingCheck* check, |
- const std::vector<SBFullHashResult>& full_hashes) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- safe_browsing_util::ListType check_type = check->check_type; |
- SBPrefix prefix = check->prefix_hits[0]; |
- GetHashRequests::iterator it = gethash_requests_.find(prefix); |
- if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) { |
- const bool hit = HandleOneCheck(check, full_hashes); |
- RecordGetHashCheckStatus(hit, check_type, full_hashes); |
- return; |
- } |
- |
- // Call back all interested parties, noting if any has a hit. |
- GetHashRequestors& requestors = it->second; |
- bool hit = false; |
- for (GetHashRequestors::iterator r = requestors.begin(); |
- r != requestors.end(); ++r) { |
- if (HandleOneCheck(*r, full_hashes)) |
- hit = true; |
- } |
- RecordGetHashCheckStatus(hit, check_type, full_hashes); |
- |
- gethash_requests_.erase(it); |
-} |
- |
-bool SafeBrowsingDatabaseManager::HandleOneCheck( |
- SafeBrowsingCheck* check, |
- const std::vector<SBFullHashResult>& full_hashes) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- DCHECK(check); |
- |
- bool is_threat = false; |
- |
- // TODO(shess): GetHashSeverestThreadListType() contains a loop, |
- // GetUrlSeverestThreatListType() a loop around that loop. Having another |
- // loop out here concerns me. It is likely that SAFE is an expected outcome, |
- // which means all of those loops run to completion. Refactoring this to |
- // generate a set of sorted items to compare in sequence would probably |
- // improve things. |
- // |
- // Additionally, the set of patterns generated from the urls is very similar |
- // to the patterns generated in ContainsBrowseUrl() and other database checks, |
- // which are called from this code. Refactoring that across the checks could |
- // interact well with batching the checks here. |
- |
- // TODO(gab): Fix the fact that Get(Url|Hash)SeverestThreatType() may return a |
- // threat for which IsExpectedThreat() returns false even if |full_hashes| |
- // actually contains an expected threat. |
- |
- for (size_t i = 0; i < check->urls.size(); ++i) { |
- size_t threat_index; |
- SBThreatType threat = |
- GetUrlSeverestThreatType(check->urls[i], full_hashes, &threat_index); |
- if (threat != SB_THREAT_TYPE_SAFE && |
- IsExpectedThreat(threat, check->expected_threats)) { |
- check->url_results[i] = threat; |
- check->url_metadata[i] = full_hashes[threat_index].metadata; |
- is_threat = true; |
- } |
- } |
- |
- for (size_t i = 0; i < check->full_hashes.size(); ++i) { |
- SBThreatType threat = |
- GetHashSeverestThreatType(check->full_hashes[i], full_hashes); |
- if (threat != SB_THREAT_TYPE_SAFE && |
- IsExpectedThreat(threat, check->expected_threats)) { |
- check->full_hash_results[i] = threat; |
- is_threat = true; |
- } |
- } |
- |
- SafeBrowsingCheckDone(check); |
- return is_threat; |
-} |
- |
-void SafeBrowsingDatabaseManager::OnAsyncCheckDone( |
- SafeBrowsingCheck* check, |
- const std::vector<SBPrefix>& prefix_hits) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- DCHECK(enable_download_protection_); |
- |
- check->prefix_hits = prefix_hits; |
- if (check->prefix_hits.empty()) { |
- SafeBrowsingCheckDone(check); |
- } else { |
- check->need_get_hash = true; |
- OnCheckDone(check); |
- } |
-} |
- |
-std::vector<SBPrefix> SafeBrowsingDatabaseManager::CheckDownloadUrlOnSBThread( |
- const std::vector<SBPrefix>& prefixes) { |
- DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread()); |
- DCHECK(enable_download_protection_); |
- |
- std::vector<SBPrefix> prefix_hits; |
- const bool result = |
- database_->ContainsDownloadUrlPrefixes(prefixes, &prefix_hits); |
- DCHECK_EQ(result, !prefix_hits.empty()); |
- return prefix_hits; |
-} |
- |
-std::vector<SBPrefix> SafeBrowsingDatabaseManager::CheckExtensionIDsOnSBThread( |
- const std::vector<SBPrefix>& prefixes) { |
- DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread()); |
- |
- std::vector<SBPrefix> prefix_hits; |
- const bool result = |
- database_->ContainsExtensionPrefixes(prefixes, &prefix_hits); |
- DCHECK_EQ(result, !prefix_hits.empty()); |
- return prefix_hits; |
-} |
- |
-void SafeBrowsingDatabaseManager::TimeoutCallback(SafeBrowsingCheck* check) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- DCHECK(check); |
- |
- if (!enabled_) |
- return; |
- |
- DCHECK(checks_.find(check) != checks_.end()); |
- if (check->client) { |
- check->client->OnSafeBrowsingResult(*check); |
- check->client = NULL; |
- } |
-} |
- |
-void SafeBrowsingDatabaseManager::SafeBrowsingCheckDone( |
- SafeBrowsingCheck* check) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- DCHECK(check); |
- |
- if (!enabled_) |
- return; |
- |
- DVLOG(1) << "SafeBrowsingCheckDone"; |
- DCHECK(checks_.find(check) != checks_.end()); |
- if (check->client) |
- check->client->OnSafeBrowsingResult(*check); |
- checks_.erase(check); |
- delete check; |
-} |
- |
-void SafeBrowsingDatabaseManager::StartSafeBrowsingCheck( |
- SafeBrowsingCheck* check, |
- const base::Callback<std::vector<SBPrefix>(void)>& task) { |
- DCHECK_CURRENTLY_ON(BrowserThread::IO); |
- check->weak_ptr_factory_.reset( |
- new base::WeakPtrFactory<SafeBrowsingDatabaseManager>(this)); |
- checks_.insert(check); |
- |
- base::PostTaskAndReplyWithResult( |
- safe_browsing_task_runner_.get(), FROM_HERE, task, |
- base::Bind(&SafeBrowsingDatabaseManager::OnAsyncCheckDone, |
- check->weak_ptr_factory_->GetWeakPtr(), check)); |
- base::MessageLoop::current()->PostDelayedTask(FROM_HERE, |
- base::Bind(&SafeBrowsingDatabaseManager::TimeoutCallback, |
- check->weak_ptr_factory_->GetWeakPtr(), check), |
- check_timeout_); |
-} |