blob: dfcfaa02e9404d442bda57fa43b35f4bcce33e7c [file] [log] [blame]
[email protected]4d9ae4a2009-10-01 17:59:381// Copyright (c) 2009 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294//
5
6#include "chrome/browser/safe_browsing/safe_browsing_service.h"
7
[email protected]2041cf342010-02-19 03:15:598#include "base/callback.h"
[email protected]894c4e82010-06-29 21:53:189#include "base/command_line.h"
initial.commit09911bf2008-07-26 23:55:2910#include "base/path_service.h"
11#include "base/string_util.h"
12#include "chrome/browser/browser_process.h"
[email protected]975bead2009-11-30 21:59:5313#include "chrome/browser/metrics/metrics_service.h"
[email protected]052313b2010-02-19 09:43:0814#include "chrome/browser/pref_service.h"
initial.commit09911bf2008-07-26 23:55:2915#include "chrome/browser/profile_manager.h"
[email protected]51065cb2009-02-19 00:25:2316#include "chrome/browser/safe_browsing/protocol_manager.h"
[email protected]5b7c7d3c2009-02-19 00:06:2217#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
initial.commit09911bf2008-07-26 23:55:2918#include "chrome/browser/safe_browsing/safe_browsing_database.h"
[email protected]f3ec7742009-01-15 00:59:1619#include "chrome/browser/tab_contents/tab_util.h"
[email protected]57c6a652009-05-04 07:58:3420#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]554147532010-06-15 01:51:1421#include "chrome/browser/chrome_thread.h"
initial.commit09911bf2008-07-26 23:55:2922#include "chrome/common/chrome_constants.h"
23#include "chrome/common/chrome_paths.h"
[email protected]894c4e82010-06-29 21:53:1824#include "chrome/common/chrome_switches.h"
[email protected]68d2a05f2010-05-07 21:39:5525#include "chrome/common/net/url_request_context_getter.h"
initial.commit09911bf2008-07-26 23:55:2926#include "chrome/common/pref_names.h"
[email protected]dcf7d352009-02-26 01:56:0227#include "chrome/common/url_constants.h"
[email protected]90a3b242009-11-13 00:28:5428#include "net/base/registry_controlled_domain.h"
29
[email protected]1a871512009-11-06 06:11:1830#if defined(OS_WIN)
31#include "chrome/installer/util/browser_distribution.h"
32#endif
initial.commit09911bf2008-07-26 23:55:2933
[email protected]e1acf6f2008-10-27 20:43:3334using base::Time;
35using base::TimeDelta;
36
[email protected]894c4e82010-06-29 21:53:1837// The default URL prefix where browser fetches chunk updates, hashes,
38// and reports malware.
39static const char* const kSbDefaultInfoURLPrefix =
40 "http://safebrowsing.clients.google.com/safebrowsing";
41
42// The default URL prefix where browser fetches MAC client key.
43static const char* const kSbDefaultMacKeyURLPrefix =
44 "https://sb-ssl.google.com/safebrowsing";
45
[email protected]d11f5662009-11-12 20:52:5646static Profile* GetDefaultProfile() {
47 FilePath user_data_dir;
48 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
49 ProfileManager* profile_manager = g_browser_process->profile_manager();
50 return profile_manager->GetDefaultProfile(user_data_dir);
51}
52
initial.commit09911bf2008-07-26 23:55:2953SafeBrowsingService::SafeBrowsingService()
[email protected]d83d03aa2009-11-02 21:44:3754 : database_(NULL),
initial.commit09911bf2008-07-26 23:55:2955 protocol_manager_(NULL),
56 enabled_(false),
[email protected]cb2a67e2009-11-17 00:48:5357 update_in_progress_(false),
58 closing_database_(false) {
initial.commit09911bf2008-07-26 23:55:2959}
60
[email protected]d83d03aa2009-11-02 21:44:3761void SafeBrowsingService::Initialize() {
initial.commit09911bf2008-07-26 23:55:2962 // Get the profile's preference for SafeBrowsing.
[email protected]d11f5662009-11-12 20:52:5663 PrefService* pref_service = GetDefaultProfile()->GetPrefs();
initial.commit09911bf2008-07-26 23:55:2964 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
65 Start();
66}
67
initial.commit09911bf2008-07-26 23:55:2968void SafeBrowsingService::ShutDown() {
[email protected]d83d03aa2009-11-02 21:44:3769 ChromeThread::PostTask(
70 ChromeThread::IO, FROM_HERE,
71 NewRunnableMethod(this, &SafeBrowsingService::OnIOShutdown));
initial.commit09911bf2008-07-26 23:55:2972}
73
[email protected]90a3b242009-11-13 00:28:5474bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
[email protected]14aab332010-06-02 14:35:4875 return url.SchemeIs(chrome::kFtpScheme) ||
76 url.SchemeIs(chrome::kHttpScheme) ||
[email protected]90a3b242009-11-13 00:28:5477 url.SchemeIs(chrome::kHttpsScheme);
78}
79
80bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
81 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]dec173b2009-11-13 21:53:2682 if (!enabled_)
[email protected]90a3b242009-11-13 00:28:5483 return true;
84
[email protected]14aab332010-06-02 14:35:4885 if (!CanCheckUrl(url))
86 return true;
87
[email protected]cb2a67e2009-11-17 00:48:5388 if (!MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:5489 QueuedCheck check;
90 check.client = client;
91 check.url = url;
92 queued_checks_.push_back(check);
93 return false;
94 }
95
96 std::string list;
97 std::vector<SBPrefix> prefix_hits;
98 std::vector<SBFullHashResult> full_hits;
99 base::Time check_start = base::Time::Now();
100 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
101 &full_hits,
102 protocol_manager_->last_update());
103
104 UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::Time::Now() - check_start);
105
106 if (!prefix_match)
107 return true; // URL is okay.
108
109 // Needs to be asynchronous, since we could be in the constructor of a
110 // ResourceDispatcherHost event handler which can't pause there.
111 SafeBrowsingCheck* check = new SafeBrowsingCheck();
112 check->url = url;
113 check->client = client;
114 check->result = URL_SAFE;
115 check->need_get_hash = full_hits.empty();
116 check->prefix_hits.swap(prefix_hits);
117 check->full_hits.swap(full_hits);
118 checks_.insert(check);
119
120 ChromeThread::PostTask(
121 ChromeThread::IO, FROM_HERE,
122 NewRunnableMethod(this, &SafeBrowsingService::OnCheckDone, check));
123
124 return false;
125}
126
127void SafeBrowsingService::CancelCheck(Client* client) {
128 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]90a3b242009-11-13 00:28:54129 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
[email protected]03addd92009-11-17 21:38:10130 // We can't delete matching checks here because the db thread has a copy of
131 // the pointer. Instead, we simply NULL out the client, and when the db
132 // thread calls us back, we'll clean up the check.
[email protected]90a3b242009-11-13 00:28:54133 if ((*i)->client == client)
134 (*i)->client = NULL;
135 }
136
137 // Scan the queued clients store. Clients may be here if they requested a URL
[email protected]fd220e602009-11-14 01:02:37138 // check before the database has finished loading.
[email protected]dec173b2009-11-13 21:53:26139 for (std::deque<QueuedCheck>::iterator it(queued_checks_.begin());
[email protected]03addd92009-11-17 21:38:10140 it != queued_checks_.end(); ) {
141 // In this case it's safe to delete matches entirely since nothing has a
142 // pointer to them.
[email protected]dec173b2009-11-13 21:53:26143 if (it->client == client)
[email protected]03addd92009-11-17 21:38:10144 it = queued_checks_.erase(it);
145 else
146 ++it;
[email protected]90a3b242009-11-13 00:28:54147 }
148}
149
150void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
151 ResourceType::Type resource_type,
152 UrlCheckResult result,
153 Client* client,
154 int render_process_host_id,
155 int render_view_id) {
[email protected]cb2a67e2009-11-17 00:48:53156 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
157
[email protected]90a3b242009-11-13 00:28:54158 // Check if the user has already ignored our warning for this render_view
159 // and domain.
160 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
161 const WhiteListedEntry& entry = white_listed_entries_[i];
162 if (entry.render_process_host_id == render_process_host_id &&
163 entry.render_view_id == render_view_id &&
164 entry.result == result &&
165 entry.domain ==
166 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
167 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
168 this, &SafeBrowsingService::NotifyClientBlockingComplete,
169 client, true));
170 return;
171 }
172 }
173
174 UnsafeResource resource;
175 resource.url = url;
176 resource.resource_type = resource_type;
177 resource.threat_type= result;
178 resource.client = client;
179 resource.render_process_host_id = render_process_host_id;
180 resource.render_view_id = render_view_id;
181
182 // The blocking page must be created from the UI thread.
183 ChromeThread::PostTask(
184 ChromeThread::UI, FROM_HERE,
185 NewRunnableMethod(
186 this, &SafeBrowsingService::DoDisplayBlockingPage, resource));
187}
188
189void SafeBrowsingService::HandleGetHashResults(
190 SafeBrowsingCheck* check,
191 const std::vector<SBFullHashResult>& full_hashes,
192 bool can_cache) {
[email protected]cb2a67e2009-11-17 00:48:53193 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]90a3b242009-11-13 00:28:54194 if (checks_.find(check) == checks_.end())
195 return;
196
197 DCHECK(enabled_);
198
199 UMA_HISTOGRAM_LONG_TIMES("SB2.Network", Time::Now() - check->start);
200
201 std::vector<SBPrefix> prefixes = check->prefix_hits;
202 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
203
[email protected]cb2a67e2009-11-17 00:48:53204 if (can_cache && MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:54205 // Cache the GetHash results in memory:
206 database_->CacheHashResults(prefixes, full_hashes);
207 }
208}
209
210void SafeBrowsingService::HandleChunk(const std::string& list,
[email protected]7b1e37102010-03-08 21:43:16211 SBChunkList* chunks) {
[email protected]90a3b242009-11-13 00:28:54212 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
213 DCHECK(enabled_);
214 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
215 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
216}
217
218void SafeBrowsingService::HandleChunkDelete(
219 std::vector<SBChunkDelete>* chunk_deletes) {
220 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
221 DCHECK(enabled_);
222 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
223 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
224}
225
226void SafeBrowsingService::UpdateStarted() {
227 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
228 DCHECK(enabled_);
229 DCHECK(!update_in_progress_);
230 update_in_progress_ = true;
231 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
232 this, &SafeBrowsingService::GetAllChunksFromDatabase));
233}
234
235void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
236 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
237 DCHECK(enabled_);
238 if (update_in_progress_) {
239 update_in_progress_ = false;
240 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
241 NewRunnableMethod(this,
242 &SafeBrowsingService::DatabaseUpdateFinished,
243 update_succeeded));
244 }
245}
246
[email protected]894c4e82010-06-29 21:53:18247bool SafeBrowsingService::IsUpdateInProgress() const {
248 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
249 return update_in_progress_;
250}
251
[email protected]90a3b242009-11-13 00:28:54252void SafeBrowsingService::OnBlockingPageDone(
253 const std::vector<UnsafeResource>& resources,
254 bool proceed) {
255 for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
256 iter != resources.end(); ++iter) {
257 const UnsafeResource& resource = *iter;
258 NotifyClientBlockingComplete(resource.client, proceed);
259
260 if (proceed) {
261 // Whitelist this domain and warning type for the given tab.
262 WhiteListedEntry entry;
263 entry.render_process_host_id = resource.render_process_host_id;
264 entry.render_view_id = resource.render_view_id;
265 entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
266 resource.url);
267 entry.result = resource.threat_type;
268 white_listed_entries_.push_back(entry);
269 }
270 }
271}
272
273void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
274 const std::string& wrapped_key) {
275 PrefService* prefs = g_browser_process->local_state();
276 if (prefs) {
[email protected]ddd231e2010-06-29 20:35:19277 prefs->SetString(prefs::kSafeBrowsingClientKey, client_key);
278 prefs->SetString(prefs::kSafeBrowsingWrappedKey, wrapped_key);
[email protected]90a3b242009-11-13 00:28:54279 }
280}
281
282void SafeBrowsingService::OnEnable(bool enabled) {
283 if (enabled)
284 Start();
285 else
286 ShutDown();
287}
288
289// static
290void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
[email protected]20ce516d2010-06-18 02:20:04291 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, "");
292 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, "");
[email protected]90a3b242009-11-13 00:28:54293}
294
[email protected]cb2a67e2009-11-17 00:48:53295void SafeBrowsingService::CloseDatabase() {
296 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
297
[email protected]03addd92009-11-17 21:38:10298 // Cases to avoid:
299 // * If |closing_database_| is true, continuing will queue up a second
300 // request, |closing_database_| will be reset after handling the first
301 // request, and if any functions on the db thread recreate the database, we
302 // could start using it on the IO thread and then have the second request
303 // handler delete it out from under us.
304 // * If |database_| is NULL, then either no creation request is in flight, in
305 // which case we don't need to do anything, or one is in flight, in which
306 // case the database will be recreated before our deletion request is
307 // handled, and could be used on the IO thread in that time period, leading
308 // to the same problem as above.
309 // * If |queued_checks_| is non-empty and |database_| is non-NULL, we're
310 // about to be called back (in DatabaseLoadComplete()). This will call
311 // CheckUrl(), which will want the database. Closing the database here
312 // would lead to an infinite loop in DatabaseLoadComplete(), and even if it
313 // didn't, it would be pointless since we'd just want to recreate.
314 //
[email protected]34094312009-12-03 21:40:26315 // The first two cases above are handled by checking DatabaseAvailable().
316 if (!DatabaseAvailable() || !queued_checks_.empty())
[email protected]cb2a67e2009-11-17 00:48:53317 return;
318
319 closing_database_ = true;
320 if (safe_browsing_thread_.get()) {
321 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
322 NewRunnableMethod(this, &SafeBrowsingService::OnCloseDatabase));
323 }
324}
325
[email protected]90a3b242009-11-13 00:28:54326void SafeBrowsingService::ResetDatabase() {
327 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53328 DCHECK(enabled_);
[email protected]90a3b242009-11-13 00:28:54329 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
330 this, &SafeBrowsingService::OnResetDatabase));
331}
332
333void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
334 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
335}
336
337SafeBrowsingService::~SafeBrowsingService() {
[email protected]cb2a67e2009-11-17 00:48:53338 // We should have already been shut down. If we're still enabled, then the
339 // database isn't going to be closed properly, which could lead to corruption.
340 DCHECK(!enabled_);
[email protected]90a3b242009-11-13 00:28:54341}
342
[email protected]d11f5662009-11-12 20:52:56343void SafeBrowsingService::OnIOInitialize(
344 const std::string& client_key,
345 const std::string& wrapped_key,
346 URLRequestContextGetter* request_context_getter) {
[email protected]d83d03aa2009-11-02 21:44:37347 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29348 enabled_ = true;
[email protected]cb2a67e2009-11-17 00:48:53349 MakeDatabaseAvailable();
[email protected]1a871512009-11-06 06:11:18350
351 // On Windows, get the safe browsing client name from the browser
352 // distribution classes in installer util. These classes don't yet have
353 // an analog on non-Windows builds so just keep the name specified here.
354#if defined(OS_WIN)
355 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
356 std::string client_name(dist->GetSafeBrowsingName());
357#else
358#if defined(GOOGLE_CHROME_BUILD)
359 std::string client_name("googlechrome");
360#else
361 std::string client_name("chromium");
362#endif
363#endif
[email protected]894c4e82010-06-29 21:53:18364 CommandLine* cmdline = CommandLine::ForCurrentProcess();
365 bool disable_auto_update = cmdline->HasSwitch(switches::kSbDisableAutoUpdate);
366 std::string info_url_prefix =
367 cmdline->HasSwitch(switches::kSbInfoURLPrefix) ?
368 cmdline->GetSwitchValueASCII(switches::kSbInfoURLPrefix) :
369 kSbDefaultInfoURLPrefix;
370 std::string mackey_url_prefix =
371 cmdline->HasSwitch(switches::kSbMacKeyURLPrefix) ?
372 cmdline->GetSwitchValueASCII(switches::kSbMacKeyURLPrefix) :
373 kSbDefaultMacKeyURLPrefix;
[email protected]1a871512009-11-06 06:11:18374
initial.commit09911bf2008-07-26 23:55:29375 protocol_manager_ = new SafeBrowsingProtocolManager(this,
[email protected]1a871512009-11-06 06:11:18376 client_name,
initial.commit09911bf2008-07-26 23:55:29377 client_key,
[email protected]d11f5662009-11-12 20:52:56378 wrapped_key,
[email protected]894c4e82010-06-29 21:53:18379 request_context_getter,
380 info_url_prefix,
381 mackey_url_prefix,
382 disable_auto_update);
[email protected]d11f5662009-11-12 20:52:56383
384 // Balance the reference added by Start().
385 request_context_getter->Release();
386
[email protected]dec173b2009-11-13 21:53:26387 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29388}
389
initial.commit09911bf2008-07-26 23:55:29390void SafeBrowsingService::OnIOShutdown() {
[email protected]d83d03aa2009-11-02 21:44:37391 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29392 if (!enabled_)
393 return;
394
395 enabled_ = false;
initial.commit09911bf2008-07-26 23:55:29396
397 // This cancels all in-flight GetHash requests.
398 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04399 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29400
[email protected]03addd92009-11-17 21:38:10401 // Delete queued checks, calling back any clients with 'URL_SAFE'.
402 // If we don't do this here we may fail to close the database below.
403 while (!queued_checks_.empty()) {
404 QueuedCheck check = queued_checks_.front();
405 if (check.client)
406 check.client->OnUrlCheckResult(check.url, URL_SAFE);
407 queued_checks_.pop_front();
408 }
409
[email protected]cb2a67e2009-11-17 00:48:53410 // Close the database. We don't simply DeleteSoon() because if a close is
411 // already pending, we'll double-free, and we don't set |database_| to NULL
412 // because if there is still anything running on the db thread, it could
413 // create a new database object (via GetDatabase()) that would then leak.
414 CloseDatabase();
initial.commit09911bf2008-07-26 23:55:29415
416 // Flush the database thread. Any in-progress database check results will be
417 // ignored and cleaned up below.
[email protected]cb2a67e2009-11-17 00:48:53418 //
419 // Note that to avoid leaking the database, we rely on the fact that no new
420 // tasks will be added to the db thread between the call above and this one.
421 // See comments on the declaration of |safe_browsing_thread_|.
422 safe_browsing_thread_.reset();
initial.commit09911bf2008-07-26 23:55:29423
[email protected]03addd92009-11-17 21:38:10424 // Delete pending checks, calling back any clients with 'URL_SAFE'. We have
425 // to do this after the db thread returns because methods on it can have
426 // copies of these pointers, so deleting them might lead to accessing garbage.
initial.commit09911bf2008-07-26 23:55:29427 for (CurrentChecks::iterator it = checks_.begin();
428 it != checks_.end(); ++it) {
429 if ((*it)->client)
430 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
431 delete *it;
432 }
433 checks_.clear();
434
435 gethash_requests_.clear();
436}
437
[email protected]34094312009-12-03 21:40:26438bool SafeBrowsingService::DatabaseAvailable() const {
439 AutoLock lock(database_lock_);
440 return !closing_database_ && (database_ != NULL);
441}
442
[email protected]cb2a67e2009-11-17 00:48:53443bool SafeBrowsingService::MakeDatabaseAvailable() {
444 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
445 DCHECK(enabled_);
[email protected]34094312009-12-03 21:40:26446 if (DatabaseAvailable())
447 return true;
448 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
449 NewRunnableMethod(this, &SafeBrowsingService::GetDatabase));
450 return false;
[email protected]cb2a67e2009-11-17 00:48:53451}
452
[email protected]90a3b242009-11-13 00:28:54453SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
454 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
455 if (database_)
456 return database_;
initial.commit09911bf2008-07-26 23:55:29457
[email protected]90a3b242009-11-13 00:28:54458 FilePath path;
459 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
460 DCHECK(result);
461 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29462
[email protected]90a3b242009-11-13 00:28:54463 Time before = Time::Now();
464 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
[email protected]7b1e37102010-03-08 21:43:16465 database->Init(path);
[email protected]34094312009-12-03 21:40:26466 {
467 // Acquiring the lock here guarantees correct ordering between the writes to
468 // the new database object above, and the setting of |databse_| below.
469 AutoLock lock(database_lock_);
470 database_ = database;
471 }
[email protected]613a03b2008-10-24 23:02:00472
[email protected]d83d03aa2009-11-02 21:44:37473 ChromeThread::PostTask(
474 ChromeThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54475 NewRunnableMethod(this, &SafeBrowsingService::DatabaseLoadComplete));
[email protected]613a03b2008-10-24 23:02:00476
[email protected]cb2a67e2009-11-17 00:48:53477 UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", Time::Now() - before);
[email protected]90a3b242009-11-13 00:28:54478 return database_;
initial.commit09911bf2008-07-26 23:55:29479}
480
[email protected]613a03b2008-10-24 23:02:00481void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
[email protected]d83d03aa2009-11-02 21:44:37482 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29483
484 // If we've been shutdown during the database lookup, this check will already
485 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00486 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29487 return;
488
[email protected]613a03b2008-10-24 23:02:00489 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29490 // We have a partial match so we need to query Google for the full hash.
491 // Clean up will happen in HandleGetHashResults.
492
493 // See if we have a GetHash request already in progress for this particular
494 // prefix. If so, we just append ourselves to the list of interested parties
495 // when the results arrive. We only do this for checks involving one prefix,
496 // since that is the common case (multiple prefixes will issue the request
497 // as normal).
[email protected]613a03b2008-10-24 23:02:00498 if (check->prefix_hits.size() == 1) {
499 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29500 GetHashRequests::iterator it = gethash_requests_.find(prefix);
501 if (it != gethash_requests_.end()) {
502 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00503 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29504 return;
505 }
506
507 // No request in progress, so we're the first for this prefix.
508 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00509 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29510 gethash_requests_[prefix] = requestors;
511 }
512
513 // Reset the start time so that we can measure the network time without the
514 // database time.
[email protected]613a03b2008-10-24 23:02:00515 check->start = Time::Now();
516 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29517 } else {
518 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00519 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29520 }
521}
522
[email protected]90a3b242009-11-13 00:28:54523void SafeBrowsingService::GetAllChunksFromDatabase() {
[email protected]f3724ea2009-05-08 22:09:56524 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54525 bool database_error = true;
526 std::vector<SBListChunkRanges> lists;
[email protected]cb2a67e2009-11-17 00:48:53527 GetDatabase(); // This guarantees that |database_| is non-NULL.
528 if (database_->UpdateStarted()) {
529 database_->GetListsInfo(&lists);
530 database_error = false;
531 } else {
532 database_->UpdateFinished(false);
[email protected]90a3b242009-11-13 00:28:54533 }
[email protected]613a03b2008-10-24 23:02:00534
[email protected]d83d03aa2009-11-02 21:44:37535 ChromeThread::PostTask(
536 ChromeThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54537 NewRunnableMethod(
538 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
539 database_error));
initial.commit09911bf2008-07-26 23:55:29540}
541
[email protected]90a3b242009-11-13 00:28:54542void SafeBrowsingService::OnGetAllChunksFromDatabase(
543 const std::vector<SBListChunkRanges>& lists, bool database_error) {
544 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
545 if (enabled_)
546 protocol_manager_->OnGetChunksComplete(lists, database_error);
547}
548
[email protected]90a3b242009-11-13 00:28:54549void SafeBrowsingService::OnChunkInserted() {
550 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
551 if (enabled_)
552 protocol_manager_->OnChunkInserted();
553}
554
555void SafeBrowsingService::DatabaseLoadComplete() {
556 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
557 if (!enabled_)
initial.commit09911bf2008-07-26 23:55:29558 return;
559
[email protected]03addd92009-11-17 21:38:10560 HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
561 if (queued_checks_.empty())
562 return;
563
564 // If the database isn't already available, calling CheckUrl() in the loop
565 // below will add the check back to the queue, and we'll infinite-loop.
[email protected]34094312009-12-03 21:40:26566 DCHECK(DatabaseAvailable());
[email protected]03addd92009-11-17 21:38:10567 while (!queued_checks_.empty()) {
568 QueuedCheck check = queued_checks_.front();
569 HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
570 // If CheckUrl() determines the URL is safe immediately, it doesn't call the
571 // client's handler function (because normally it's being directly called by
572 // the client). Since we're not the client, we have to convey this result.
573 if (check.client && CheckUrl(check.url, check.client))
574 check.client->OnUrlCheckResult(check.url, URL_SAFE);
575 queued_checks_.pop_front();
576 }
[email protected]90a3b242009-11-13 00:28:54577}
initial.commit09911bf2008-07-26 23:55:29578
[email protected]90a3b242009-11-13 00:28:54579void SafeBrowsingService::HandleChunkForDatabase(
580 const std::string& list_name,
[email protected]7b1e37102010-03-08 21:43:16581 SBChunkList* chunks) {
[email protected]90a3b242009-11-13 00:28:54582 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16583 if (chunks) {
584 GetDatabase()->InsertChunks(list_name, *chunks);
585 delete chunks;
586 }
587 ChromeThread::PostTask(
588 ChromeThread::IO, FROM_HERE,
589 NewRunnableMethod(this, &SafeBrowsingService::OnChunkInserted));
[email protected]90a3b242009-11-13 00:28:54590}
591
592void SafeBrowsingService::DeleteChunks(
593 std::vector<SBChunkDelete>* chunk_deletes) {
594 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16595 if (chunk_deletes) {
596 GetDatabase()->DeleteChunks(*chunk_deletes);
597 delete chunk_deletes;
598 }
[email protected]90a3b242009-11-13 00:28:54599}
600
601SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
602 const std::string& list_name) {
603 if (safe_browsing_util::IsPhishingList(list_name)) {
604 return URL_PHISHING;
[email protected]613a03b2008-10-24 23:02:00605 }
[email protected]90a3b242009-11-13 00:28:54606
607 if (safe_browsing_util::IsMalwareList(list_name)) {
608 return URL_MALWARE;
609 }
610
611 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
612 return URL_SAFE;
613}
614
615void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
616 bool proceed) {
617 client->OnBlockingPageComplete(proceed);
618}
619
620void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
621 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]cb2a67e2009-11-17 00:48:53622 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]90a3b242009-11-13 00:28:54623}
624
625void SafeBrowsingService::Start() {
626 DCHECK(!safe_browsing_thread_.get());
627 safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
628 if (!safe_browsing_thread_->Start())
629 return;
630
631 // Retrieve client MAC keys.
632 PrefService* local_state = g_browser_process->local_state();
633 std::string client_key, wrapped_key;
634 if (local_state) {
635 client_key =
[email protected]ddd231e2010-06-29 20:35:19636 local_state->GetString(prefs::kSafeBrowsingClientKey);
[email protected]90a3b242009-11-13 00:28:54637 wrapped_key =
[email protected]ddd231e2010-06-29 20:35:19638 local_state->GetString(prefs::kSafeBrowsingWrappedKey);
[email protected]90a3b242009-11-13 00:28:54639 }
640
641 // We will issue network fetches using the default profile's request context.
642 URLRequestContextGetter* request_context_getter =
643 GetDefaultProfile()->GetRequestContext();
644 request_context_getter->AddRef(); // Balanced in OnIOInitialize.
645
646 ChromeThread::PostTask(
647 ChromeThread::IO, FROM_HERE,
648 NewRunnableMethod(
649 this, &SafeBrowsingService::OnIOInitialize, client_key, wrapped_key,
650 request_context_getter));
[email protected]cb2a67e2009-11-17 00:48:53651}
[email protected]90a3b242009-11-13 00:28:54652
[email protected]cb2a67e2009-11-17 00:48:53653void SafeBrowsingService::OnCloseDatabase() {
654 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]a3aaf3d2009-11-17 22:12:39655 DCHECK(closing_database_);
[email protected]cb2a67e2009-11-17 00:48:53656
657 // Because |closing_database_| is true, nothing on the IO thread will be
658 // accessing the database, so it's safe to delete and then NULL the pointer.
659 delete database_;
660 database_ = NULL;
[email protected]34094312009-12-03 21:40:26661
662 // Acquiring the lock here guarantees correct ordering between the resetting
663 // of |database_| above and of |closing_database_| below, which ensures there
664 // won't be a window during which the IO thread falsely believes the database
665 // is available.
666 AutoLock lock(database_lock_);
[email protected]cb2a67e2009-11-17 00:48:53667 closing_database_ = false;
[email protected]90a3b242009-11-13 00:28:54668}
669
670void SafeBrowsingService::OnResetDatabase() {
671 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
672 GetDatabase()->ResetDatabase();
[email protected]90a3b242009-11-13 00:28:54673}
674
675void SafeBrowsingService::CacheHashResults(
676 const std::vector<SBPrefix>& prefixes,
677 const std::vector<SBFullHashResult>& full_hashes) {
678 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
679 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29680}
681
682void SafeBrowsingService::OnHandleGetHashResults(
683 SafeBrowsingCheck* check,
684 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]cb2a67e2009-11-17 00:48:53685 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29686 SBPrefix prefix = check->prefix_hits[0];
687 GetHashRequests::iterator it = gethash_requests_.find(prefix);
688 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
689 HandleOneCheck(check, full_hashes);
690 return;
691 }
692
693 // Call back all interested parties.
694 GetHashRequestors& requestors = it->second;
695 for (GetHashRequestors::iterator r = requestors.begin();
696 r != requestors.end(); ++r) {
697 HandleOneCheck(*r, full_hashes);
698 }
699
700 gethash_requests_.erase(it);
701}
702
703void SafeBrowsingService::HandleOneCheck(
704 SafeBrowsingCheck* check,
705 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]cb2a67e2009-11-17 00:48:53706 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29707 if (check->client) {
708 UrlCheckResult result = URL_SAFE;
709 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
[email protected]09d985f2009-05-11 21:59:58710 if (index != -1) {
initial.commit09911bf2008-07-26 23:55:29711 result = GetResultFromListname(full_hashes[index].list_name);
[email protected]09d985f2009-05-11 21:59:58712 } else {
713 // Log the case where the SafeBrowsing servers return full hashes in the
714 // GetHash response that match the prefix we're looking up, but don't
715 // match the full hash of the URL.
716 if (!full_hashes.empty())
717 UMA_HISTOGRAM_COUNTS("SB2.GetHashServerMiss", 1);
718 }
initial.commit09911bf2008-07-26 23:55:29719
720 // Let the client continue handling the original request.
721 check->client->OnUrlCheckResult(check->url, result);
722 }
723
724 checks_.erase(check);
725 delete check;
726}
727
[email protected]90a3b242009-11-13 00:28:54728void SafeBrowsingService::DoDisplayBlockingPage(
729 const UnsafeResource& resource) {
730 // The tab might have been closed.
731 TabContents* wc =
732 tab_util::GetTabContentsByID(resource.render_process_host_id,
733 resource.render_view_id);
initial.commit09911bf2008-07-26 23:55:29734
[email protected]90a3b242009-11-13 00:28:54735 if (!wc) {
736 // The tab is gone and we did not have a chance at showing the interstitial.
737 // Just act as "Don't Proceed" was chosen.
738 std::vector<UnsafeResource> resources;
739 resources.push_back(resource);
740 ChromeThread::PostTask(
[email protected]d83d03aa2009-11-02 21:44:37741 ChromeThread::IO, FROM_HERE,
742 NewRunnableMethod(
[email protected]90a3b242009-11-13 00:28:54743 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
744 return;
initial.commit09911bf2008-07-26 23:55:29745 }
746
[email protected]90a3b242009-11-13 00:28:54747 // Report the malware sub-resource to the SafeBrowsing servers if we have a
748 // malware sub-resource on a safe page and only if the user has opted in to
749 // reporting statistics.
[email protected]975bead2009-11-30 21:59:53750 const MetricsService* metrics = g_browser_process->metrics_service();
751 DCHECK(metrics);
752 if (metrics && metrics->reporting_active() &&
[email protected]90a3b242009-11-13 00:28:54753 resource.threat_type == SafeBrowsingService::URL_MALWARE) {
754 GURL page_url = wc->GetURL();
755 GURL referrer_url;
756 NavigationEntry* entry = wc->controller().GetActiveEntry();
757 if (entry)
758 referrer_url = entry->referrer();
[email protected]ee4d3e802010-08-12 23:59:51759
760 if (resource.url != page_url || !referrer_url.is_empty()) {
761 bool is_subresource = resource.resource_type != ResourceType::MAIN_FRAME;
762 ChromeThread::PostTask(
763 ChromeThread::IO, FROM_HERE,
764 NewRunnableMethod(this,
765 &SafeBrowsingService::ReportMalware,
766 resource.url,
767 page_url,
768 referrer_url,
769 is_subresource));
770 }
initial.commit09911bf2008-07-26 23:55:29771 }
772
[email protected]90a3b242009-11-13 00:28:54773 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
[email protected]613a03b2008-10-24 23:02:00774}
775
[email protected]dfdb0de72009-02-19 21:58:14776void SafeBrowsingService::ReportMalware(const GURL& malware_url,
777 const GURL& page_url,
[email protected]ee4d3e802010-08-12 23:59:51778 const GURL& referrer_url,
779 bool is_subresource) {
[email protected]d83d03aa2009-11-02 21:44:37780 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]dfdb0de72009-02-19 21:58:14781
[email protected]fd220e602009-11-14 01:02:37782 if (!enabled_)
[email protected]dfdb0de72009-02-19 21:58:14783 return;
784
[email protected]34094312009-12-03 21:40:26785 if (DatabaseAvailable()) {
[email protected]fd220e602009-11-14 01:02:37786 // Check if 'page_url' is already blacklisted (exists in our cache). Only
[email protected]ee4d3e802010-08-12 23:59:51787 // report if it's not there. This can happen if the user has ignored
788 // the warning for page_url and is now hitting a warning for a resource.
[email protected]fd220e602009-11-14 01:02:37789 std::string list;
790 std::vector<SBPrefix> prefix_hits;
791 std::vector<SBFullHashResult> full_hits;
792 database_->ContainsUrl(page_url, &list, &prefix_hits, &full_hits,
793 protocol_manager_->last_update());
794 if (!full_hits.empty())
795 return;
796 }
[email protected]dfdb0de72009-02-19 21:58:14797
[email protected]ee4d3e802010-08-12 23:59:51798 protocol_manager_->ReportMalware(malware_url, page_url, referrer_url,
799 is_subresource);
[email protected]dfdb0de72009-02-19 21:58:14800}