• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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