diff options
author | noelutz@google.com <noelutz@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-09 19:04:51 +0000 |
---|---|---|
committer | noelutz@google.com <noelutz@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-09 19:04:51 +0000 |
commit | d4b7a5d6b150a86813fde4013ae83ba77448621e (patch) | |
tree | 9c293f6170bbc6a12a4e93a4439131a4a3504df1 | |
parent | 023424e03760080f4ae54c3643e80dce51490ab2 (diff) | |
download | chromium_src-d4b7a5d6b150a86813fde4013ae83ba77448621e.zip chromium_src-d4b7a5d6b150a86813fde4013ae83ba77448621e.tar.gz chromium_src-d4b7a5d6b150a86813fde4013ae83ba77448621e.tar.bz2 |
Add a new SafeBrowsing list which contains a whitelist for the client-side
phishing detection. This whitelist will only contain full-length hashes
and should be small (fewer than 1K entries). The whitelist is loaded in
memory and is looked up on every page load before we start the client-side
phishing detection. If the loaded URL is on the csd whitelist we won't start
client-side phishing detection for that URL.
BUG=
TEST=
Review URL: http://codereview.chromium.org/6592056
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@77494 0039d316-1c4b-4281-b951-d872f2087c98
11 files changed, 515 insertions, 120 deletions
diff --git a/chrome/browser/safe_browsing/safe_browsing_database.cc b/chrome/browser/safe_browsing/safe_browsing_database.cc index 00b16f7..5b9d9a9 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database.cc +++ b/chrome/browser/safe_browsing/safe_browsing_database.cc @@ -4,6 +4,8 @@ #include "chrome/browser/safe_browsing/safe_browsing_database.h" +#include <algorithm> + #include "base/file_util.h" #include "base/metrics/histogram.h" #include "base/metrics/stats_counters.h" @@ -22,6 +24,9 @@ namespace { const FilePath::CharType kBloomFilterFile[] = FILE_PATH_LITERAL(" Filter 2"); // Filename suffix for download store. const FilePath::CharType kDownloadDBFile[] = FILE_PATH_LITERAL(" Download"); +// Filename suffix for client-side phishing detection whitelist store. +const FilePath::CharType kCsdWhitelistDBFile[] = + FILE_PATH_LITERAL(" Csd Whitelist"); // Filename suffix for browse store. // TODO(lzheng): change to a better name when we change the file format. const FilePath::CharType kBrowseDBFile[] = FILE_PATH_LITERAL(" Bloom"); @@ -29,6 +34,16 @@ const FilePath::CharType kBrowseDBFile[] = FILE_PATH_LITERAL(" Bloom"); // The maximum staleness for a cached entry. const int kMaxStalenessMinutes = 45; +// Maximum number of entries we allow in the client-side phishing detection +// whitelist. If the whitelist on disk contains more entries then +// ContainsCsdWhitelistedUrl will always return true. +const size_t kMaxCsdWhitelistSize = 5000; + +// If the hash of this exact expression is on the csd whitelist then +// ContainsCsdWhitelistedUrl will always return true. +const char kCsdKillSwitchUrl[] = + "sb-ssl.google.com/safebrowsing/csd/killswitch"; + // To save space, the incoming |chunk_id| and |list_id| are combined // into an |encoded_chunk_id| for storage by shifting the |list_id| // into the low-order bits. These functions decode that information. @@ -59,13 +74,14 @@ void GetDownloadUrlPrefix(const GURL& url, SBPrefix* prefix) { *prefix = full_hash.prefix; } -// Generate the set of prefixes to check for |url|. +// Generate the set of full hashes to check for |url|. // TODO(shess): This function is almost the same as // |CompareFullHashes()| in safe_browsing_util.cc, except that code // does an early exit on match. Since match should be the infrequent // case (phishing or malware found), consider combining this function // with that one. -void BrowsePrefixesToCheck(const GURL& url, std::vector<SBPrefix>* prefixes) { +void BrowseFullHashesToCheck(const GURL& url, + std::vector<SBFullHash>* full_hashes) { std::vector<std::string> hosts; if (url.HostIsIPAddress()) { hosts.push_back(url.host()); @@ -81,7 +97,7 @@ void BrowsePrefixesToCheck(const GURL& url, std::vector<SBPrefix>* prefixes) { SBFullHash full_hash; base::SHA256HashString(hosts[i] + paths[j], &full_hash, sizeof(full_hash)); - prefixes->push_back(full_hash.prefix); + full_hashes->push_back(full_hash); } } } @@ -129,47 +145,55 @@ void GetCachedFullHashesForBrowse(const std::vector<SBPrefix>& prefix_hits, } } +// This function generates a chunk range string for |chunks|. It +// outputs one chunk range string per list and writes it to the +// |list_ranges| vector. We expect |list_ranges| to already be of the +// right size. E.g., if |chunks| contains chunks with two different +// list ids then |list_ranges| must contain two elements. void GetChunkRanges(const std::vector<int>& chunks, - std::string* list0, - std::string* list1) { - std::vector<int> chunks0; - std::vector<int> chunks1; - + std::vector<std::string>* list_ranges) { + DCHECK_GT(list_ranges->size(), 0U); + DCHECK_LE(list_ranges->size(), 2U); + std::vector<std::vector<int> > decoded_chunks(list_ranges->size()); for (std::vector<int>::const_iterator iter = chunks.begin(); iter != chunks.end(); ++iter) { int mod_list_id = GetListIdBit(*iter); - if (0 == mod_list_id) { - chunks0.push_back(DecodeChunkId(*iter)); - } else { - DCHECK_EQ(1, mod_list_id); - chunks1.push_back(DecodeChunkId(*iter)); - } + DCHECK_GE(mod_list_id, 0); + DCHECK_LT(static_cast<size_t>(mod_list_id), decoded_chunks.size()); + decoded_chunks[mod_list_id].push_back(DecodeChunkId(*iter)); + } + for (size_t i = 0; i < decoded_chunks.size(); ++i) { + ChunksToRangeString(decoded_chunks[i], &((*list_ranges)[i])); } - - ChunksToRangeString(chunks0, list0); - ChunksToRangeString(chunks1, list1); } // Helper function to create chunk range lists for Browse related // lists. -void UpdateChunkRanges(const std::vector<int>& add_chunks, - const std::vector<int>& sub_chunks, - const std::string& list_name0, - const std::string& list_name1, +void UpdateChunkRanges(SafeBrowsingStore* store, + const std::vector<std::string>& listnames, std::vector<SBListChunkRanges>* lists) { - DCHECK_EQ(safe_browsing_util::GetListId(list_name0) % 2, 0); - DCHECK_EQ(safe_browsing_util::GetListId(list_name1) % 2, 1); - DCHECK_NE(safe_browsing_util::GetListId(list_name0), - safe_browsing_util::INVALID); - DCHECK_NE(safe_browsing_util::GetListId(list_name1), - safe_browsing_util::INVALID); - - SBListChunkRanges chunkrange0(list_name0); - SBListChunkRanges chunkrange1(list_name1); - GetChunkRanges(add_chunks, &chunkrange0.adds, &chunkrange1.adds); - GetChunkRanges(sub_chunks, &chunkrange0.subs, &chunkrange1.subs); - lists->push_back(chunkrange0); - lists->push_back(chunkrange1); + DCHECK_GT(listnames.size(), 0U); + DCHECK_LE(listnames.size(), 2U); + std::vector<int> add_chunks; + std::vector<int> sub_chunks; + store->GetAddChunks(&add_chunks); + store->GetSubChunks(&sub_chunks); + + std::vector<std::string> adds(listnames.size()); + std::vector<std::string> subs(listnames.size()); + GetChunkRanges(add_chunks, &adds); + GetChunkRanges(sub_chunks, &subs); + + for (size_t i = 0; i < listnames.size(); ++i) { + const std::string& listname = listnames[i]; + DCHECK_EQ(safe_browsing_util::GetListId(listname) % 2, + static_cast<int>(i % 2)); + DCHECK_NE(safe_browsing_util::GetListId(listname), + safe_browsing_util::INVALID); + lists->push_back(SBListChunkRanges(listname)); + lists->back().adds.swap(adds[i]); + lists->back().subs.swap(subs[i]); + } } // Order |SBAddFullHash| on the prefix part. |SBAddPrefixLess()| from @@ -207,14 +231,12 @@ void RecordPrefixSetInfo(PrefixSetEvent event_type) { class SafeBrowsingDatabaseFactoryImpl : public SafeBrowsingDatabaseFactory { public: virtual SafeBrowsingDatabase* CreateSafeBrowsingDatabase( - bool enable_download_protection) { - if (enable_download_protection) { - // Create database with browse url store and download store. - return new SafeBrowsingDatabaseNew(new SafeBrowsingStoreFile, - new SafeBrowsingStoreFile); - } - // Create database with only browse url store. - return new SafeBrowsingDatabaseNew(new SafeBrowsingStoreFile, NULL); + bool enable_download_protection, + bool enable_client_side_whitelist) { + return new SafeBrowsingDatabaseNew( + new SafeBrowsingStoreFile, + enable_download_protection ? new SafeBrowsingStoreFile : NULL, + enable_client_side_whitelist ? new SafeBrowsingStoreFile : NULL); } SafeBrowsingDatabaseFactoryImpl() { } @@ -232,10 +254,12 @@ SafeBrowsingDatabaseFactory* SafeBrowsingDatabase::factory_ = NULL; // SafeBrowsingDatabaseNew to SafeBrowsingDatabase, and have Create() // callers just construct things directly. SafeBrowsingDatabase* SafeBrowsingDatabase::Create( - bool enable_download_protection) { + bool enable_download_protection, + bool enable_client_side_whitelist) { if (!factory_) factory_ = new SafeBrowsingDatabaseFactoryImpl(); - return factory_->CreateSafeBrowsingDatabase(enable_download_protection); + return factory_->CreateSafeBrowsingDatabase(enable_download_protection, + enable_client_side_whitelist); } SafeBrowsingDatabase::~SafeBrowsingDatabase() { @@ -259,6 +283,12 @@ FilePath SafeBrowsingDatabase::BloomFilterForFilename( return FilePath(db_filename.value() + kBloomFilterFile); } +// static +FilePath SafeBrowsingDatabase::CsdWhitelistDBFilename( + const FilePath& db_filename) { + return FilePath(db_filename.value() + kCsdWhitelistDBFile); +} + SafeBrowsingStore* SafeBrowsingDatabaseNew::GetStore(const int list_id) { DVLOG(3) << "Get store for list: " << list_id; if (list_id == safe_browsing_util::PHISH || @@ -267,6 +297,8 @@ SafeBrowsingStore* SafeBrowsingDatabaseNew::GetStore(const int list_id) { } else if (list_id == safe_browsing_util::BINURL || list_id == safe_browsing_util::BINHASH) { return download_store_.get(); + } else if (list_id == safe_browsing_util::CSDWHITELIST) { + return csd_whitelist_store_.get(); } return NULL; } @@ -281,16 +313,21 @@ SafeBrowsingDatabaseNew::SafeBrowsingDatabaseNew() : creation_loop_(MessageLoop::current()), browse_store_(new SafeBrowsingStoreFile), download_store_(NULL), + csd_whitelist_store_(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(reset_factory_(this)) { DCHECK(browse_store_.get()); DCHECK(!download_store_.get()); + DCHECK(!csd_whitelist_store_.get()); } SafeBrowsingDatabaseNew::SafeBrowsingDatabaseNew( - SafeBrowsingStore* browse_store, SafeBrowsingStore* download_store) + SafeBrowsingStore* browse_store, + SafeBrowsingStore* download_store, + SafeBrowsingStore* csd_whitelist_store) : creation_loop_(MessageLoop::current()), browse_store_(browse_store), download_store_(download_store), + csd_whitelist_store_(csd_whitelist_store), ALLOW_THIS_IN_INITIALIZER_LIST(reset_factory_(this)), corruption_detected_(false) { DCHECK(browse_store_.get()); @@ -302,28 +339,30 @@ SafeBrowsingDatabaseNew::~SafeBrowsingDatabaseNew() { void SafeBrowsingDatabaseNew::Init(const FilePath& filename_base) { DCHECK_EQ(creation_loop_, MessageLoop::current()); - - // NOTE: There is no need to grab the lock in this function, since - // until it returns, there are no pointers to this class on other - // threads. Then again, that means there is no possibility of - // contention on the lock... - base::AutoLock locked(lookup_lock_); - - DCHECK(browse_filename_.empty()); // Ensure we haven't been run before. - DCHECK(download_filename_.empty()); // Ensure we haven't been run before. + // Ensure we haven't been run before. + DCHECK(browse_filename_.empty()); + DCHECK(download_filename_.empty()); + DCHECK(csd_whitelist_filename_.empty()); browse_filename_ = BrowseDBFilename(filename_base); + bloom_filter_filename_ = BloomFilterForFilename(browse_filename_); + browse_store_->Init( browse_filename_, NewCallback(this, &SafeBrowsingDatabaseNew::HandleCorruptDatabase)); - - full_browse_hashes_.clear(); - pending_browse_hashes_.clear(); - - bloom_filter_filename_ = BloomFilterForFilename(browse_filename_); - LoadBloomFilter(); DVLOG(1) << "Init browse store: " << browse_filename_.value(); + { + // NOTE: There is no need to grab the lock in this function, since + // until it returns, there are no pointers to this class on other + // threads. Then again, that means there is no possibility of + // contention on the lock... + base::AutoLock locked(lookup_lock_); + full_browse_hashes_.clear(); + pending_browse_hashes_.clear(); + LoadBloomFilter(); + } + if (download_store_.get()) { download_filename_ = DownloadDBFilename(filename_base); download_store_->Init( @@ -331,6 +370,22 @@ void SafeBrowsingDatabaseNew::Init(const FilePath& filename_base) { NewCallback(this, &SafeBrowsingDatabaseNew::HandleCorruptDatabase)); DVLOG(1) << "Init download store: " << download_filename_.value(); } + + if (csd_whitelist_store_.get()) { + csd_whitelist_filename_ = CsdWhitelistDBFilename(filename_base); + csd_whitelist_store_->Init( + csd_whitelist_filename_, + NewCallback(this, &SafeBrowsingDatabaseNew::HandleCorruptDatabase)); + DVLOG(1) << "Init csd whitelist store: " << csd_whitelist_filename_.value(); + std::vector<SBAddFullHash> full_hashes; + if (csd_whitelist_store_->GetAddFullHashes(&full_hashes)) { + LoadCsdWhitelist(full_hashes); + } else { + CsdWhitelistAllUrls(); + } + } else { + CsdWhitelistAllUrls(); // Just to be safe. + } } bool SafeBrowsingDatabaseNew::ResetDatabase() { @@ -354,6 +409,7 @@ bool SafeBrowsingDatabaseNew::ResetDatabase() { // TODO(shess): It is simpler for the code to assume that presence // of a bloom filter always implies presence of a prefix set. prefix_set_.reset(new safe_browsing::PrefixSet(std::vector<SBPrefix>())); + CsdWhitelistAllUrls(); } return true; @@ -371,9 +427,9 @@ bool SafeBrowsingDatabaseNew::ContainsBrowseUrl( prefix_hits->clear(); full_hits->clear(); - std::vector<SBPrefix> prefixes; - BrowsePrefixesToCheck(url, &prefixes); - if (prefixes.empty()) + std::vector<SBFullHash> full_hashes; + BrowseFullHashesToCheck(url, &full_hashes); + if (full_hashes.empty()) return false; // This function is called on the I/O thread, prevent changes to @@ -388,15 +444,15 @@ bool SafeBrowsingDatabaseNew::ContainsBrowseUrl( std::vector<SBPrefix> restored; size_t miss_count = 0; - for (size_t i = 0; i < prefixes.size(); ++i) { - bool found = prefix_set_->Exists(prefixes[i]); + for (size_t i = 0; i < full_hashes.size(); ++i) { + bool found = prefix_set_->Exists(full_hashes[i].prefix); - if (browse_bloom_filter_->Exists(prefixes[i])) { + if (browse_bloom_filter_->Exists(full_hashes[i].prefix)) { RecordPrefixSetInfo(PREFIX_SET_EVENT_BLOOM_HIT); if (found) RecordPrefixSetInfo(PREFIX_SET_EVENT_HIT); - prefix_hits->push_back(prefixes[i]); - if (prefix_miss_cache_.count(prefixes[i]) > 0) + prefix_hits->push_back(full_hashes[i].prefix); + if (prefix_miss_cache_.count(full_hashes[i].prefix) > 0) ++miss_count; } else { // Bloom filter misses should never be in prefix set. Re-create @@ -413,7 +469,8 @@ bool SafeBrowsingDatabaseNew::ContainsBrowseUrl( // If the item is not in the re-created list, then there is an // error in |PrefixSet::Exists()|. If the item is in the // re-created list, then the bloom filter was wrong. - if (std::binary_search(restored.begin(), restored.end(), prefixes[i])) { + if (std::binary_search(restored.begin(), restored.end(), + full_hashes[i].prefix)) { RecordPrefixSetInfo(PREFIX_SET_EVENT_BLOOM_MISS_PREFIX_HIT); } else { RecordPrefixSetInfo(PREFIX_SET_EVENT_BLOOM_MISS_PREFIX_HIT_INVALID); @@ -481,6 +538,24 @@ bool SafeBrowsingDatabaseNew::ContainsDownloadHashPrefix( &prefix_hit); } +bool SafeBrowsingDatabaseNew::ContainsCsdWhitelistedUrl(const GURL& url) { + // This method is theoretically thread-safe but we expect all calls to + // originate from the creation thread. + DCHECK_EQ(creation_loop_, MessageLoop::current()); + base::AutoLock l(lookup_lock_); + if (csd_whitelist_all_urls_) + return true; + + std::vector<SBFullHash> full_hashes; + BrowseFullHashesToCheck(url, &full_hashes); + for (std::vector<SBFullHash>::const_iterator it = full_hashes.begin(); + it != full_hashes.end(); ++it) { + if (std::binary_search(csd_whitelist_.begin(), csd_whitelist_.end(), *it)) + return true; + } + return false; +} + // Helper to insert entries for all of the prefixes or full hashes in // |entry| into the store. void SafeBrowsingDatabaseNew::InsertAdd(int chunk_id, SBPrefix host, @@ -732,24 +807,29 @@ bool SafeBrowsingDatabaseNew::UpdateStarted( return false; } - std::vector<int> browse_add_chunks; - browse_store_->GetAddChunks(&browse_add_chunks); - std::vector<int> browse_sub_chunks; - browse_store_->GetSubChunks(&browse_sub_chunks); - UpdateChunkRanges(browse_add_chunks, browse_sub_chunks, - safe_browsing_util::kMalwareList, - safe_browsing_util::kPhishingList, - lists); + if (csd_whitelist_store_.get() && !csd_whitelist_store_->BeginUpdate()) { + RecordFailure(FAILURE_CSD_WHITELIST_DATABASE_UPDATE_BEGIN); + HandleCorruptDatabase(); + return false; + } + + std::vector<std::string> browse_listnames; + browse_listnames.push_back(safe_browsing_util::kMalwareList); + browse_listnames.push_back(safe_browsing_util::kPhishingList); + UpdateChunkRanges(browse_store_.get(), browse_listnames, lists); if (download_store_.get()) { - std::vector<int> download_add_chunks; - download_store_->GetAddChunks(&download_add_chunks); - std::vector<int> download_sub_chunks; - download_store_->GetSubChunks(&download_sub_chunks); - UpdateChunkRanges(download_add_chunks, download_sub_chunks, - safe_browsing_util::kBinUrlList, - safe_browsing_util::kBinHashList, - lists); + std::vector<std::string> download_listnames; + download_listnames.push_back(safe_browsing_util::kBinUrlList); + download_listnames.push_back(safe_browsing_util::kBinHashList); + UpdateChunkRanges(download_store_.get(), download_listnames, lists); + } + + if (csd_whitelist_store_.get()) { + std::vector<std::string> csd_whitelist_listnames; + csd_whitelist_listnames.push_back(safe_browsing_util::kCsdWhiteList); + UpdateChunkRanges(csd_whitelist_store_.get(), + csd_whitelist_listnames, lists); } corruption_detected_ = false; @@ -772,6 +852,8 @@ void SafeBrowsingDatabaseNew::UpdateFinished(bool update_succeeded) { browse_store_->CancelUpdate(); if (download_store_.get()) download_store_->CancelUpdate(); + if (csd_whitelist_store_.get()) + csd_whitelist_store_->CancelUpdate(); return; } @@ -779,9 +861,37 @@ void SafeBrowsingDatabaseNew::UpdateFinished(bool update_succeeded) { UpdateDownloadStore(); // for browsing UpdateBrowseStore(); + // for csd whitelist + UpdateCsdWhitelistStore(); +} + +void SafeBrowsingDatabaseNew::UpdateCsdWhitelistStore() { + if (!csd_whitelist_store_.get()) + return; + + // For the csd whitelist, we don't cache and save full hashes since all + // hashes are already full. + std::vector<SBAddFullHash> empty_add_hashes; + + // Not needed for the csd whitelist. + std::set<SBPrefix> empty_miss_cache; + + // Note: prefixes will not be empty. The current data store implementation + // stores all full-length hashes as both full and prefix hashes. + std::vector<SBAddPrefix> prefixes; + std::vector<SBAddFullHash> full_hashes; + if (!csd_whitelist_store_->FinishUpdate(empty_add_hashes, + empty_miss_cache, + &prefixes, + &full_hashes)) { + RecordFailure(FAILURE_CSD_WHITELIST_DATABASE_UPDATE_FINISH); + CsdWhitelistAllUrls(); + return; + } + LoadCsdWhitelist(full_hashes); } -void SafeBrowsingDatabaseNew:: UpdateDownloadStore() { +void SafeBrowsingDatabaseNew::UpdateDownloadStore() { if (!download_store_.get()) return; @@ -1010,10 +1120,15 @@ bool SafeBrowsingDatabaseNew::Delete() { if (!r2) RecordFailure(FAILURE_DATABASE_STORE_DELETE); - const bool r3 = file_util::Delete(bloom_filter_filename_, false); + const bool r3 = csd_whitelist_store_.get() ? + csd_whitelist_store_->Delete() : true; if (!r3) + RecordFailure(FAILURE_DATABASE_STORE_DELETE); + + const bool r4 = file_util::Delete(bloom_filter_filename_, false); + if (!r4) RecordFailure(FAILURE_DATABASE_FILTER_DELETE); - return r1 && r2 && r3; + return r1 && r2 && r3 && r4; } void SafeBrowsingDatabaseNew::WriteBloomFilter() { @@ -1030,3 +1145,37 @@ void SafeBrowsingDatabaseNew::WriteBloomFilter() { if (!write_ok) RecordFailure(FAILURE_DATABASE_FILTER_WRITE); } + +void SafeBrowsingDatabaseNew::CsdWhitelistAllUrls() { + base::AutoLock locked(lookup_lock_); + csd_whitelist_all_urls_ = true; + csd_whitelist_.clear(); +} + +void SafeBrowsingDatabaseNew::LoadCsdWhitelist( + const std::vector<SBAddFullHash>& full_hashes) { + DCHECK_EQ(creation_loop_, MessageLoop::current()); + if (full_hashes.size() > kMaxCsdWhitelistSize) { + CsdWhitelistAllUrls(); + return; + } + + std::vector<SBFullHash> new_csd_whitelist; + for (std::vector<SBAddFullHash>::const_iterator it = full_hashes.begin(); + it != full_hashes.end(); ++it) { + new_csd_whitelist.push_back(it->full_hash); + } + std::sort(new_csd_whitelist.begin(), new_csd_whitelist.end()); + + SBFullHash kill_switch; + base::SHA256HashString(kCsdKillSwitchUrl, &kill_switch, sizeof(kill_switch)); + if (std::binary_search(new_csd_whitelist.begin(), new_csd_whitelist.end(), + kill_switch)) { + // The kill switch is whitelisted hence we whitelist all URLs. + CsdWhitelistAllUrls(); + } else { + base::AutoLock locked(lookup_lock_); + csd_whitelist_all_urls_ = false; + csd_whitelist_.swap(new_csd_whitelist); + } +} diff --git a/chrome/browser/safe_browsing/safe_browsing_database.h b/chrome/browser/safe_browsing/safe_browsing_database.h index b1712f0..31abb99 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database.h +++ b/chrome/browser/safe_browsing/safe_browsing_database.h @@ -36,28 +36,34 @@ class SafeBrowsingDatabaseFactory { SafeBrowsingDatabaseFactory() { } virtual ~SafeBrowsingDatabaseFactory() { } virtual SafeBrowsingDatabase* CreateSafeBrowsingDatabase( - bool enable_download_protection) = 0; + bool enable_download_protection, + bool enable_client_side_whitelist) = 0; private: DISALLOW_COPY_AND_ASSIGN(SafeBrowsingDatabaseFactory); }; - -// Encapsulates on-disk databases that for safebrowsing. There are two -// databases: browse database and download database. The browse database -// contains information about phishing and malware urls. The download -// database contains URLs for bad binaries (e.g: those containing virus) -// and hash of these downloaded contents. These on-disk databases are shared -// among all profiles, as it doesn't contain user-specific data. This object -// is not thread-safe, i.e. all its methods should be used on the same thread -// that it was created on. +// Encapsulates on-disk databases that for safebrowsing. There are +// three databases: browse, download and client-side detection (csd) +// whitelist databases. The browse database contains information +// about phishing and malware urls. The download database contains +// URLs for bad binaries (e.g: those containing virus) and hash of +// these downloaded contents. The csd whitelist database contains URLs +// that will never be considered as phishing by the client-side +// phishing detection. These on-disk databases are shared among all +// profiles, as it doesn't contain user-specific data. This object is +// not thread-safe, i.e. all its methods should be used on the same +// thread that it was created on. class SafeBrowsingDatabase { public: // Factory method for obtaining a SafeBrowsingDatabase implementation. // It is not thread safe. // |enable_download_protection| is used to control the download database // feature. - static SafeBrowsingDatabase* Create(bool enable_download_protection); + // |enable_client_side_whitelist| is used to control the csd whitelist + // database feature. + static SafeBrowsingDatabase* Create(bool enable_download_protection, + bool enable_client_side_whitelist); // Makes the passed |factory| the factory used to instantiate // a SafeBrowsingDatabase. This is used for tests. @@ -94,6 +100,12 @@ class SafeBrowsingDatabase { // This function could ONLY be accessed from creation thread. virtual bool ContainsDownloadHashPrefix(const SBPrefix& prefix) = 0; + // Returns false if |url| is not on the client-side phishing detection + // whitelist. Otherwise, this function returns true. Note: the whitelist + // only contains full-length hashes so we don't return any prefix hit. + // This function could ONLY be accessed from creation thread. + virtual bool ContainsCsdWhitelistedUrl(const GURL& url) = 0; + // A database transaction should look like: // // std::vector<SBListChunkRanges> lists; @@ -138,6 +150,10 @@ class SafeBrowsingDatabase { // Filename for download URL and download binary hash database. static FilePath DownloadDBFilename(const FilePath& db_base_filename); + // Filename for client-side phishing detection whitelist databsae. + static FilePath CsdWhitelistDBFilename( + const FilePath& csd_whitelist_base_filename); + // Enumerate failures for histogramming purposes. DO NOT CHANGE THE // ORDERING OF THESE VALUES. enum FailureType { @@ -153,6 +169,8 @@ class SafeBrowsingDatabase { FAILURE_DATABASE_STORE_DELETE, FAILURE_DOWNLOAD_DATABASE_UPDATE_BEGIN, FAILURE_DOWNLOAD_DATABASE_UPDATE_FINISH, + FAILURE_CSD_WHITELIST_DATABASE_UPDATE_BEGIN, + FAILURE_CSD_WHITELIST_DATABASE_UPDATE_FINISH, // Memory space for histograms is determined by the max. ALWAYS // ADD NEW VALUES BEFORE THIS ONE. @@ -170,12 +188,14 @@ class SafeBrowsingDatabase { class SafeBrowsingDatabaseNew : public SafeBrowsingDatabase { public: - // Create a database with a browse store and download store. Takes ownership - // of browse_store and download_store. When |download_store| is NULL, - // the database will ignore any operations related download (url hashes and - // binary hashes). + // Create a database with a browse store, download store and + // csd_whitelist_store. Takes ownership of browse_store, download_store and + // csd_whitelist_store. When |download_store| is NULL, the database + // will ignore any operations related download (url hashes and + // binary hashes). Same for the |csd_whitelist_store|. SafeBrowsingDatabaseNew(SafeBrowsingStore* browse_store, - SafeBrowsingStore* download_store); + SafeBrowsingStore* download_store, + SafeBrowsingStore* csd_whitelist_store); // Create a database with a browse store. This is a legacy interface that // useds Sqlite. @@ -194,6 +214,7 @@ class SafeBrowsingDatabaseNew : public SafeBrowsingDatabase { virtual bool ContainsDownloadUrl(const GURL& url, SBPrefix* prefix_hit); virtual bool ContainsDownloadHashPrefix(const SBPrefix& prefix); + virtual bool ContainsCsdWhitelistedUrl(const GURL& url); virtual bool UpdateStarted(std::vector<SBListChunkRanges>* lists); virtual void InsertChunks(const std::string& list_name, const SBChunkList& chunks); @@ -206,7 +227,8 @@ class SafeBrowsingDatabaseNew : public SafeBrowsingDatabase { friend class SafeBrowsingDatabaseTest; FRIEND_TEST(SafeBrowsingDatabaseTest, HashCaching); - // Return the browse_store_ or download_store_ based on list_id. + // Return the browse_store_, download_store_ or csd_whitelist_store_ + // based on list_id. SafeBrowsingStore* GetStore(int list_id); // Deletes the files on disk. @@ -218,6 +240,15 @@ class SafeBrowsingDatabaseNew : public SafeBrowsingDatabase { // Writes the current bloom filter to disk. void WriteBloomFilter(); + // Loads the given full-length hashes to the csd whitelist. If the number + // of hashes is too large or if the kill switch URL is on the whitelist + // we will whitelist all URLs. + void LoadCsdWhitelist(const std::vector<SBAddFullHash>& full_hashes); + + // Call this method if an error occured with the csd whitelist. This will + // result in all calls to ContainsCsdWhitelistedUrl() to returning true. + void CsdWhitelistAllUrls(); + // Helpers for handling database corruption. // |OnHandleCorruptDatabase()| runs |ResetDatabase()| and sets // |corruption_detected_|, |HandleCorruptDatabase()| posts @@ -236,6 +267,7 @@ class SafeBrowsingDatabaseNew : public SafeBrowsingDatabase { void UpdateDownloadStore(); void UpdateBrowseStore(); + void UpdateCsdWhitelistStore(); // Helper function to compare addprefixes in download_store_ with prefix. // The |list_bit| indicates which list (download url or download hash) @@ -252,7 +284,8 @@ class SafeBrowsingDatabaseNew : public SafeBrowsingDatabase { // Lock for protecting access to variables that may be used on the // IO thread. This includes |browse_bloom_filter_|, |full_browse_hashes_|, - // |pending_browse_hashes_|, and |prefix_miss_cache_|. + // |pending_browse_hashes_|, |prefix_miss_cache_|, |csd_whitelist_|, and + // |csd_whitelist_all_urls_|. base::Lock lookup_lock_; // Underlying persistent store for chunk data. @@ -264,6 +297,21 @@ class SafeBrowsingDatabaseNew : public SafeBrowsingDatabase { FilePath download_filename_; scoped_ptr<SafeBrowsingStore> download_store_; + // For the client-side phishing detection whitelist chunks and full-length + // hashes. This list only contains 256 bit hashes. + FilePath csd_whitelist_filename_; + scoped_ptr<SafeBrowsingStore> csd_whitelist_store_; + + // All the client-side phishing detection whitelist entries are loaded in + // a sorted vector. + std::vector<SBFullHash> csd_whitelist_; + + // If true, ContainsCsdWhitelistedUrl will always return true for all URLs. + // This is set to true if the csd whitelist is too large to be stored in + // memory, if the kill switch URL is on the csd whitelist or if there was + // an error during the most recent update. + bool csd_whitelist_all_urls_; + // Bloom filter generated from the add-prefixes in |browse_store_|. // Only browse_store_ requires the BloomFilter for fast query. FilePath bloom_filter_filename_; diff --git a/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc b/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc index a71a797..575f757 100644 --- a/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc +++ b/chrome/browser/safe_browsing/safe_browsing_database_unittest.cc @@ -62,6 +62,21 @@ void InsertAddChunkHostPrefixUrl(SBChunk* chunk, Sha256Prefix(url)); } +// Same as InsertAddChunkHostPrefixUrl, but with full hashes. +void InsertAddChunkHostFullHashes(SBChunk* chunk, + int chunk_number, + const std::string& host_name, + const std::string& url) { + chunk->chunk_number = chunk_number; + chunk->is_add = true; + SBChunkHost host; + host.host = Sha256Prefix(host_name); + host.entry = SBEntry::Create(SBEntry::ADD_FULL_HASH, 1); + host.entry->set_chunk_id(chunk->chunk_number); + host.entry->SetFullHashAt(0, Sha256Hash(url)); + chunk->hosts.push_back(host); +} + // Same as InsertAddChunkHostPrefixUrl, but with two urls for prefixes. void InsertAddChunkHost2PrefixUrls(SBChunk* chunk, int chunk_number, @@ -369,7 +384,10 @@ TEST_F(SafeBrowsingDatabaseTest, ListNameForBrowseAndDownload) { MessageLoop loop(MessageLoop::TYPE_DEFAULT); SafeBrowsingStoreFile* browse_store = new SafeBrowsingStoreFile(); SafeBrowsingStoreFile* download_store = new SafeBrowsingStoreFile(); - database_.reset(new SafeBrowsingDatabaseNew(browse_store, download_store)); + SafeBrowsingStoreFile* csd_whitelist_store = new SafeBrowsingStoreFile(); + database_.reset(new SafeBrowsingDatabaseNew(browse_store, + download_store, + csd_whitelist_store)); database_->Init(database_filename_); SBChunkList chunks; @@ -404,10 +422,17 @@ TEST_F(SafeBrowsingDatabaseTest, ListNameForBrowseAndDownload) { chunks.push_back(chunk); database_->InsertChunks(safe_browsing_util::kBinHashList, chunks); + chunk.hosts.clear(); + InsertAddChunkHostFullHashes(&chunk, 5, "www.forwhitelist.com/", + "www.forwhitelist.com/a.html"); + chunks.clear(); + chunks.push_back(chunk); + database_->InsertChunks(safe_browsing_util::kCsdWhiteList, chunks); + database_->UpdateFinished(true); GetListsInfo(&lists); - EXPECT_EQ(4U, lists.size()); + EXPECT_EQ(5U, lists.size()); EXPECT_TRUE(lists[0].name == safe_browsing_util::kMalwareList); EXPECT_EQ(lists[0].adds, "1"); EXPECT_TRUE(lists[0].subs.empty()); @@ -420,6 +445,9 @@ TEST_F(SafeBrowsingDatabaseTest, ListNameForBrowseAndDownload) { EXPECT_TRUE(lists[3].name == safe_browsing_util::kBinHashList); EXPECT_EQ(lists[3].adds, "4"); EXPECT_TRUE(lists[3].subs.empty()); + EXPECT_TRUE(lists[4].name == safe_browsing_util::kCsdWhiteList); + EXPECT_EQ(lists[4].adds, "5"); + EXPECT_TRUE(lists[4].subs.empty()); database_.reset(); } @@ -1043,7 +1071,7 @@ TEST_F(SafeBrowsingDatabaseTest, DISABLED_FileCorruptionHandling) { database_.reset(); MessageLoop loop(MessageLoop::TYPE_DEFAULT); SafeBrowsingStoreFile* store = new SafeBrowsingStoreFile(); - database_.reset(new SafeBrowsingDatabaseNew(store, NULL)); + database_.reset(new SafeBrowsingDatabaseNew(store, NULL, NULL)); database_->Init(database_filename_); // This will cause an empty database to be created. @@ -1112,7 +1140,10 @@ TEST_F(SafeBrowsingDatabaseTest, ContainsDownloadUrl) { MessageLoop loop(MessageLoop::TYPE_DEFAULT); SafeBrowsingStoreFile* browse_store = new SafeBrowsingStoreFile(); SafeBrowsingStoreFile* download_store = new SafeBrowsingStoreFile(); - database_.reset(new SafeBrowsingDatabaseNew(browse_store, download_store)); + SafeBrowsingStoreFile* csd_whitelist_store = new SafeBrowsingStoreFile(); + database_.reset(new SafeBrowsingDatabaseNew(browse_store, + download_store, + csd_whitelist_store)); database_->Init(database_filename_); const char kEvil1Host[] = "www.evil1.com/"; @@ -1130,10 +1161,7 @@ TEST_F(SafeBrowsingDatabaseTest, ContainsDownloadUrl) { database_->InsertChunks(safe_browsing_util::kBinUrlList, chunks); database_->UpdateFinished(true); - const Time now = Time::Now(); SBPrefix prefix_hit; - std::string matching_list; - EXPECT_TRUE(database_->ContainsDownloadUrl( GURL(std::string("http://") + kEvil1Url1), &prefix_hit)); EXPECT_EQ(prefix_hit, Sha256Prefix(kEvil1Url1)); @@ -1155,6 +1183,99 @@ TEST_F(SafeBrowsingDatabaseTest, ContainsDownloadUrl) { database_.reset(); } +// Checks that the csd-whitelist is handled properly. +TEST_F(SafeBrowsingDatabaseTest, CsdWhitelist) { + database_.reset(); + MessageLoop loop(MessageLoop::TYPE_DEFAULT); + // If the whitelist is disabled everything should match the whitelist. + database_.reset(new SafeBrowsingDatabaseNew(new SafeBrowsingStoreFile(), + NULL, NULL)); + database_->Init(database_filename_); + EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("http://www.phishig.com/")))); + + SafeBrowsingStoreFile* browse_store = new SafeBrowsingStoreFile(); + SafeBrowsingStoreFile* csd_whitelist_store = new SafeBrowsingStoreFile(); + database_.reset(new SafeBrowsingDatabaseNew(browse_store, NULL, + csd_whitelist_store)); + database_->Init(database_filename_); + + const char kGood1Host[] = "www.good1.com/"; + const char kGood1Url1[] = "www.good1.com/a/b.html"; + const char kGood1Url2[] = "www.good1.com/b/"; + + SBChunkList chunks; + SBChunk chunk; + // Add a simple chunk with one hostkey for the csd whitelist. + InsertAddChunkHost2FullHashes(&chunk, 1, kGood1Host, + kGood1Url1, kGood1Url2); + chunks.push_back(chunk); + std::vector<SBListChunkRanges> lists; + EXPECT_TRUE(database_->UpdateStarted(&lists)); + database_->InsertChunks(safe_browsing_util::kCsdWhiteList, chunks); + database_->UpdateFinished(true); + + EXPECT_FALSE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("http://") + kGood1Host))); + + EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("http://") + kGood1Url1))); + EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("http://") + kGood1Url1 + "?a=b"))); + + EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("http://") + kGood1Url2))); + EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("http://") + kGood1Url2 + "/c.html"))); + + EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("https://") + kGood1Url2 + "/c.html"))); + + EXPECT_FALSE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("http://www.google.com/")))); + + // Test that the kill-switch works as intended. + chunks.clear(); + lists.clear(); + SBChunk chunk2; + InsertAddChunkHostFullHashes(&chunk2, 2, "sb-ssl.google.com/", + "sb-ssl.google.com/safebrowsing/csd/killswitch"); + chunks.push_back(chunk2); + + EXPECT_TRUE(database_->UpdateStarted(&lists)); + database_->InsertChunks(safe_browsing_util::kCsdWhiteList, chunks); + database_->UpdateFinished(true); + + EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("https://") + kGood1Url2 + "/c.html"))); + EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("http://www.google.com/")))); + EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("http://www.phishing_url.com/")))); + + // Remove the kill-switch and verify that we can recover. + chunks.clear(); + lists.clear(); + SBChunk sub_chunk; + InsertSubChunkHostFullHash(&sub_chunk, 1, 2, + "sb-ssl.google.com/", + "sb-ssl.google.com/safebrowsing/csd/killswitch"); + chunks.push_back(sub_chunk); + + EXPECT_TRUE(database_->UpdateStarted(&lists)); + database_->InsertChunks(safe_browsing_util::kCsdWhiteList, chunks); + database_->UpdateFinished(true); + + EXPECT_TRUE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("https://") + kGood1Url2 + "/c.html"))); + EXPECT_FALSE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("http://www.google.com/")))); + EXPECT_FALSE(database_->ContainsCsdWhitelistedUrl( + GURL(std::string("http://www.phishing_url.com/")))); + + database_.reset(); +} + // Test to make sure we could insert chunk list that // contains entries for the same host. TEST_F(SafeBrowsingDatabaseTest, SameHostEntriesOkay) { @@ -1262,7 +1383,9 @@ TEST_F(SafeBrowsingDatabaseTest, BinHashInsertLookup) { MessageLoop loop(MessageLoop::TYPE_DEFAULT); SafeBrowsingStoreFile* browse_store = new SafeBrowsingStoreFile(); SafeBrowsingStoreFile* download_store = new SafeBrowsingStoreFile(); - database_.reset(new SafeBrowsingDatabaseNew(browse_store, download_store)); + database_.reset(new SafeBrowsingDatabaseNew(browse_store, + download_store, + NULL)); database_->Init(database_filename_); SBChunkList chunks; diff --git a/chrome/browser/safe_browsing/safe_browsing_service.cc b/chrome/browser/safe_browsing/safe_browsing_service.cc index 5bbfb6f..c96930b9 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service.cc +++ b/chrome/browser/safe_browsing/safe_browsing_service.cc @@ -156,6 +156,7 @@ SafeBrowsingService::SafeBrowsingService() protocol_manager_(NULL), enabled_(false), enable_download_protection_(false), + enable_csd_whitelist_(false), update_in_progress_(false), database_update_in_progress_(false), closing_database_(false), @@ -238,6 +239,17 @@ bool SafeBrowsingService::CheckDownloadHash(const std::string& full_hash, return false; } +bool SafeBrowsingService::MatchCsdWhitelistUrl(const GURL& url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(enable_csd_whitelist_); + if (!enabled_ || !enable_csd_whitelist_ || !MakeDatabaseAvailable()) { + // There is something funky going on here. Just to be safe we return + // true in this case. + return true; + } + return database_->ContainsCsdWhitelistedUrl(url); +} + bool SafeBrowsingService::CheckBrowseUrl(const GURL& url, Client* client) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); @@ -526,6 +538,8 @@ void SafeBrowsingService::OnIOInitialize( CommandLine* cmdline = CommandLine::ForCurrentProcess(); enable_download_protection_ = !cmdline->HasSwitch(switches::kSbDisableDownloadProtection); + enable_csd_whitelist_ = + cmdline->HasSwitch(switches::kEnableClientSidePhishingDetection); MakeDatabaseAvailable(); @@ -658,7 +672,8 @@ SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() { const base::TimeTicks before = base::TimeTicks::Now(); SafeBrowsingDatabase* database = - SafeBrowsingDatabase::Create(enable_download_protection_); + SafeBrowsingDatabase::Create(enable_download_protection_, + enable_csd_whitelist_); database->Init(path); { diff --git a/chrome/browser/safe_browsing/safe_browsing_service.h b/chrome/browser/safe_browsing/safe_browsing_service.h index 2e4a596..a3703f3 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service.h +++ b/chrome/browser/safe_browsing/safe_browsing_service.h @@ -160,6 +160,13 @@ class SafeBrowsingService // Result will be passed to callback in |client|. virtual bool CheckDownloadHash(const std::string& full_hash, Client* client); + // Check if the |url| matches any of the full-length hashes from the + // client-side phishing detection whitelist. Returns true if there was a + // match and false otherwise. To make sure we are conservative we will return + // true if an error occurs. This method is expected to be called on the IO + // thread. + bool MatchCsdWhitelistUrl(const GURL& url); + // Called on the IO thread to cancel a pending check if the result is no // longer needed. void CancelCheck(Client* client); @@ -413,6 +420,10 @@ class SafeBrowsingService // so we allow this feature to be exersized. bool enable_download_protection_; + // Indicate if client-side phishing detection whitelist should be enabled + // or not. + bool enable_csd_whitelist_; + // The SafeBrowsing thread that runs database operations. // // Note: Functions that run on this thread should run synchronously and return diff --git a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc index eeb302d..f31cded 100644 --- a/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc +++ b/chrome/browser/safe_browsing/safe_browsing_service_browsertest.cc @@ -71,6 +71,9 @@ class TestSafeBrowsingDatabase : public SafeBrowsingDatabase { virtual bool ContainsDownloadHashPrefix(const SBPrefix& prefix) { return download_digest_prefix_.count(prefix) > 0; } + virtual bool ContainsCsdWhitelistedUrl(const GURL& url) { + return true; + } virtual bool UpdateStarted(std::vector<SBListChunkRanges>* lists) { ADD_FAILURE() << "Not implemented."; return false; @@ -145,7 +148,8 @@ class TestSafeBrowsingDatabaseFactory : public SafeBrowsingDatabaseFactory { virtual ~TestSafeBrowsingDatabaseFactory() {} virtual SafeBrowsingDatabase* CreateSafeBrowsingDatabase( - bool enable_download_protection) { + bool enable_download_protection, + bool enable_client_side_whitelist) { db_ = new TestSafeBrowsingDatabase(); return db_; } diff --git a/chrome/browser/safe_browsing/safe_browsing_store.h b/chrome/browser/safe_browsing/safe_browsing_store.h index b099d9f..07b6128 100644 --- a/chrome/browser/safe_browsing/safe_browsing_store.h +++ b/chrome/browser/safe_browsing/safe_browsing_store.h @@ -177,6 +177,10 @@ class SafeBrowsingStore { // Get all Add prefixes out from the store. virtual bool GetAddPrefixes(std::vector<SBAddPrefix>* add_prefixes) = 0; + // Get all add full-length hashes. + virtual bool GetAddFullHashes( + std::vector<SBAddFullHash>* add_full_hashes) = 0; + // Start an update. None of the following methods should be called // unless this returns true. If this returns true, the update // should be terminated by FinishUpdate() or CancelUpdate(). diff --git a/chrome/browser/safe_browsing/safe_browsing_store_file.cc b/chrome/browser/safe_browsing/safe_browsing_store_file.cc index fcdb4ce..bfb6062 100644 --- a/chrome/browser/safe_browsing/safe_browsing_store_file.cc +++ b/chrome/browser/safe_browsing/safe_browsing_store_file.cc @@ -306,6 +306,31 @@ bool SafeBrowsingStoreFile::GetAddPrefixes( return true; } +bool SafeBrowsingStoreFile::GetAddFullHashes( + std::vector<SBAddFullHash>* add_full_hashes) { + add_full_hashes->clear(); + + file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb")); + if (file.get() == NULL) return false; + + FileHeader header; + if (!ReadAndVerifyHeader(filename_, file.get(), &header, NULL)) + return OnCorruptDatabase(); + + size_t offset = + header.add_chunk_count * sizeof(int32) + + header.sub_chunk_count * sizeof(int32) + + header.add_prefix_count * sizeof(SBAddPrefix) + + header.sub_prefix_count * sizeof(SBSubPrefix); + if (!FileSkip(offset, file.get())) + return false; + + return ReadToVector(add_full_hashes, + header.add_hash_count, + file.get(), + NULL); +} + bool SafeBrowsingStoreFile::WriteAddHash(int32 chunk_id, base::Time receive_time, const SBFullHash& full_hash) { diff --git a/chrome/browser/safe_browsing/safe_browsing_store_file.h b/chrome/browser/safe_browsing/safe_browsing_store_file.h index 88e1b2f..72a05f7 100644 --- a/chrome/browser/safe_browsing/safe_browsing_store_file.h +++ b/chrome/browser/safe_browsing/safe_browsing_store_file.h @@ -115,8 +115,10 @@ class SafeBrowsingStoreFile : public SafeBrowsingStore { // Delete any on-disk files, including the permanent storage. virtual bool Delete(); - // Get all Add prefixes from the store. + // Get all add hash prefixes and full-length hashes, respectively, from + // the store. virtual bool GetAddPrefixes(std::vector<SBAddPrefix>* add_prefixes); + virtual bool GetAddFullHashes(std::vector<SBAddFullHash>* add_full_hashes); virtual bool BeginChunk(); @@ -161,6 +163,7 @@ class SafeBrowsingStoreFile : public SafeBrowsingStore { const std::set<SBPrefix>& prefix_misses, std::vector<SBAddPrefix>* add_prefixes_result, std::vector<SBAddFullHash>* add_full_hashes_result); + // Enumerate different format-change events for histogramming // purposes. DO NOT CHANGE THE ORDERING OF THESE VALUES. // TODO(shess): Remove this once the format change is complete. diff --git a/chrome/browser/safe_browsing/safe_browsing_util.cc b/chrome/browser/safe_browsing/safe_browsing_util.cc index 06a18ca..4eb6083 100644 --- a/chrome/browser/safe_browsing/safe_browsing_util.cc +++ b/chrome/browser/safe_browsing/safe_browsing_util.cc @@ -167,6 +167,7 @@ const char kMalwareList[] = "goog-malware-shavar"; const char kPhishingList[] = "goog-phish-shavar"; const char kBinUrlList[] = "goog-badbinurl-shavar"; const char kBinHashList[] = "goog-badbinhash-shavar"; +const char kCsdWhiteList[] = "goog-csdwhite-sha256"; // Keywords to identify a list type from listname. // TODO(lzheng): check if this can be replaced by full listnames. @@ -183,8 +184,10 @@ int GetListId(const std::string& name) { id = PHISH; } else if (name == safe_browsing_util::kBinUrlList) { id = BINURL; - } else if (name == safe_browsing_util::kBinHashList) { + } else if (name == safe_browsing_util::kBinHashList) { id = BINHASH; + } else if (name == safe_browsing_util::kCsdWhiteList) { + id = CSDWHITELIST; } else { id = INVALID; } @@ -205,6 +208,9 @@ bool GetListName(int list_id, std::string* list) { case BINHASH: *list = safe_browsing_util::kBinHashList; break; + case CSDWHITELIST: + *list = safe_browsing_util::kCsdWhiteList; + break; default: return false; } diff --git a/chrome/browser/safe_browsing/safe_browsing_util.h b/chrome/browser/safe_browsing/safe_browsing_util.h index 5b01b44..78e652e 100644 --- a/chrome/browser/safe_browsing/safe_browsing_util.h +++ b/chrome/browser/safe_browsing/safe_browsing_util.h @@ -36,8 +36,12 @@ union SBFullHash { SBPrefix prefix; }; -inline bool operator==(const SBFullHash& rhash, const SBFullHash& lhash) { - return memcmp(rhash.full_hash, lhash.full_hash, sizeof(SBFullHash)) == 0; +inline bool operator==(const SBFullHash& lhash, const SBFullHash& rhash) { + return memcmp(lhash.full_hash, rhash.full_hash, sizeof(SBFullHash)) == 0; +} + +inline bool operator<(const SBFullHash& lhash, const SBFullHash& rhash) { + return memcmp(lhash.full_hash, rhash.full_hash, sizeof(SBFullHash)) < 0; } // Container for information about a specific host in an add/sub chunk. @@ -260,6 +264,8 @@ extern const char kPhishingList[]; // Binary Download list names. extern const char kBinUrlList[]; extern const char kBinHashList[]; +// SafeBrowsing client-side detection whitelist list name. +extern const char kCsdWhiteList[]; enum ListType { INVALID = -1, @@ -267,6 +273,7 @@ enum ListType { PHISH = 1, BINURL = 2, BINHASH = 3, + CSDWHITELIST = 4, }; // Maps a list name to ListType. |