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