Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(929)

Side by Side Diff: chrome/browser/safe_browsing/database_manager.cc

Issue 1110723002: Split to SafeBrowsingDatabaseManager into Local* and Remote*. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Respond to review. Tweak comments and list initializer. Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 #include "chrome/browser/safe_browsing/database_manager.h"
6
7 #include <algorithm>
8
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/metrics/histogram_macros.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_util.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/prerender/prerender_field_trial.h"
20 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
21 #include "chrome/browser/safe_browsing/download_protection_service.h"
22 #include "chrome/browser/safe_browsing/malware_details.h"
23 #include "chrome/browser/safe_browsing/protocol_manager.h"
24 #include "chrome/browser/safe_browsing/safe_browsing_database.h"
25 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
26 #include "chrome/browser/safe_browsing/ui_manager.h"
27 #include "chrome/common/chrome_constants.h"
28 #include "chrome/common/chrome_paths.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "components/startup_metric_utils/startup_metric_utils.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/notification_service.h"
33 #include "url/url_constants.h"
34
35 using content::BrowserThread;
36
37 namespace {
38
39 // Timeout for match checks, e.g. download URLs, hashes.
40 const int kCheckTimeoutMs = 10000;
41
42 // Records disposition information about the check. |hit| should be
43 // |true| if there were any prefix hits in |full_hashes|.
44 void RecordGetHashCheckStatus(
45 bool hit,
46 safe_browsing_util::ListType check_type,
47 const std::vector<SBFullHashResult>& full_hashes) {
48 SafeBrowsingProtocolManager::ResultType result;
49 if (full_hashes.empty()) {
50 result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_EMPTY;
51 } else if (hit) {
52 result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_HIT;
53 } else {
54 result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_MISS;
55 }
56 bool is_download = check_type == safe_browsing_util::BINURL;
57 SafeBrowsingProtocolManager::RecordGetHashResult(is_download, result);
58 }
59
60 bool IsExpectedThreat(
61 const SBThreatType threat_type,
62 const std::vector<SBThreatType>& expected_threats) {
63 return expected_threats.end() != std::find(expected_threats.begin(),
64 expected_threats.end(),
65 threat_type);
66 }
67
68 // Return the severest list id from the results in |full_hashes| which matches
69 // |hash|, or INVALID if none match.
70 safe_browsing_util::ListType GetHashSeverestThreatListType(
71 const SBFullHash& hash,
72 const std::vector<SBFullHashResult>& full_hashes,
73 size_t* index) {
74 safe_browsing_util::ListType pending_threat = safe_browsing_util::INVALID;
75 for (size_t i = 0; i < full_hashes.size(); ++i) {
76 if (SBFullHashEqual(hash, full_hashes[i].hash)) {
77 const safe_browsing_util::ListType threat =
78 static_cast<safe_browsing_util::ListType>(full_hashes[i].list_id);
79 switch (threat) {
80 case safe_browsing_util::INVALID:
81 // |full_hashes| should never contain INVALID as a |list_id|.
82 NOTREACHED();
83 break;
84 case safe_browsing_util::MALWARE: // Falls through.
85 case safe_browsing_util::PHISH: // Falls through.
86 case safe_browsing_util::BINURL: // Falls through.
87 case safe_browsing_util::CSDWHITELIST: // Falls through.
88 case safe_browsing_util::DOWNLOADWHITELIST: // Falls through.
89 case safe_browsing_util::INCLUSIONWHITELIST: // Falls through.
90 case safe_browsing_util::EXTENSIONBLACKLIST: // Falls through.
91 case safe_browsing_util::IPBLACKLIST:
92 if (index)
93 *index = i;
94 return threat;
95 case safe_browsing_util::UNWANTEDURL:
96 // UNWANTEDURL is considered less severe than other threats, keep
97 // looking.
98 pending_threat = threat;
99 if (index)
100 *index = i;
101 break;
102 }
103 }
104 }
105 return pending_threat;
106 }
107
108 // Given a URL, compare all the possible host + path full hashes to the set of
109 // provided full hashes. Returns the list id of the severest matching result
110 // from |full_hashes|, or INVALID if none match.
111 safe_browsing_util::ListType GetUrlSeverestThreatListType(
112 const GURL& url,
113 const std::vector<SBFullHashResult>& full_hashes,
114 size_t* index) {
115 if (full_hashes.empty())
116 return safe_browsing_util::INVALID;
117
118 std::vector<std::string> patterns;
119 safe_browsing_util::GeneratePatternsToCheck(url, &patterns);
120
121 safe_browsing_util::ListType pending_threat = safe_browsing_util::INVALID;
122 for (size_t i = 0; i < patterns.size(); ++i) {
123 safe_browsing_util::ListType threat = GetHashSeverestThreatListType(
124 SBFullHashForString(patterns[i]), full_hashes, index);
125 switch (threat) {
126 case safe_browsing_util::INVALID:
127 // Ignore patterns with no matching threat.
128 break;
129 case safe_browsing_util::MALWARE: // Falls through.
130 case safe_browsing_util::PHISH: // Falls through.
131 case safe_browsing_util::BINURL: // Falls through.
132 case safe_browsing_util::CSDWHITELIST: // Falls through.
133 case safe_browsing_util::DOWNLOADWHITELIST: // Falls through.
134 case safe_browsing_util::INCLUSIONWHITELIST: // Falls through.
135 case safe_browsing_util::EXTENSIONBLACKLIST: // Falls through.
136 case safe_browsing_util::IPBLACKLIST:
137 return threat;
138 case safe_browsing_util::UNWANTEDURL:
139 // UNWANTEDURL is considered less severe than other threats, keep
140 // looking.
141 pending_threat = threat;
142 break;
143 }
144 }
145 return pending_threat;
146 }
147
148 SBThreatType GetThreatTypeFromListType(safe_browsing_util::ListType list_type) {
149 switch (list_type) {
150 case safe_browsing_util::PHISH:
151 return SB_THREAT_TYPE_URL_PHISHING;
152 case safe_browsing_util::MALWARE:
153 return SB_THREAT_TYPE_URL_MALWARE;
154 case safe_browsing_util::UNWANTEDURL:
155 return SB_THREAT_TYPE_URL_UNWANTED;
156 case safe_browsing_util::BINURL:
157 return SB_THREAT_TYPE_BINARY_MALWARE_URL;
158 case safe_browsing_util::EXTENSIONBLACKLIST:
159 return SB_THREAT_TYPE_EXTENSION;
160 default:
161 DVLOG(1) << "Unknown safe browsing list id " << list_type;
162 return SB_THREAT_TYPE_SAFE;
163 }
164 }
165
166 } // namespace
167
168 // static
169 SBThreatType SafeBrowsingDatabaseManager::GetHashSeverestThreatType(
170 const SBFullHash& hash,
171 const std::vector<SBFullHashResult>& full_hashes) {
172 return GetThreatTypeFromListType(
173 GetHashSeverestThreatListType(hash, full_hashes, NULL));
174 }
175
176 // static
177 SBThreatType SafeBrowsingDatabaseManager::GetUrlSeverestThreatType(
178 const GURL& url,
179 const std::vector<SBFullHashResult>& full_hashes,
180 size_t* index) {
181 return GetThreatTypeFromListType(
182 GetUrlSeverestThreatListType(url, full_hashes, index));
183 }
184
185 SafeBrowsingDatabaseManager::SafeBrowsingCheck::SafeBrowsingCheck(
186 const std::vector<GURL>& urls,
187 const std::vector<SBFullHash>& full_hashes,
188 Client* client,
189 safe_browsing_util::ListType check_type,
190 const std::vector<SBThreatType>& expected_threats)
191 : urls(urls),
192 url_results(urls.size(), SB_THREAT_TYPE_SAFE),
193 url_metadata(urls.size()),
194 full_hashes(full_hashes),
195 full_hash_results(full_hashes.size(), SB_THREAT_TYPE_SAFE),
196 client(client),
197 need_get_hash(false),
198 check_type(check_type),
199 expected_threats(expected_threats) {
200 DCHECK_EQ(urls.empty(), !full_hashes.empty())
201 << "Exactly one of urls and full_hashes must be set";
202 }
203
204 SafeBrowsingDatabaseManager::SafeBrowsingCheck::~SafeBrowsingCheck() {}
205
206 void SafeBrowsingDatabaseManager::Client::OnSafeBrowsingResult(
207 const SafeBrowsingCheck& check) {
208 DCHECK_CURRENTLY_ON(BrowserThread::IO);
209
210 DCHECK_EQ(check.urls.size(), check.url_results.size());
211 DCHECK_EQ(check.full_hashes.size(), check.full_hash_results.size());
212 if (!check.urls.empty()) {
213 DCHECK(check.full_hashes.empty());
214 switch (check.check_type) {
215 case safe_browsing_util::MALWARE:
216 case safe_browsing_util::PHISH:
217 case safe_browsing_util::UNWANTEDURL:
218 DCHECK_EQ(1u, check.urls.size());
219 OnCheckBrowseUrlResult(
220 check.urls[0], check.url_results[0], check.url_metadata[0]);
221 break;
222 case safe_browsing_util::BINURL:
223 DCHECK_EQ(check.urls.size(), check.url_results.size());
224 OnCheckDownloadUrlResult(
225 check.urls,
226 *std::max_element(check.url_results.begin(),
227 check.url_results.end()));
228 break;
229 default:
230 NOTREACHED();
231 }
232 } else if (!check.full_hashes.empty()) {
233 switch (check.check_type) {
234 case safe_browsing_util::EXTENSIONBLACKLIST: {
235 std::set<std::string> unsafe_extension_ids;
236 for (size_t i = 0; i < check.full_hashes.size(); ++i) {
237 std::string extension_id =
238 safe_browsing_util::SBFullHashToString(check.full_hashes[i]);
239 if (check.full_hash_results[i] == SB_THREAT_TYPE_EXTENSION)
240 unsafe_extension_ids.insert(extension_id);
241 }
242 OnCheckExtensionsResult(unsafe_extension_ids);
243 break;
244 }
245 default:
246 NOTREACHED();
247 }
248 } else {
249 NOTREACHED();
250 }
251 }
252
253 SafeBrowsingDatabaseManager::SafeBrowsingDatabaseManager(
254 const scoped_refptr<SafeBrowsingService>& service)
255 : sb_service_(service),
256 database_(NULL),
257 enabled_(false),
258 enable_download_protection_(false),
259 enable_csd_whitelist_(false),
260 enable_download_whitelist_(false),
261 enable_extension_blacklist_(false),
262 enable_ip_blacklist_(false),
263 enable_unwanted_software_blacklist_(false),
264 update_in_progress_(false),
265 database_update_in_progress_(false),
266 closing_database_(false),
267 check_timeout_(base::TimeDelta::FromMilliseconds(kCheckTimeoutMs)) {
268 DCHECK_CURRENTLY_ON(BrowserThread::UI);
269 DCHECK(sb_service_.get() != NULL);
270
271 // Android only supports a subset of FULL_SAFE_BROWSING.
272 // TODO(shess): This shouldn't be OS-driven <http://crbug.com/394379>
273 #if !defined(OS_ANDROID)
274 base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
275 enable_download_protection_ =
276 !cmdline->HasSwitch(switches::kSbDisableDownloadProtection);
277
278 // We only download the csd-whitelist if client-side phishing detection is
279 // enabled.
280 enable_csd_whitelist_ =
281 !cmdline->HasSwitch(switches::kDisableClientSidePhishingDetection);
282
283 // TODO(noelutz): remove this boolean variable since it should always be true
284 // if SafeBrowsing is enabled. Unfortunately, we have no test data for this
285 // list right now. This means that we need to be able to disable this list
286 // for the SafeBrowsing test to pass.
287 enable_download_whitelist_ = enable_csd_whitelist_;
288
289 // TODO(kalman): there really shouldn't be a flag for this.
290 enable_extension_blacklist_ =
291 !cmdline->HasSwitch(switches::kSbDisableExtensionBlacklist);
292
293 // The client-side IP blacklist feature is tightly integrated with client-side
294 // phishing protection for now.
295 enable_ip_blacklist_ = enable_csd_whitelist_;
296
297 // The UwS blacklist feature is controlled by a flag for M40.
298 enable_unwanted_software_blacklist_ =
299 safe_browsing_util::GetUnwantedTrialGroup() > safe_browsing_util::UWS_OFF;
300 #endif
301 }
302
303 SafeBrowsingDatabaseManager::~SafeBrowsingDatabaseManager() {
304 // The DCHECK is disabled due to crbug.com/438754.
305 // DCHECK_CURRENTLY_ON(BrowserThread::UI);
306
307 // We should have already been shut down. If we're still enabled, then the
308 // database isn't going to be closed properly, which could lead to corruption.
309 DCHECK(!enabled_);
310 }
311
312 bool SafeBrowsingDatabaseManager::CanCheckUrl(const GURL& url) const {
313 return url.SchemeIs(url::kFtpScheme) ||
314 url.SchemeIs(url::kHttpScheme) ||
315 url.SchemeIs(url::kHttpsScheme);
316 }
317
318 bool SafeBrowsingDatabaseManager::CheckDownloadUrl(
319 const std::vector<GURL>& url_chain,
320 Client* client) {
321 DCHECK_CURRENTLY_ON(BrowserThread::IO);
322 if (!enabled_ || !enable_download_protection_)
323 return true;
324
325 // We need to check the database for url prefix, and later may fetch the url
326 // from the safebrowsing backends. These need to be asynchronous.
327 SafeBrowsingCheck* check =
328 new SafeBrowsingCheck(url_chain,
329 std::vector<SBFullHash>(),
330 client,
331 safe_browsing_util::BINURL,
332 std::vector<SBThreatType>(1,
333 SB_THREAT_TYPE_BINARY_MALWARE_URL));
334 std::vector<SBPrefix> prefixes;
335 SafeBrowsingDatabase::GetDownloadUrlPrefixes(url_chain, &prefixes);
336 StartSafeBrowsingCheck(
337 check,
338 base::Bind(&SafeBrowsingDatabaseManager::CheckDownloadUrlOnSBThread, this,
339 prefixes));
340 return false;
341 }
342
343 bool SafeBrowsingDatabaseManager::CheckExtensionIDs(
344 const std::set<std::string>& extension_ids,
345 Client* client) {
346 DCHECK_CURRENTLY_ON(BrowserThread::IO);
347
348 if (!enabled_ || !enable_extension_blacklist_)
349 return true;
350
351 std::vector<SBFullHash> extension_id_hashes;
352 std::transform(extension_ids.begin(), extension_ids.end(),
353 std::back_inserter(extension_id_hashes),
354 safe_browsing_util::StringToSBFullHash);
355 std::vector<SBPrefix> prefixes;
356 for (const SBFullHash& hash : extension_id_hashes)
357 prefixes.push_back(hash.prefix);
358
359 SafeBrowsingCheck* check = new SafeBrowsingCheck(
360 std::vector<GURL>(),
361 extension_id_hashes,
362 client,
363 safe_browsing_util::EXTENSIONBLACKLIST,
364 std::vector<SBThreatType>(1, SB_THREAT_TYPE_EXTENSION));
365 StartSafeBrowsingCheck(
366 check,
367 base::Bind(&SafeBrowsingDatabaseManager::CheckExtensionIDsOnSBThread,
368 this, prefixes));
369 return false;
370 }
371
372 bool SafeBrowsingDatabaseManager::MatchMalwareIP(
373 const std::string& ip_address) {
374 DCHECK_CURRENTLY_ON(BrowserThread::IO);
375 if (!enabled_ || !enable_ip_blacklist_ || !MakeDatabaseAvailable()) {
376 return false; // Fail open.
377 }
378 return database_->ContainsMalwareIP(ip_address);
379 }
380
381 bool SafeBrowsingDatabaseManager::MatchCsdWhitelistUrl(const GURL& url) {
382 DCHECK_CURRENTLY_ON(BrowserThread::IO);
383 if (!enabled_ || !enable_csd_whitelist_ || !MakeDatabaseAvailable()) {
384 // There is something funky going on here -- for example, perhaps the user
385 // has not restarted since enabling metrics reporting, so we haven't
386 // enabled the csd whitelist yet. Just to be safe we return true in this
387 // case.
388 return true;
389 }
390 return database_->ContainsCsdWhitelistedUrl(url);
391 }
392
393 bool SafeBrowsingDatabaseManager::MatchDownloadWhitelistUrl(const GURL& url) {
394 DCHECK_CURRENTLY_ON(BrowserThread::IO);
395 if (!enabled_ || !enable_download_whitelist_ || !MakeDatabaseAvailable()) {
396 return true;
397 }
398 return database_->ContainsDownloadWhitelistedUrl(url);
399 }
400
401 bool SafeBrowsingDatabaseManager::MatchDownloadWhitelistString(
402 const std::string& str) {
403 DCHECK_CURRENTLY_ON(BrowserThread::IO);
404 if (!enabled_ || !enable_download_whitelist_ || !MakeDatabaseAvailable()) {
405 return true;
406 }
407 return database_->ContainsDownloadWhitelistedString(str);
408 }
409
410 bool SafeBrowsingDatabaseManager::MatchInclusionWhitelistUrl(const GURL& url) {
411 DCHECK_CURRENTLY_ON(BrowserThread::IO);
412 if (!enabled_ || !MakeDatabaseAvailable())
413 return true;
414 return database_->ContainsInclusionWhitelistedUrl(url);
415 }
416
417 bool SafeBrowsingDatabaseManager::IsMalwareKillSwitchOn() {
418 DCHECK_CURRENTLY_ON(BrowserThread::IO);
419 if (!enabled_ || !MakeDatabaseAvailable()) {
420 return true;
421 }
422 return database_->IsMalwareIPMatchKillSwitchOn();
423 }
424
425 bool SafeBrowsingDatabaseManager::IsCsdWhitelistKillSwitchOn() {
426 DCHECK_CURRENTLY_ON(BrowserThread::IO);
427 if (!enabled_ || !MakeDatabaseAvailable()) {
428 return true;
429 }
430 return database_->IsCsdWhitelistKillSwitchOn();
431 }
432
433 bool SafeBrowsingDatabaseManager::CheckBrowseUrl(const GURL& url,
434 Client* client) {
435 DCHECK_CURRENTLY_ON(BrowserThread::IO);
436 if (!enabled_)
437 return true;
438
439 if (!CanCheckUrl(url))
440 return true;
441
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);
446
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;
456 }
457
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;
463
464 std::vector<SBPrefix> browse_prefix_hits;
465 bool browse_prefix_match = database_->ContainsBrowseUrl(
466 url, &browse_prefix_hits, &cache_hits);
467
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);
472
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());
485
486 UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::TimeTicks::Now() - start);
487
488 if (!browse_prefix_match && !unwanted_prefix_match)
489 return true; // URL is okay.
490
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);
506
507 BrowserThread::PostTask(
508 BrowserThread::IO, FROM_HERE,
509 base::Bind(&SafeBrowsingDatabaseManager::OnCheckDone, this, check));
510
511 return false;
512 }
513
514 void SafeBrowsingDatabaseManager::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;
522 }
523
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;
534 }
535 }
536
537 void SafeBrowsingDatabaseManager::HandleGetHashResults(
538 SafeBrowsingCheck* check,
539 const std::vector<SBFullHashResult>& full_hashes,
540 const base::TimeDelta& cache_lifetime) {
541 DCHECK_CURRENTLY_ON(BrowserThread::IO);
542
543 if (!enabled_)
544 return;
545
546 // If the service has been shut down, |check| should have been deleted.
547 DCHECK(checks_.find(check) != checks_.end());
548
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);
554
555 std::vector<SBPrefix> prefixes = check->prefix_hits;
556 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
557
558 // Cache the GetHash results.
559 if (cache_lifetime != base::TimeDelta() && MakeDatabaseAvailable())
560 database_->CacheHashResults(prefixes, full_hashes, cache_lifetime);
561 }
562
563 void SafeBrowsingDatabaseManager::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(&SafeBrowsingDatabaseManager::GetAllChunksFromDatabase, this,
570 callback));
571 }
572
573 void SafeBrowsingDatabaseManager::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, base::Bind(&SafeBrowsingDatabaseManager::AddDatabaseChunks,
582 this, list, base::Passed(&chunks), callback));
583 }
584
585 void SafeBrowsingDatabaseManager::DeleteChunks(
586 scoped_ptr<std::vector<SBChunkDelete> > chunk_deletes) {
587 DCHECK_CURRENTLY_ON(BrowserThread::IO);
588 DCHECK(enabled_);
589 safe_browsing_task_runner_->PostTask(
590 FROM_HERE, base::Bind(&SafeBrowsingDatabaseManager::DeleteDatabaseChunks,
591 this, base::Passed(&chunk_deletes)));
592 }
593
594 void SafeBrowsingDatabaseManager::UpdateStarted() {
595 DCHECK_CURRENTLY_ON(BrowserThread::IO);
596 DCHECK(enabled_);
597 DCHECK(!update_in_progress_);
598 update_in_progress_ = true;
599 }
600
601 void SafeBrowsingDatabaseManager::UpdateFinished(bool update_succeeded) {
602 DCHECK_CURRENTLY_ON(BrowserThread::IO);
603 DCHECK(enabled_);
604 if (update_in_progress_) {
605 update_in_progress_ = false;
606 safe_browsing_task_runner_->PostTask(
607 FROM_HERE,
608 base::Bind(&SafeBrowsingDatabaseManager::DatabaseUpdateFinished, this,
609 update_succeeded));
610 }
611 }
612
613 void SafeBrowsingDatabaseManager::ResetDatabase() {
614 DCHECK_CURRENTLY_ON(BrowserThread::IO);
615 DCHECK(enabled_);
616 safe_browsing_task_runner_->PostTask(
617 FROM_HERE,
618 base::Bind(&SafeBrowsingDatabaseManager::OnResetDatabase, this));
619 }
620
621 void SafeBrowsingDatabaseManager::StartOnIOThread() {
622 DCHECK_CURRENTLY_ON(BrowserThread::IO);
623 if (enabled_)
624 return;
625
626 // Only get a new task runner if there isn't one already. If the service has
627 // previously been started and stopped, a task runner could already exist.
628 if (!safe_browsing_task_runner_) {
629 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
630 safe_browsing_task_runner_ =
631 pool->GetSequencedTaskRunnerWithShutdownBehavior(
632 pool->GetSequenceToken(),
633 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
634 }
635
636 enabled_ = true;
637
638 MakeDatabaseAvailable();
639 }
640
641 void SafeBrowsingDatabaseManager::StopOnIOThread(bool shutdown) {
642 DCHECK_CURRENTLY_ON(BrowserThread::IO);
643
644 DoStopOnIOThread();
645 if (shutdown) {
646 sb_service_ = NULL;
647 }
648 }
649
650 void SafeBrowsingDatabaseManager::NotifyDatabaseUpdateFinished(
651 bool update_succeeded) {
652 DCHECK_CURRENTLY_ON(BrowserThread::UI);
653 content::NotificationService::current()->Notify(
654 chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
655 content::Source<SafeBrowsingDatabaseManager>(this),
656 content::Details<bool>(&update_succeeded));
657 }
658
659 SafeBrowsingDatabaseManager::QueuedCheck::QueuedCheck(
660 const safe_browsing_util::ListType check_type,
661 Client* client,
662 const GURL& url,
663 const std::vector<SBThreatType>& expected_threats,
664 const base::TimeTicks& start)
665 : check_type(check_type),
666 client(client),
667 url(url),
668 expected_threats(expected_threats),
669 start(start) {
670 }
671
672 SafeBrowsingDatabaseManager::QueuedCheck::~QueuedCheck() {
673 }
674
675 void SafeBrowsingDatabaseManager::DoStopOnIOThread() {
676 DCHECK_CURRENTLY_ON(BrowserThread::IO);
677
678 if (!enabled_)
679 return;
680
681 enabled_ = false;
682
683 // Delete queued checks, calling back any clients with 'SB_THREAT_TYPE_SAFE'.
684 while (!queued_checks_.empty()) {
685 QueuedCheck queued = queued_checks_.front();
686 if (queued.client) {
687 SafeBrowsingCheck sb_check(std::vector<GURL>(1, queued.url),
688 std::vector<SBFullHash>(),
689 queued.client,
690 queued.check_type,
691 queued.expected_threats);
692 queued.client->OnSafeBrowsingResult(sb_check);
693 }
694 queued_checks_.pop_front();
695 }
696
697 // Close the database. Cases to avoid:
698 // * If |closing_database_| is true, continuing will queue up a second
699 // request, |closing_database_| will be reset after handling the first
700 // request, and if any functions on the db thread recreate the database, we
701 // could start using it on the IO thread and then have the second request
702 // handler delete it out from under us.
703 // * If |database_| is NULL, then either no creation request is in flight, in
704 // which case we don't need to do anything, or one is in flight, in which
705 // case the database will be recreated before our deletion request is
706 // handled, and could be used on the IO thread in that time period, leading
707 // to the same problem as above.
708 // Checking DatabaseAvailable() avoids both of these.
709 if (DatabaseAvailable()) {
710 closing_database_ = true;
711 safe_browsing_task_runner_->PostTask(
712 FROM_HERE,
713 base::Bind(&SafeBrowsingDatabaseManager::OnCloseDatabase, this));
714 }
715
716 // Delete pending checks, calling back any clients with 'SB_THREAT_TYPE_SAFE'.
717 // We have to do this after the db thread returns because methods on it can
718 // have copies of these pointers, so deleting them might lead to accessing
719 // garbage.
720 for (CurrentChecks::iterator it = checks_.begin();
721 it != checks_.end(); ++it) {
722 SafeBrowsingCheck* check = *it;
723 if (check->client)
724 check->client->OnSafeBrowsingResult(*check);
725 }
726 STLDeleteElements(&checks_);
727
728 gethash_requests_.clear();
729 }
730
731 bool SafeBrowsingDatabaseManager::DatabaseAvailable() const {
732 base::AutoLock lock(database_lock_);
733 return !closing_database_ && (database_ != NULL);
734 }
735
736 bool SafeBrowsingDatabaseManager::MakeDatabaseAvailable() {
737 DCHECK_CURRENTLY_ON(BrowserThread::IO);
738 DCHECK(enabled_);
739 if (DatabaseAvailable())
740 return true;
741 safe_browsing_task_runner_->PostTask(
742 FROM_HERE,
743 base::Bind(base::IgnoreResult(&SafeBrowsingDatabaseManager::GetDatabase),
744 this));
745 return false;
746 }
747
748 SafeBrowsingDatabase* SafeBrowsingDatabaseManager::GetDatabase() {
749 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
750
751 if (database_)
752 return database_;
753 startup_metric_utils::ScopedSlowStartupUMA
754 scoped_timer("Startup.SlowStartupSafeBrowsingGetDatabase");
755 const base::TimeTicks before = base::TimeTicks::Now();
756
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_);
762
763 database->Init(SafeBrowsingService::GetBaseFilename());
764 {
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;
769 }
770
771 BrowserThread::PostTask(
772 BrowserThread::IO, FROM_HERE,
773 base::Bind(&SafeBrowsingDatabaseManager::DatabaseLoadComplete, this));
774
775 UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", base::TimeTicks::Now() - before);
776 return database_;
777 }
778
779 void SafeBrowsingDatabaseManager::OnCheckDone(SafeBrowsingCheck* check) {
780 DCHECK_CURRENTLY_ON(BrowserThread::IO);
781
782 if (!enabled_)
783 return;
784
785 // If the service has been shut down, |check| should have been deleted.
786 DCHECK(checks_.find(check) != checks_.end());
787
788 if (check->client && check->need_get_hash) {
789 // We have a partial match so we need to query Google for the full hash.
790 // Clean up will happen in HandleGetHashResults.
791
792 // See if we have a GetHash request already in progress for this particular
793 // prefix. If so, we just append ourselves to the list of interested parties
794 // when the results arrive. We only do this for checks involving one prefix,
795 // since that is the common case (multiple prefixes will issue the request
796 // as normal).
797 if (check->prefix_hits.size() == 1) {
798 SBPrefix prefix = check->prefix_hits[0];
799 GetHashRequests::iterator it = gethash_requests_.find(prefix);
800 if (it != gethash_requests_.end()) {
801 // There's already a request in progress.
802 it->second.push_back(check);
803 return;
804 }
805
806 // No request in progress, so we're the first for this prefix.
807 GetHashRequestors requestors;
808 requestors.push_back(check);
809 gethash_requests_[prefix] = requestors;
810 }
811
812 // Reset the start time so that we can measure the network time without the
813 // database time.
814 check->start = base::TimeTicks::Now();
815 // Note: If |this| is deleted or stopped, the protocol_manager will
816 // be destroyed as well - hence it's OK to do unretained in this case.
817 bool is_download = check->check_type == safe_browsing_util::BINURL;
818 sb_service_->protocol_manager()->GetFullHash(
819 check->prefix_hits,
820 base::Bind(&SafeBrowsingDatabaseManager::HandleGetHashResults,
821 base::Unretained(this),
822 check),
823 is_download);
824 } else {
825 // We may have cached results for previous GetHash queries. Since
826 // this data comes from cache, don't histogram hits.
827 HandleOneCheck(check, check->cache_hits);
828 }
829 }
830
831 void SafeBrowsingDatabaseManager::GetAllChunksFromDatabase(
832 GetChunksCallback callback) {
833 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
834
835 bool database_error = true;
836 std::vector<SBListChunkRanges> lists;
837 DCHECK(!database_update_in_progress_);
838 database_update_in_progress_ = true;
839 GetDatabase(); // This guarantees that |database_| is non-NULL.
840 if (database_->UpdateStarted(&lists)) {
841 database_error = false;
842 } else {
843 database_->UpdateFinished(false);
844 }
845
846 BrowserThread::PostTask(
847 BrowserThread::IO, FROM_HERE,
848 base::Bind(&SafeBrowsingDatabaseManager::OnGetAllChunksFromDatabase,
849 this, lists, database_error, callback));
850 }
851
852 void SafeBrowsingDatabaseManager::OnGetAllChunksFromDatabase(
853 const std::vector<SBListChunkRanges>& lists, bool database_error,
854 GetChunksCallback callback) {
855 DCHECK_CURRENTLY_ON(BrowserThread::IO);
856 if (enabled_)
857 callback.Run(lists, database_error);
858 }
859
860 void SafeBrowsingDatabaseManager::OnAddChunksComplete(
861 AddChunksCallback callback) {
862 DCHECK_CURRENTLY_ON(BrowserThread::IO);
863 if (enabled_)
864 callback.Run();
865 }
866
867 void SafeBrowsingDatabaseManager::DatabaseLoadComplete() {
868 DCHECK_CURRENTLY_ON(BrowserThread::IO);
869 if (!enabled_)
870 return;
871
872 LOCAL_HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
873 if (queued_checks_.empty())
874 return;
875
876 // If the database isn't already available, calling CheckUrl() in the loop
877 // below will add the check back to the queue, and we'll infinite-loop.
878 DCHECK(DatabaseAvailable());
879 while (!queued_checks_.empty()) {
880 QueuedCheck check = queued_checks_.front();
881 DCHECK(!check.start.is_null());
882 LOCAL_HISTOGRAM_TIMES("SB.QueueDelay",
883 base::TimeTicks::Now() - check.start);
884 // If CheckUrl() determines the URL is safe immediately, it doesn't call the
885 // client's handler function (because normally it's being directly called by
886 // the client). Since we're not the client, we have to convey this result.
887 if (check.client && CheckBrowseUrl(check.url, check.client)) {
888 SafeBrowsingCheck sb_check(std::vector<GURL>(1, check.url),
889 std::vector<SBFullHash>(),
890 check.client,
891 check.check_type,
892 check.expected_threats);
893 check.client->OnSafeBrowsingResult(sb_check);
894 }
895 queued_checks_.pop_front();
896 }
897 }
898
899 void SafeBrowsingDatabaseManager::AddDatabaseChunks(
900 const std::string& list_name,
901 scoped_ptr<ScopedVector<SBChunkData> > chunks,
902 AddChunksCallback callback) {
903 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
904 if (chunks)
905 GetDatabase()->InsertChunks(list_name, chunks->get());
906 BrowserThread::PostTask(
907 BrowserThread::IO, FROM_HERE,
908 base::Bind(&SafeBrowsingDatabaseManager::OnAddChunksComplete, this,
909 callback));
910 }
911
912 void SafeBrowsingDatabaseManager::DeleteDatabaseChunks(
913 scoped_ptr<std::vector<SBChunkDelete> > chunk_deletes) {
914 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
915 if (chunk_deletes)
916 GetDatabase()->DeleteChunks(*chunk_deletes);
917 }
918
919 void SafeBrowsingDatabaseManager::DatabaseUpdateFinished(
920 bool update_succeeded) {
921 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
922 GetDatabase()->UpdateFinished(update_succeeded);
923 DCHECK(database_update_in_progress_);
924 database_update_in_progress_ = false;
925 BrowserThread::PostTask(
926 BrowserThread::UI, FROM_HERE,
927 base::Bind(&SafeBrowsingDatabaseManager::NotifyDatabaseUpdateFinished,
928 this, update_succeeded));
929 }
930
931 void SafeBrowsingDatabaseManager::OnCloseDatabase() {
932 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
933 DCHECK(closing_database_);
934
935 // Because |closing_database_| is true, nothing on the IO thread will be
936 // accessing the database, so it's safe to delete and then NULL the pointer.
937 delete database_;
938 database_ = NULL;
939
940 // Acquiring the lock here guarantees correct ordering between the resetting
941 // of |database_| above and of |closing_database_| below, which ensures there
942 // won't be a window during which the IO thread falsely believes the database
943 // is available.
944 base::AutoLock lock(database_lock_);
945 closing_database_ = false;
946 }
947
948 void SafeBrowsingDatabaseManager::OnResetDatabase() {
949 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
950
951 GetDatabase()->ResetDatabase();
952 }
953
954 void SafeBrowsingDatabaseManager::OnHandleGetHashResults(
955 SafeBrowsingCheck* check,
956 const std::vector<SBFullHashResult>& full_hashes) {
957 DCHECK_CURRENTLY_ON(BrowserThread::IO);
958 safe_browsing_util::ListType check_type = check->check_type;
959 SBPrefix prefix = check->prefix_hits[0];
960 GetHashRequests::iterator it = gethash_requests_.find(prefix);
961 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
962 const bool hit = HandleOneCheck(check, full_hashes);
963 RecordGetHashCheckStatus(hit, check_type, full_hashes);
964 return;
965 }
966
967 // Call back all interested parties, noting if any has a hit.
968 GetHashRequestors& requestors = it->second;
969 bool hit = false;
970 for (GetHashRequestors::iterator r = requestors.begin();
971 r != requestors.end(); ++r) {
972 if (HandleOneCheck(*r, full_hashes))
973 hit = true;
974 }
975 RecordGetHashCheckStatus(hit, check_type, full_hashes);
976
977 gethash_requests_.erase(it);
978 }
979
980 bool SafeBrowsingDatabaseManager::HandleOneCheck(
981 SafeBrowsingCheck* check,
982 const std::vector<SBFullHashResult>& full_hashes) {
983 DCHECK_CURRENTLY_ON(BrowserThread::IO);
984 DCHECK(check);
985
986 bool is_threat = false;
987
988 // TODO(shess): GetHashSeverestThreadListType() contains a loop,
989 // GetUrlSeverestThreatListType() a loop around that loop. Having another
990 // loop out here concerns me. It is likely that SAFE is an expected outcome,
991 // which means all of those loops run to completion. Refactoring this to
992 // generate a set of sorted items to compare in sequence would probably
993 // improve things.
994 //
995 // Additionally, the set of patterns generated from the urls is very similar
996 // to the patterns generated in ContainsBrowseUrl() and other database checks,
997 // which are called from this code. Refactoring that across the checks could
998 // interact well with batching the checks here.
999
1000 // TODO(gab): Fix the fact that Get(Url|Hash)SeverestThreatType() may return a
1001 // threat for which IsExpectedThreat() returns false even if |full_hashes|
1002 // actually contains an expected threat.
1003
1004 for (size_t i = 0; i < check->urls.size(); ++i) {
1005 size_t threat_index;
1006 SBThreatType threat =
1007 GetUrlSeverestThreatType(check->urls[i], full_hashes, &threat_index);
1008 if (threat != SB_THREAT_TYPE_SAFE &&
1009 IsExpectedThreat(threat, check->expected_threats)) {
1010 check->url_results[i] = threat;
1011 check->url_metadata[i] = full_hashes[threat_index].metadata;
1012 is_threat = true;
1013 }
1014 }
1015
1016 for (size_t i = 0; i < check->full_hashes.size(); ++i) {
1017 SBThreatType threat =
1018 GetHashSeverestThreatType(check->full_hashes[i], full_hashes);
1019 if (threat != SB_THREAT_TYPE_SAFE &&
1020 IsExpectedThreat(threat, check->expected_threats)) {
1021 check->full_hash_results[i] = threat;
1022 is_threat = true;
1023 }
1024 }
1025
1026 SafeBrowsingCheckDone(check);
1027 return is_threat;
1028 }
1029
1030 void SafeBrowsingDatabaseManager::OnAsyncCheckDone(
1031 SafeBrowsingCheck* check,
1032 const std::vector<SBPrefix>& prefix_hits) {
1033 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1034 DCHECK(enable_download_protection_);
1035
1036 check->prefix_hits = prefix_hits;
1037 if (check->prefix_hits.empty()) {
1038 SafeBrowsingCheckDone(check);
1039 } else {
1040 check->need_get_hash = true;
1041 OnCheckDone(check);
1042 }
1043 }
1044
1045 std::vector<SBPrefix> SafeBrowsingDatabaseManager::CheckDownloadUrlOnSBThread(
1046 const std::vector<SBPrefix>& prefixes) {
1047 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
1048 DCHECK(enable_download_protection_);
1049
1050 std::vector<SBPrefix> prefix_hits;
1051 const bool result =
1052 database_->ContainsDownloadUrlPrefixes(prefixes, &prefix_hits);
1053 DCHECK_EQ(result, !prefix_hits.empty());
1054 return prefix_hits;
1055 }
1056
1057 std::vector<SBPrefix> SafeBrowsingDatabaseManager::CheckExtensionIDsOnSBThread(
1058 const std::vector<SBPrefix>& prefixes) {
1059 DCHECK(safe_browsing_task_runner_->RunsTasksOnCurrentThread());
1060
1061 std::vector<SBPrefix> prefix_hits;
1062 const bool result =
1063 database_->ContainsExtensionPrefixes(prefixes, &prefix_hits);
1064 DCHECK_EQ(result, !prefix_hits.empty());
1065 return prefix_hits;
1066 }
1067
1068 void SafeBrowsingDatabaseManager::TimeoutCallback(SafeBrowsingCheck* check) {
1069 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1070 DCHECK(check);
1071
1072 if (!enabled_)
1073 return;
1074
1075 DCHECK(checks_.find(check) != checks_.end());
1076 if (check->client) {
1077 check->client->OnSafeBrowsingResult(*check);
1078 check->client = NULL;
1079 }
1080 }
1081
1082 void SafeBrowsingDatabaseManager::SafeBrowsingCheckDone(
1083 SafeBrowsingCheck* check) {
1084 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1085 DCHECK(check);
1086
1087 if (!enabled_)
1088 return;
1089
1090 DVLOG(1) << "SafeBrowsingCheckDone";
1091 DCHECK(checks_.find(check) != checks_.end());
1092 if (check->client)
1093 check->client->OnSafeBrowsingResult(*check);
1094 checks_.erase(check);
1095 delete check;
1096 }
1097
1098 void SafeBrowsingDatabaseManager::StartSafeBrowsingCheck(
1099 SafeBrowsingCheck* check,
1100 const base::Callback<std::vector<SBPrefix>(void)>& task) {
1101 DCHECK_CURRENTLY_ON(BrowserThread::IO);
1102 check->weak_ptr_factory_.reset(
1103 new base::WeakPtrFactory<SafeBrowsingDatabaseManager>(this));
1104 checks_.insert(check);
1105
1106 base::PostTaskAndReplyWithResult(
1107 safe_browsing_task_runner_.get(), FROM_HERE, task,
1108 base::Bind(&SafeBrowsingDatabaseManager::OnAsyncCheckDone,
1109 check->weak_ptr_factory_->GetWeakPtr(), check));
1110 base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
1111 base::Bind(&SafeBrowsingDatabaseManager::TimeoutCallback,
1112 check->weak_ptr_factory_->GetWeakPtr(), check),
1113 check_timeout_);
1114 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698