[sql] Remove _HAS_EXCEPTIONS=0 from build info.
[chromium-blink-merge.git] / chrome / browser / safe_browsing / local_database_manager.cc
blob9ba2db4304c1ec1cecae80e4e0a7a90a3c91daa6
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/safe_browsing/local_database_manager.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/callback.h"
12 #include "base/command_line.h"
13 #include "base/debug/leak_tracker.h"
14 #include "base/location.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_util.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/chrome_notification_types.h"
22 #include "chrome/browser/prerender/prerender_field_trial.h"
23 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
24 #include "chrome/browser/safe_browsing/download_protection_service.h"
25 #include "chrome/browser/safe_browsing/malware_details.h"
26 #include "chrome/browser/safe_browsing/protocol_manager.h"
27 #include "chrome/browser/safe_browsing/safe_browsing_database.h"
28 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
29 #include "chrome/browser/safe_browsing/ui_manager.h"
30 #include "chrome/common/chrome_constants.h"
31 #include "chrome/common/chrome_paths.h"
32 #include "chrome/common/chrome_switches.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/notification_service.h"
35 #include "url/url_constants.h"
37 using content::BrowserThread;
39 namespace {
41 // Timeout for match checks, e.g. download URLs, hashes.
42 const int kCheckTimeoutMs = 10000;
44 // Records disposition information about the check. |hit| should be
45 // |true| if there were any prefix hits in |full_hashes|.
46 void RecordGetHashCheckStatus(
47 bool hit,
48 safe_browsing_util::ListType check_type,
49 const std::vector<SBFullHashResult>& full_hashes) {
50 SafeBrowsingProtocolManager::ResultType result;
51 if (full_hashes.empty()) {
52 result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_EMPTY;
53 } else if (hit) {
54 result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_HIT;
55 } else {
56 result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_MISS;
58 bool is_download = check_type == safe_browsing_util::BINURL;
59 SafeBrowsingProtocolManager::RecordGetHashResult(is_download, result);
62 bool IsExpectedThreat(
63 const SBThreatType threat_type,
64 const std::vector<SBThreatType>& expected_threats) {
65 return expected_threats.end() != std::find(expected_threats.begin(),
66 expected_threats.end(),
67 threat_type);
70 // Return the severest list id from the results in |full_hashes| which matches
71 // |hash|, or INVALID if none match.
72 safe_browsing_util::ListType GetHashSeverestThreatListType(
73 const SBFullHash& hash,
74 const std::vector<SBFullHashResult>& full_hashes,
75 size_t* index) {
76 safe_browsing_util::ListType pending_threat = safe_browsing_util::INVALID;
77 for (size_t i = 0; i < full_hashes.size(); ++i) {
78 if (SBFullHashEqual(hash, full_hashes[i].hash)) {
79 const safe_browsing_util::ListType threat =
80 static_cast<safe_browsing_util::ListType>(full_hashes[i].list_id);
81 switch (threat) {
82 case safe_browsing_util::INVALID:
83 // |full_hashes| should never contain INVALID as a |list_id|.
84 NOTREACHED();
85 break;
86 case safe_browsing_util::MALWARE: // Falls through.
87 case safe_browsing_util::PHISH: // Falls through.
88 case safe_browsing_util::BINURL: // Falls through.
89 case safe_browsing_util::CSDWHITELIST: // Falls through.
90 case safe_browsing_util::DOWNLOADWHITELIST: // Falls through.
91 case safe_browsing_util::INCLUSIONWHITELIST: // Falls through.
92 case safe_browsing_util::EXTENSIONBLACKLIST: // Falls through.
93 case safe_browsing_util::IPBLACKLIST:
94 if (index)
95 *index = i;
96 return threat;
97 case safe_browsing_util::UNWANTEDURL:
98 // UNWANTEDURL is considered less severe than other threats, keep
99 // looking.
100 pending_threat = threat;
101 if (index)
102 *index = i;
103 break;
107 return pending_threat;
110 // Given a URL, compare all the possible host + path full hashes to the set of
111 // provided full hashes. Returns the list id of the severest matching result
112 // from |full_hashes|, or INVALID if none match.
113 safe_browsing_util::ListType GetUrlSeverestThreatListType(
114 const GURL& url,
115 const std::vector<SBFullHashResult>& full_hashes,
116 size_t* index) {
117 if (full_hashes.empty())
118 return safe_browsing_util::INVALID;
120 std::vector<std::string> patterns;
121 safe_browsing_util::GeneratePatternsToCheck(url, &patterns);
123 safe_browsing_util::ListType pending_threat = safe_browsing_util::INVALID;
124 for (size_t i = 0; i < patterns.size(); ++i) {
125 safe_browsing_util::ListType threat = GetHashSeverestThreatListType(
126 SBFullHashForString(patterns[i]), full_hashes, index);
127 switch (threat) {
128 case safe_browsing_util::INVALID:
129 // Ignore patterns with no matching threat.
130 break;
131 case safe_browsing_util::MALWARE: // Falls through.
132 case safe_browsing_util::PHISH: // Falls through.
133 case safe_browsing_util::BINURL: // Falls through.
134 case safe_browsing_util::CSDWHITELIST: // Falls through.
135 case safe_browsing_util::DOWNLOADWHITELIST: // Falls through.
136 case safe_browsing_util::INCLUSIONWHITELIST: // Falls through.
137 case safe_browsing_util::EXTENSIONBLACKLIST: // Falls through.
138 case safe_browsing_util::IPBLACKLIST:
139 return threat;
140 case safe_browsing_util::UNWANTEDURL:
141 // UNWANTEDURL is considered less severe than other threats, keep
142 // looking.
143 pending_threat = threat;
144 break;
147 return pending_threat;
150 SBThreatType GetThreatTypeFromListType(safe_browsing_util::ListType list_type) {
151 switch (list_type) {
152 case safe_browsing_util::PHISH:
153 return SB_THREAT_TYPE_URL_PHISHING;
154 case safe_browsing_util::MALWARE:
155 return SB_THREAT_TYPE_URL_MALWARE;
156 case safe_browsing_util::UNWANTEDURL:
157 return SB_THREAT_TYPE_URL_UNWANTED;
158 case safe_browsing_util::BINURL:
159 return SB_THREAT_TYPE_BINARY_MALWARE_URL;
160 case safe_browsing_util::EXTENSIONBLACKLIST:
161 return SB_THREAT_TYPE_EXTENSION;
162 default:
163 DVLOG(1) << "Unknown safe browsing list id " << list_type;
164 return SB_THREAT_TYPE_SAFE;
168 } // namespace
170 // static
171 SBThreatType LocalSafeBrowsingDatabaseManager::GetHashSeverestThreatType(
172 const SBFullHash& hash,
173 const std::vector<SBFullHashResult>& full_hashes) {
174 return GetThreatTypeFromListType(
175 GetHashSeverestThreatListType(hash, full_hashes, NULL));
178 // static
179 SBThreatType LocalSafeBrowsingDatabaseManager::GetUrlSeverestThreatType(
180 const GURL& url,
181 const std::vector<SBFullHashResult>& full_hashes,
182 size_t* index) {
183 return GetThreatTypeFromListType(
184 GetUrlSeverestThreatListType(url, full_hashes, index));
187 LocalSafeBrowsingDatabaseManager::SafeBrowsingCheck::SafeBrowsingCheck(
188 const std::vector<GURL>& urls,
189 const std::vector<SBFullHash>& full_hashes,
190 Client* client,
191 safe_browsing_util::ListType check_type,
192 const std::vector<SBThreatType>& expected_threats)
193 : urls(urls),
194 url_results(urls.size(), SB_THREAT_TYPE_SAFE),
195 url_metadata(urls.size()),
196 full_hashes(full_hashes),
197 full_hash_results(full_hashes.size(), SB_THREAT_TYPE_SAFE),
198 client(client),
199 need_get_hash(false),
200 check_type(check_type),
201 expected_threats(expected_threats) {
202 DCHECK_EQ(urls.empty(), !full_hashes.empty())
203 << "Exactly one of urls and full_hashes must be set";
206 LocalSafeBrowsingDatabaseManager::SafeBrowsingCheck::~SafeBrowsingCheck() {
209 void LocalSafeBrowsingDatabaseManager::SafeBrowsingCheck::
210 OnSafeBrowsingResult() {
211 DCHECK_CURRENTLY_ON(BrowserThread::IO);
213 DCHECK(client);
214 DCHECK_EQ(urls.size(), url_results.size());
215 DCHECK_EQ(full_hashes.size(), full_hash_results.size());
216 if (!urls.empty()) {
217 DCHECK(full_hashes.empty());
218 switch (check_type) {
219 case safe_browsing_util::MALWARE:
220 case safe_browsing_util::PHISH:
221 case safe_browsing_util::UNWANTEDURL:
222 DCHECK_EQ(1u, urls.size());
223 client->OnCheckBrowseUrlResult(urls[0], url_results[0],
224 url_metadata[0]);
225 break;
226 case safe_browsing_util::BINURL:
227 DCHECK_EQ(urls.size(), url_results.size());
228 client->OnCheckDownloadUrlResult(
229 urls, *std::max_element(url_results.begin(), url_results.end()));
230 break;
231 default:
232 NOTREACHED();
234 } else if (!full_hashes.empty()) {
235 switch (check_type) {
236 case safe_browsing_util::EXTENSIONBLACKLIST: {
237 std::set<std::string> unsafe_extension_ids;
238 for (size_t i = 0; i < full_hashes.size(); ++i) {
239 std::string extension_id =
240 safe_browsing_util::SBFullHashToString(full_hashes[i]);
241 if (full_hash_results[i] == SB_THREAT_TYPE_EXTENSION)
242 unsafe_extension_ids.insert(extension_id);
244 client->OnCheckExtensionsResult(unsafe_extension_ids);
245 break;
247 default:
248 NOTREACHED();
250 } else {
251 NOTREACHED();
255 LocalSafeBrowsingDatabaseManager::LocalSafeBrowsingDatabaseManager(
256 const scoped_refptr<SafeBrowsingService>& service)
257 : sb_service_(service),
258 database_(NULL),
259 enabled_(false),
260 enable_download_protection_(false),
261 enable_csd_whitelist_(false),
262 enable_download_whitelist_(false),
263 enable_extension_blacklist_(false),
264 enable_ip_blacklist_(false),
265 enable_unwanted_software_blacklist_(true),
266 update_in_progress_(false),
267 database_update_in_progress_(false),
268 closing_database_(false),
269 check_timeout_(base::TimeDelta::FromMilliseconds(kCheckTimeoutMs)) {
270 DCHECK_CURRENTLY_ON(BrowserThread::UI);
271 DCHECK(sb_service_.get() != NULL);
273 // Android only supports a subset of FULL_SAFE_BROWSING.
274 // TODO(shess): This shouldn't be OS-driven <http://crbug.com/394379>
275 #if !defined(OS_ANDROID)
276 base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
277 enable_download_protection_ =
278 !cmdline->HasSwitch(switches::kSbDisableDownloadProtection);
280 // We only download the csd-whitelist if client-side phishing detection is
281 // enabled.
282 enable_csd_whitelist_ =
283 !cmdline->HasSwitch(switches::kDisableClientSidePhishingDetection);
285 // TODO(noelutz): remove this boolean variable since it should always be true
286 // if SafeBrowsing is enabled. Unfortunately, we have no test data for this
287 // list right now. This means that we need to be able to disable this list
288 // for the SafeBrowsing test to pass.
289 enable_download_whitelist_ = enable_csd_whitelist_;
291 // TODO(kalman): there really shouldn't be a flag for this.
292 enable_extension_blacklist_ =
293 !cmdline->HasSwitch(switches::kSbDisableExtensionBlacklist);
295 // The client-side IP blacklist feature is tightly integrated with client-side
296 // phishing protection for now.
297 enable_ip_blacklist_ = enable_csd_whitelist_;
298 #endif
301 LocalSafeBrowsingDatabaseManager::~LocalSafeBrowsingDatabaseManager() {
302 // The DCHECK is disabled due to crbug.com/438754.
303 // DCHECK_CURRENTLY_ON(BrowserThread::UI);
305 // We should have already been shut down. If we're still enabled, then the
306 // database isn't going to be closed properly, which could lead to corruption.
307 DCHECK(!enabled_);
310 bool LocalSafeBrowsingDatabaseManager::CanCheckUrl(const GURL& url) const {
311 return url.SchemeIs(url::kFtpScheme) ||
312 url.SchemeIs(url::kHttpScheme) ||
313 url.SchemeIs(url::kHttpsScheme);
316 bool LocalSafeBrowsingDatabaseManager::CheckDownloadUrl(
317 const std::vector<GURL>& url_chain,
318 Client* client) {
319 DCHECK_CURRENTLY_ON(BrowserThread::IO);
320 if (!enabled_ || !enable_download_protection_)
321 return true;
323 // We need to check the database for url prefix, and later may fetch the url
324 // from the safebrowsing backends. These need to be asynchronous.
325 SafeBrowsingCheck* check =
326 new SafeBrowsingCheck(url_chain,
327 std::vector<SBFullHash>(),
328 client,
329 safe_browsing_util::BINURL,
330 std::vector<SBThreatType>(1,
331 SB_THREAT_TYPE_BINARY_MALWARE_URL));
332 std::vector<SBPrefix> prefixes;
333 SafeBrowsingDatabase::GetDownloadUrlPrefixes(url_chain, &prefixes);
334 StartSafeBrowsingCheck(
335 check,
336 base::Bind(&LocalSafeBrowsingDatabaseManager::CheckDownloadUrlOnSBThread,
337 this, prefixes));
338 return false;
341 bool LocalSafeBrowsingDatabaseManager::CheckExtensionIDs(
342 const std::set<std::string>& extension_ids,
343 Client* client) {
344 DCHECK_CURRENTLY_ON(BrowserThread::IO);
346 if (!enabled_ || !enable_extension_blacklist_)
347 return true;
349 std::vector<SBFullHash> extension_id_hashes;
350 std::transform(extension_ids.begin(), extension_ids.end(),
351 std::back_inserter(extension_id_hashes),
352 safe_browsing_util::StringToSBFullHash);
353 std::vector<SBPrefix> prefixes;
354 for (const SBFullHash& hash : extension_id_hashes)
355 prefixes.push_back(hash.prefix);
357 SafeBrowsingCheck* check = new SafeBrowsingCheck(
358 std::vector<GURL>(),
359 extension_id_hashes,
360 client,
361 safe_browsing_util::EXTENSIONBLACKLIST,
362 std::vector<SBThreatType>(1, SB_THREAT_TYPE_EXTENSION));
363 StartSafeBrowsingCheck(
364 check,
365 base::Bind(&LocalSafeBrowsingDatabaseManager::CheckExtensionIDsOnSBThread,
366 this, prefixes));
367 return false;
370 bool LocalSafeBrowsingDatabaseManager::MatchMalwareIP(
371 const std::string& ip_address) {
372 DCHECK_CURRENTLY_ON(BrowserThread::IO);
373 if (!enabled_ || !enable_ip_blacklist_ || !MakeDatabaseAvailable()) {
374 return false; // Fail open.
376 return database_->ContainsMalwareIP(ip_address);
379 bool LocalSafeBrowsingDatabaseManager::MatchCsdWhitelistUrl(const GURL& url) {
380 DCHECK_CURRENTLY_ON(BrowserThread::IO);
381 if (!enabled_ || !enable_csd_whitelist_ || !MakeDatabaseAvailable()) {
382 // There is something funky going on here -- for example, perhaps the user
383 // has not restarted since enabling metrics reporting, so we haven't
384 // enabled the csd whitelist yet. Just to be safe we return true in this
385 // case.
386 return true;
388 return database_->ContainsCsdWhitelistedUrl(url);
391 bool LocalSafeBrowsingDatabaseManager::MatchDownloadWhitelistUrl(
392 const GURL& url) {
393 DCHECK_CURRENTLY_ON(BrowserThread::IO);
394 if (!enabled_ || !enable_download_whitelist_ || !MakeDatabaseAvailable()) {
395 return true;
397 return database_->ContainsDownloadWhitelistedUrl(url);
400 bool LocalSafeBrowsingDatabaseManager::MatchDownloadWhitelistString(
401 const std::string& str) {
402 DCHECK_CURRENTLY_ON(BrowserThread::IO);
403 if (!enabled_ || !enable_download_whitelist_ || !MakeDatabaseAvailable()) {
404 return true;
406 return database_->ContainsDownloadWhitelistedString(str);
409 bool LocalSafeBrowsingDatabaseManager::MatchInclusionWhitelistUrl(
410 const GURL& url) {
411 DCHECK_CURRENTLY_ON(BrowserThread::IO);
412 if (!enabled_ || !MakeDatabaseAvailable())
413 return true;
414 return database_->ContainsInclusionWhitelistedUrl(url);
417 bool LocalSafeBrowsingDatabaseManager::IsMalwareKillSwitchOn() {
418 DCHECK_CURRENTLY_ON(BrowserThread::IO);
419 if (!enabled_ || !MakeDatabaseAvailable()) {
420 return true;
422 return database_->IsMalwareIPMatchKillSwitchOn();
425 bool LocalSafeBrowsingDatabaseManager::IsCsdWhitelistKillSwitchOn() {
426 DCHECK_CURRENTLY_ON(BrowserThread::IO);
427 if (!enabled_ || !MakeDatabaseAvailable()) {
428 return true;
430 return database_->IsCsdWhitelistKillSwitchOn();
433 bool LocalSafeBrowsingDatabaseManager::CheckBrowseUrl(const GURL& url,
434 Client* client) {
435 DCHECK_CURRENTLY_ON(BrowserThread::IO);
436 if (!enabled_)
437 return true;
439 if (!CanCheckUrl(url))
440 return true;
442 std::vector<SBThreatType> expected_threats;
443 expected_threats.push_back(SB_THREAT_TYPE_URL_MALWARE);
444 expected_threats.push_back(SB_THREAT_TYPE_URL_PHISHING);
445 expected_threats.push_back(SB_THREAT_TYPE_URL_UNWANTED);
447 const base::TimeTicks start = base::TimeTicks::Now();
448 if (!MakeDatabaseAvailable()) {
449 QueuedCheck queued_check(safe_browsing_util::MALWARE, // or PHISH
450 client,
451 url,
452 expected_threats,
453 start);
454 queued_checks_.push_back(queued_check);
455 return false;
458 // Cache hits should, in general, be the same for both (ignoring potential
459 // cache evictions in the second call for entries that were just about to be
460 // evicted in the first call).
461 // TODO(gab): Refactor SafeBrowsingDatabase to avoid depending on this here.
462 std::vector<SBFullHashResult> cache_hits;
464 std::vector<SBPrefix> browse_prefix_hits;
465 bool browse_prefix_match = database_->ContainsBrowseUrl(
466 url, &browse_prefix_hits, &cache_hits);
468 std::vector<SBPrefix> unwanted_prefix_hits;
469 std::vector<SBFullHashResult> unused_cache_hits;
470 bool unwanted_prefix_match = database_->ContainsUnwantedSoftwareUrl(
471 url, &unwanted_prefix_hits, &unused_cache_hits);
473 // Merge the two pre-sorted prefix hits lists.
474 // TODO(gab): Refactor SafeBrowsingDatabase for it to return this merged list
475 // by default rather than building it here.
476 std::vector<SBPrefix> prefix_hits(browse_prefix_hits.size() +
477 unwanted_prefix_hits.size());
478 std::merge(browse_prefix_hits.begin(),
479 browse_prefix_hits.end(),
480 unwanted_prefix_hits.begin(),
481 unwanted_prefix_hits.end(),
482 prefix_hits.begin());
483 prefix_hits.erase(std::unique(prefix_hits.begin(), prefix_hits.end()),
484 prefix_hits.end());
486 UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::TimeTicks::Now() - start);
488 if (!browse_prefix_match && !unwanted_prefix_match)
489 return true; // URL is okay.
491 // Needs to be asynchronous, since we could be in the constructor of a
492 // ResourceDispatcherHost event handler which can't pause there.
493 // This check will ping the Safe Browsing servers and get all lists which it
494 // matches. These lists will then be filtered against the |expected_threats|
495 // and the result callback for MALWARE (which is the same as for PHISH and
496 // UNWANTEDURL) will eventually be invoked with the final decision.
497 SafeBrowsingCheck* check = new SafeBrowsingCheck(std::vector<GURL>(1, url),
498 std::vector<SBFullHash>(),
499 client,
500 safe_browsing_util::MALWARE,
501 expected_threats);
502 check->need_get_hash = cache_hits.empty();
503 check->prefix_hits.swap(prefix_hits);
504 check->cache_hits.swap(cache_hits);
505 checks_.insert(check);
507 BrowserThread::PostTask(
508 BrowserThread::IO, FROM_HERE,
509 base::Bind(&LocalSafeBrowsingDatabaseManager::OnCheckDone, this, check));
511 return false;
514 void LocalSafeBrowsingDatabaseManager::CancelCheck(Client* client) {
515 DCHECK_CURRENTLY_ON(BrowserThread::IO);
516 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
517 // We can't delete matching checks here because the db thread has a copy of
518 // the pointer. Instead, we simply NULL out the client, and when the db
519 // thread calls us back, we'll clean up the check.
520 if ((*i)->client == client)
521 (*i)->client = NULL;
524 // Scan the queued clients store. Clients may be here if they requested a URL
525 // check before the database has finished loading.
526 for (std::deque<QueuedCheck>::iterator it(queued_checks_.begin());
527 it != queued_checks_.end(); ) {
528 // In this case it's safe to delete matches entirely since nothing has a
529 // pointer to them.
530 if (it->client == client)
531 it = queued_checks_.erase(it);
532 else
533 ++it;
537 void LocalSafeBrowsingDatabaseManager::HandleGetHashResults(
538 SafeBrowsingCheck* check,
539 const std::vector<SBFullHashResult>& full_hashes,
540 const base::TimeDelta& cache_lifetime) {
541 DCHECK_CURRENTLY_ON(BrowserThread::IO);
543 if (!enabled_)
544 return;
546 // If the service has been shut down, |check| should have been deleted.
547 DCHECK(checks_.find(check) != checks_.end());
549 // |start| is set before calling |GetFullHash()|, which should be
550 // the only path which gets to here.
551 DCHECK(!check->start.is_null());
552 UMA_HISTOGRAM_LONG_TIMES("SB2.Network",
553 base::TimeTicks::Now() - check->start);
555 std::vector<SBPrefix> prefixes = check->prefix_hits;
556 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
558 // Cache the GetHash results.
559 if (cache_lifetime != base::TimeDelta() && MakeDatabaseAvailable())
560 database_->CacheHashResults(prefixes, full_hashes, cache_lifetime);
563 void LocalSafeBrowsingDatabaseManager::GetChunks(GetChunksCallback callback) {
564 DCHECK_CURRENTLY_ON(BrowserThread::IO);
565 DCHECK(enabled_);
566 DCHECK(!callback.is_null());
567 safe_browsing_task_runner_->PostTask(
568 FROM_HERE,
569 base::Bind(&LocalSafeBrowsingDatabaseManager::GetAllChunksFromDatabase,
570 this, callback));
573 void LocalSafeBrowsingDatabaseManager::AddChunks(
574 const std::string& list,
575 scoped_ptr<ScopedVector<SBChunkData> > chunks,
576 AddChunksCallback callback) {
577 DCHECK_CURRENTLY_ON(BrowserThread::IO);
578 DCHECK(enabled_);
579 DCHECK(!callback.is_null());
580 safe_browsing_task_runner_->PostTask(
581 FROM_HERE,
582 base::Bind(&LocalSafeBrowsingDatabaseManager::AddDatabaseChunks, this,
583 list, base::Passed(&chunks), callback));
586 void LocalSafeBrowsingDatabaseManager::DeleteChunks(
587 scoped_ptr<std::vector<SBChunkDelete> > chunk_deletes) {
588 DCHECK_CURRENTLY_ON(BrowserThread::IO);
589 DCHECK(enabled_);
590 safe_browsing_task_runner_->PostTask(
591 FROM_HERE,
592 base::Bind(&LocalSafeBrowsingDatabaseManager::DeleteDatabaseChunks, this,
593 base::Passed(&chunk_deletes)));
596 void LocalSafeBrowsingDatabaseManager::UpdateStarted() {
597 DCHECK_CURRENTLY_ON(BrowserThread::IO);
598 DCHECK(enabled_);
599 DCHECK(!update_in_progress_);
600 update_in_progress_ = true;
603 void LocalSafeBrowsingDatabaseManager::UpdateFinished(bool update_succeeded) {
604 DCHECK_CURRENTLY_ON(BrowserThread::IO);
605 DCHECK(enabled_);
606 if (update_in_progress_) {
607 update_in_progress_ = false;
608 safe_browsing_task_runner_->PostTask(
609 FROM_HERE,
610 base::Bind(&LocalSafeBrowsingDatabaseManager::DatabaseUpdateFinished,
611 this, update_succeeded));
615 void LocalSafeBrowsingDatabaseManager::ResetDatabase() {
616 DCHECK_CURRENTLY_ON(BrowserThread::IO);
617 DCHECK(enabled_);
618 safe_browsing_task_runner_->PostTask(
619 FROM_HERE,
620 base::Bind(&LocalSafeBrowsingDatabaseManager::OnResetDatabase, this));
623 void LocalSafeBrowsingDatabaseManager::StartOnIOThread() {
624 DCHECK_CURRENTLY_ON(BrowserThread::IO);
625 if (enabled_)
626 return;
628 // Only get a new task runner if there isn't one already. If the service has
629 // previously been started and stopped, a task runner could already exist.
630 if (!safe_browsing_task_runner_) {
631 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
632 safe_browsing_task_runner_ =
633 pool->GetSequencedTaskRunnerWithShutdownBehavior(
634 pool->GetSequenceToken(),
635 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
638 enabled_ = true;
640 MakeDatabaseAvailable();
643 void LocalSafeBrowsingDatabaseManager::StopOnIOThread(bool shutdown) {
644 DCHECK_CURRENTLY_ON(BrowserThread::IO);
646 DoStopOnIOThread();
647 if (shutdown) {
648 sb_service_ = NULL;
652 void LocalSafeBrowsingDatabaseManager::NotifyDatabaseUpdateFinished(
653 bool update_succeeded) {
654 DCHECK_CURRENTLY_ON(BrowserThread::UI);
655 content::NotificationService::current()->Notify(
656 chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
657 content::Source<SafeBrowsingDatabaseManager>(this),
658 content::Details<bool>(&update_succeeded));
661 LocalSafeBrowsingDatabaseManager::QueuedCheck::QueuedCheck(
662 const safe_browsing_util::ListType check_type,
663 Client* client,
664 const GURL& url,
665 const std::vector<SBThreatType>& expected_threats,
666 const base::TimeTicks& start)
667 : check_type(check_type),
668 client(client),
669 url(url),
670 expected_threats(expected_threats),
671 start(start) {
674 LocalSafeBrowsingDatabaseManager::QueuedCheck::~QueuedCheck() {
677 void LocalSafeBrowsingDatabaseManager::DoStopOnIOThread() {
678 DCHECK_CURRENTLY_ON(BrowserThread::IO);
680 if (!enabled_)
681 return;
683 enabled_ = false;
685 // Delete queued checks, calling back any clients with 'SB_THREAT_TYPE_SAFE'.
686 while (!queued_checks_.empty()) {
687 QueuedCheck queued = queued_checks_.front();
688 if (queued.client) {
689 SafeBrowsingCheck sb_check(std::vector<GURL>(1, queued.url),
690 std::vector<SBFullHash>(),
691 queued.client,
692 queued.check_type,
693 queued.expected_threats);
694 sb_check.OnSafeBrowsingResult();
696 queued_checks_.pop_front();
699 // Close the database. Cases to avoid:
700 // * If |closing_database_| is true, continuing will queue up a second
701 // request, |closing_database_| will be reset after handling the first
702 // request, and if any functions on the db thread recreate the database, we
703 // could start using it on the IO thread and then have the second request
704 // handler delete it out from under us.
705 // * If |database_| is NULL, then either no creation request is in flight, in
706 // which case we don't need to do anything, or one is in flight, in which
707 // case the database will be recreated before our deletion request is
708 // handled, and could be used on the IO thread in that time period, leading
709 // to the same problem as above.
710 // Checking DatabaseAvailable() avoids both of these.
711 if (DatabaseAvailable()) {
712 closing_database_ = true;
713 safe_browsing_task_runner_->PostTask(
714 FROM_HERE,
715 base::Bind(&LocalSafeBrowsingDatabaseManager::OnCloseDatabase, this));
718 // Delete pending checks, calling back any clients with 'SB_THREAT_TYPE_SAFE'.
719 // We have to do this after the db thread returns because methods on it can
720 // have copies of these pointers, so deleting them might lead to accessing
721 // garbage.
722 for (CurrentChecks::iterator it = checks_.begin();
723 it != checks_.end(); ++it) {
724 SafeBrowsingCheck* check = *it;
725 if (check->client)
726 check->OnSafeBrowsingResult();
728 STLDeleteElements(&checks_);
730 gethash_requests_.clear();
733 bool LocalSafeBrowsingDatabaseManager::DatabaseAvailable() const {
734 base::AutoLock lock(database_lock_);
735 return !closing_database_ && (database_ != NULL);
738 bool LocalSafeBrowsingDatabaseManager::MakeDatabaseAvailable() {
739 DCHECK_CURRENTLY_ON(BrowserThread::IO);
740 DCHECK(enabled_);
741 if (DatabaseAvailable())
742 return true;
743 safe_browsing_task_runner_->PostTask(
744 FROM_HERE, base::Bind(base::IgnoreResult(
745 &LocalSafeBrowsingDatabaseManager::GetDatabase),
746 this));
747 return false;
750 SafeBrowsingDatabase* LocalSafeBrowsingDatabaseManager::GetDatabase() {
751 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
753 if (database_)
754 return database_;
756 const base::TimeTicks before = base::TimeTicks::Now();
757 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create(
758 safe_browsing_task_runner_, enable_download_protection_,
759 enable_csd_whitelist_, enable_download_whitelist_,
760 enable_extension_blacklist_, enable_ip_blacklist_,
761 enable_unwanted_software_blacklist_);
763 database->Init(SafeBrowsingService::GetBaseFilename());
765 // Acquiring the lock here guarantees correct ordering between the writes to
766 // the new database object above, and the setting of |database_| below.
767 base::AutoLock lock(database_lock_);
768 database_ = database;
771 BrowserThread::PostTask(
772 BrowserThread::IO, FROM_HERE,
773 base::Bind(&LocalSafeBrowsingDatabaseManager::DatabaseLoadComplete,
774 this));
776 UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", base::TimeTicks::Now() - before);
777 return database_;
780 void LocalSafeBrowsingDatabaseManager::OnCheckDone(SafeBrowsingCheck* check) {
781 DCHECK_CURRENTLY_ON(BrowserThread::IO);
783 if (!enabled_)
784 return;
786 // If the service has been shut down, |check| should have been deleted.
787 DCHECK(checks_.find(check) != checks_.end());
789 if (check->client && check->need_get_hash) {
790 // We have a partial match so we need to query Google for the full hash.
791 // Clean up will happen in HandleGetHashResults.
793 // See if we have a GetHash request already in progress for this particular
794 // prefix. If so, we just append ourselves to the list of interested parties
795 // when the results arrive. We only do this for checks involving one prefix,
796 // since that is the common case (multiple prefixes will issue the request
797 // as normal).
798 if (check->prefix_hits.size() == 1) {
799 SBPrefix prefix = check->prefix_hits[0];
800 GetHashRequests::iterator it = gethash_requests_.find(prefix);
801 if (it != gethash_requests_.end()) {
802 // There's already a request in progress.
803 it->second.push_back(check);
804 return;
807 // No request in progress, so we're the first for this prefix.
808 GetHashRequestors requestors;
809 requestors.push_back(check);
810 gethash_requests_[prefix] = requestors;
813 // Reset the start time so that we can measure the network time without the
814 // database time.
815 check->start = base::TimeTicks::Now();
816 // Note: If |this| is deleted or stopped, the protocol_manager will
817 // be destroyed as well - hence it's OK to do unretained in this case.
818 bool is_download = check->check_type == safe_browsing_util::BINURL;
819 sb_service_->protocol_manager()->GetFullHash(
820 check->prefix_hits,
821 base::Bind(&LocalSafeBrowsingDatabaseManager::HandleGetHashResults,
822 base::Unretained(this),
823 check),
824 is_download);
825 } else {
826 // We may have cached results for previous GetHash queries. Since
827 // this data comes from cache, don't histogram hits.
828 HandleOneCheck(check, check->cache_hits);
832 void LocalSafeBrowsingDatabaseManager::GetAllChunksFromDatabase(
833 GetChunksCallback callback) {
834 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
836 bool database_error = true;
837 std::vector<SBListChunkRanges> lists;
838 DCHECK(!database_update_in_progress_);
839 database_update_in_progress_ = true;
840 GetDatabase(); // This guarantees that |database_| is non-NULL.
841 if (database_->UpdateStarted(&lists)) {
842 database_error = false;
843 } else {
844 database_->UpdateFinished(false);
847 BrowserThread::PostTask(
848 BrowserThread::IO, FROM_HERE,
849 base::Bind(&LocalSafeBrowsingDatabaseManager::OnGetAllChunksFromDatabase,
850 this, lists, database_error, callback));
853 void LocalSafeBrowsingDatabaseManager::OnGetAllChunksFromDatabase(
854 const std::vector<SBListChunkRanges>& lists, bool database_error,
855 GetChunksCallback callback) {
856 DCHECK_CURRENTLY_ON(BrowserThread::IO);
857 if (enabled_)
858 callback.Run(lists, database_error);
861 void LocalSafeBrowsingDatabaseManager::OnAddChunksComplete(
862 AddChunksCallback callback) {
863 DCHECK_CURRENTLY_ON(BrowserThread::IO);
864 if (enabled_)
865 callback.Run();
868 void LocalSafeBrowsingDatabaseManager::DatabaseLoadComplete() {
869 DCHECK_CURRENTLY_ON(BrowserThread::IO);
870 if (!enabled_)
871 return;
873 LOCAL_HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
874 if (queued_checks_.empty())
875 return;
877 // If the database isn't already available, calling CheckUrl() in the loop
878 // below will add the check back to the queue, and we'll infinite-loop.
879 DCHECK(DatabaseAvailable());
880 while (!queued_checks_.empty()) {
881 QueuedCheck check = queued_checks_.front();
882 DCHECK(!check.start.is_null());
883 LOCAL_HISTOGRAM_TIMES("SB.QueueDelay",
884 base::TimeTicks::Now() - check.start);
885 // If CheckUrl() determines the URL is safe immediately, it doesn't call the
886 // client's handler function (because normally it's being directly called by
887 // the client). Since we're not the client, we have to convey this result.
888 if (check.client && CheckBrowseUrl(check.url, check.client)) {
889 SafeBrowsingCheck sb_check(std::vector<GURL>(1, check.url),
890 std::vector<SBFullHash>(),
891 check.client,
892 check.check_type,
893 check.expected_threats);
894 sb_check.OnSafeBrowsingResult();
896 queued_checks_.pop_front();
900 void LocalSafeBrowsingDatabaseManager::AddDatabaseChunks(
901 const std::string& list_name,
902 scoped_ptr<ScopedVector<SBChunkData> > chunks,
903 AddChunksCallback callback) {
904 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
905 if (chunks)
906 GetDatabase()->InsertChunks(list_name, chunks->get());
907 BrowserThread::PostTask(
908 BrowserThread::IO, FROM_HERE,
909 base::Bind(&LocalSafeBrowsingDatabaseManager::OnAddChunksComplete, this,
910 callback));
913 void LocalSafeBrowsingDatabaseManager::DeleteDatabaseChunks(
914 scoped_ptr<std::vector<SBChunkDelete> > chunk_deletes) {
915 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
916 if (chunk_deletes)
917 GetDatabase()->DeleteChunks(*chunk_deletes);
920 void LocalSafeBrowsingDatabaseManager::DatabaseUpdateFinished(
921 bool update_succeeded) {
922 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
923 GetDatabase()->UpdateFinished(update_succeeded);
924 DCHECK(database_update_in_progress_);
925 database_update_in_progress_ = false;
926 BrowserThread::PostTask(
927 BrowserThread::UI, FROM_HERE,
928 base::Bind(
929 &LocalSafeBrowsingDatabaseManager::NotifyDatabaseUpdateFinished, this,
930 update_succeeded));
933 void LocalSafeBrowsingDatabaseManager::OnCloseDatabase() {
934 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
935 DCHECK(closing_database_);
937 // Because |closing_database_| is true, nothing on the IO thread will be
938 // accessing the database, so it's safe to delete and then NULL the pointer.
939 delete database_;
940 database_ = NULL;
942 // Acquiring the lock here guarantees correct ordering between the resetting
943 // of |database_| above and of |closing_database_| below, which ensures there
944 // won't be a window during which the IO thread falsely believes the database
945 // is available.
946 base::AutoLock lock(database_lock_);
947 closing_database_ = false;
950 void LocalSafeBrowsingDatabaseManager::OnResetDatabase() {
951 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
953 GetDatabase()->ResetDatabase();
956 void LocalSafeBrowsingDatabaseManager::OnHandleGetHashResults(
957 SafeBrowsingCheck* check,
958 const std::vector<SBFullHashResult>& full_hashes) {
959 DCHECK_CURRENTLY_ON(BrowserThread::IO);
960 safe_browsing_util::ListType check_type = check->check_type;
961 SBPrefix prefix = check->prefix_hits[0];
962 GetHashRequests::iterator it = gethash_requests_.find(prefix);
963 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
964 const bool hit = HandleOneCheck(check, full_hashes);
965 RecordGetHashCheckStatus(hit, check_type, full_hashes);
966 return;
969 // Call back all interested parties, noting if any has a hit.
970 GetHashRequestors& requestors = it->second;
971 bool hit = false;
972 for (GetHashRequestors::iterator r = requestors.begin();
973 r != requestors.end(); ++r) {
974 if (HandleOneCheck(*r, full_hashes))
975 hit = true;
977 RecordGetHashCheckStatus(hit, check_type, full_hashes);
979 gethash_requests_.erase(it);
982 bool LocalSafeBrowsingDatabaseManager::HandleOneCheck(
983 SafeBrowsingCheck* check,
984 const std::vector<SBFullHashResult>& full_hashes) {
985 DCHECK_CURRENTLY_ON(BrowserThread::IO);
986 DCHECK(check);
988 bool is_threat = false;
990 // TODO(shess): GetHashSeverestThreadListType() contains a loop,
991 // GetUrlSeverestThreatListType() a loop around that loop. Having another
992 // loop out here concerns me. It is likely that SAFE is an expected outcome,
993 // which means all of those loops run to completion. Refactoring this to
994 // generate a set of sorted items to compare in sequence would probably
995 // improve things.
997 // Additionally, the set of patterns generated from the urls is very similar
998 // to the patterns generated in ContainsBrowseUrl() and other database checks,
999 // which are called from this code. Refactoring that across the checks could
1000 // interact well with batching the checks here.
1002 // TODO(gab): Fix the fact that Get(Url|Hash)SeverestThreatType() may return a
1003 // threat for which IsExpectedThreat() returns false even if |full_hashes|
1004 // actually contains an expected threat.
1006 for (size_t i = 0; i < check->urls.size(); ++i) {
1007 size_t threat_index;
1008 SBThreatType threat =
1009 GetUrlSeverestThreatType(check->urls[i], full_hashes, &threat_index);
1010 if (threat != SB_THREAT_TYPE_SAFE &&
1011 IsExpectedThreat(threat, check->expected_threats)) {
1012 check->url_results[i] = threat;
1013 check->url_metadata[i] = full_hashes[threat_index].metadata;
1014 is_threat = true;
1018 for (size_t i = 0; i < check->full_hashes.size(); ++i) {
1019 SBThreatType threat =
1020 GetHashSeverestThreatType(check->full_hashes[i], full_hashes);
1021 if (threat != SB_THREAT_TYPE_SAFE &&
1022 IsExpectedThreat(threat, check->expected_threats)) {
1023 check->full_hash_results[i] = threat;
1024 is_threat = true;
1028 SafeBrowsingCheckDone(check);
1029 return is_threat;
1032 void LocalSafeBrowsingDatabaseManager::OnAsyncCheckDone(
1033 SafeBrowsingCheck* check,
1034 const std::vector<SBPrefix>& prefix_hits) {
1035 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1036 DCHECK(enable_download_protection_);
1038 check->prefix_hits = prefix_hits;
1039 if (check->prefix_hits.empty()) {
1040 SafeBrowsingCheckDone(check);
1041 } else {
1042 check->need_get_hash = true;
1043 OnCheckDone(check);
1047 std::vector<SBPrefix>
1048 LocalSafeBrowsingDatabaseManager::CheckDownloadUrlOnSBThread(
1049 const std::vector<SBPrefix>& prefixes) {
1050 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
1051 DCHECK(enable_download_protection_);
1053 std::vector<SBPrefix> prefix_hits;
1054 const bool result =
1055 database_->ContainsDownloadUrlPrefixes(prefixes, &prefix_hits);
1056 DCHECK_EQ(result, !prefix_hits.empty());
1057 return prefix_hits;
1060 std::vector<SBPrefix>
1061 LocalSafeBrowsingDatabaseManager::CheckExtensionIDsOnSBThread(
1062 const std::vector<SBPrefix>& prefixes) {
1063 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
1065 std::vector<SBPrefix> prefix_hits;
1066 const bool result =
1067 database_->ContainsExtensionPrefixes(prefixes, &prefix_hits);
1068 DCHECK_EQ(result, !prefix_hits.empty());
1069 return prefix_hits;
1072 void LocalSafeBrowsingDatabaseManager::TimeoutCallback(
1073 SafeBrowsingCheck* check) {
1074 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1075 DCHECK(check);
1077 if (!enabled_)
1078 return;
1080 DCHECK(checks_.find(check) != checks_.end());
1081 if (check->client) {
1082 check->OnSafeBrowsingResult();
1083 check->client = NULL;
1087 void LocalSafeBrowsingDatabaseManager::SafeBrowsingCheckDone(
1088 SafeBrowsingCheck* check) {
1089 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1090 DCHECK(check);
1092 if (!enabled_)
1093 return;
1095 DVLOG(1) << "SafeBrowsingCheckDone";
1096 DCHECK(checks_.find(check) != checks_.end());
1097 if (check->client)
1098 check->OnSafeBrowsingResult();
1099 checks_.erase(check);
1100 delete check;
1103 void LocalSafeBrowsingDatabaseManager::StartSafeBrowsingCheck(
1104 SafeBrowsingCheck* check,
1105 const base::Callback<std::vector<SBPrefix>(void)>& task) {
1106 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1107 check->weak_ptr_factory_.reset(
1108 new base::WeakPtrFactory<LocalSafeBrowsingDatabaseManager>(this));
1109 checks_.insert(check);
1111 base::PostTaskAndReplyWithResult(
1112 safe_browsing_task_runner_.get(), FROM_HERE, task,
1113 base::Bind(&LocalSafeBrowsingDatabaseManager::OnAsyncCheckDone,
1114 check->weak_ptr_factory_->GetWeakPtr(), check));
1115 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
1116 FROM_HERE, base::Bind(&LocalSafeBrowsingDatabaseManager::TimeoutCallback,
1117 check->weak_ptr_factory_->GetWeakPtr(), check),
1118 check_timeout_);
1121 bool LocalSafeBrowsingDatabaseManager::download_protection_enabled() const {
1122 return enable_download_protection_;