blob: 6f8619cf89841761d89905a65eb6d58d7261e3a6 [file] [log] [blame]
[email protected]c23161342010-08-18 20:34:041// Copyright (c) 2010 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]017a7a112010-10-12 16:38:2713#include "chrome/browser/browser_thread.h"
[email protected]975bead2009-11-30 21:59:5314#include "chrome/browser/metrics/metrics_service.h"
[email protected]37858e52010-08-26 00:22:0215#include "chrome/browser/prefs/pref_service.h"
initial.commit09911bf2008-07-26 23:55:2916#include "chrome/browser/profile_manager.h"
[email protected]51065cb2009-02-19 00:25:2317#include "chrome/browser/safe_browsing/protocol_manager.h"
[email protected]5b7c7d3c2009-02-19 00:06:2218#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
initial.commit09911bf2008-07-26 23:55:2919#include "chrome/browser/safe_browsing/safe_browsing_database.h"
[email protected]f3ec7742009-01-15 00:59:1620#include "chrome/browser/tab_contents/tab_util.h"
[email protected]57c6a652009-05-04 07:58:3421#include "chrome/browser/tab_contents/tab_contents.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
[email protected]93aa89c72010-10-20 21:32:0453struct SafeBrowsingService::WhiteListedEntry {
54 int render_process_host_id;
55 int render_view_id;
56 std::string domain;
57 UrlCheckResult result;
58};
59
initial.commit09911bf2008-07-26 23:55:2960SafeBrowsingService::SafeBrowsingService()
[email protected]d83d03aa2009-11-02 21:44:3761 : database_(NULL),
initial.commit09911bf2008-07-26 23:55:2962 protocol_manager_(NULL),
63 enabled_(false),
[email protected]cb2a67e2009-11-17 00:48:5364 update_in_progress_(false),
[email protected]ef5b4822010-09-09 16:45:4965 database_update_in_progress_(false),
[email protected]cb2a67e2009-11-17 00:48:5366 closing_database_(false) {
initial.commit09911bf2008-07-26 23:55:2967}
68
[email protected]d83d03aa2009-11-02 21:44:3769void SafeBrowsingService::Initialize() {
initial.commit09911bf2008-07-26 23:55:2970 // Get the profile's preference for SafeBrowsing.
[email protected]d11f5662009-11-12 20:52:5671 PrefService* pref_service = GetDefaultProfile()->GetPrefs();
initial.commit09911bf2008-07-26 23:55:2972 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
73 Start();
74}
75
initial.commit09911bf2008-07-26 23:55:2976void SafeBrowsingService::ShutDown() {
[email protected]4d3b42a2010-10-07 05:00:2977 BrowserThread::PostTask(
78 BrowserThread::IO, FROM_HERE,
[email protected]d83d03aa2009-11-02 21:44:3779 NewRunnableMethod(this, &SafeBrowsingService::OnIOShutdown));
initial.commit09911bf2008-07-26 23:55:2980}
81
[email protected]90a3b242009-11-13 00:28:5482bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
[email protected]14aab332010-06-02 14:35:4883 return url.SchemeIs(chrome::kFtpScheme) ||
84 url.SchemeIs(chrome::kHttpScheme) ||
[email protected]90a3b242009-11-13 00:28:5485 url.SchemeIs(chrome::kHttpsScheme);
86}
87
88bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
[email protected]4d3b42a2010-10-07 05:00:2989 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]dec173b2009-11-13 21:53:2690 if (!enabled_)
[email protected]90a3b242009-11-13 00:28:5491 return true;
92
[email protected]14aab332010-06-02 14:35:4893 if (!CanCheckUrl(url))
94 return true;
95
[email protected]cb2a67e2009-11-17 00:48:5396 if (!MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:5497 QueuedCheck check;
98 check.client = client;
99 check.url = url;
100 queued_checks_.push_back(check);
101 return false;
102 }
103
104 std::string list;
105 std::vector<SBPrefix> prefix_hits;
106 std::vector<SBFullHashResult> full_hits;
107 base::Time check_start = base::Time::Now();
108 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
109 &full_hits,
110 protocol_manager_->last_update());
111
112 UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::Time::Now() - check_start);
113
114 if (!prefix_match)
115 return true; // URL is okay.
116
117 // Needs to be asynchronous, since we could be in the constructor of a
118 // ResourceDispatcherHost event handler which can't pause there.
119 SafeBrowsingCheck* check = new SafeBrowsingCheck();
120 check->url = url;
121 check->client = client;
122 check->result = URL_SAFE;
123 check->need_get_hash = full_hits.empty();
124 check->prefix_hits.swap(prefix_hits);
125 check->full_hits.swap(full_hits);
126 checks_.insert(check);
127
[email protected]4d3b42a2010-10-07 05:00:29128 BrowserThread::PostTask(
129 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54130 NewRunnableMethod(this, &SafeBrowsingService::OnCheckDone, check));
131
132 return false;
133}
134
135void SafeBrowsingService::CancelCheck(Client* client) {
[email protected]4d3b42a2010-10-07 05:00:29136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54137 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
[email protected]03addd92009-11-17 21:38:10138 // We can't delete matching checks here because the db thread has a copy of
139 // the pointer. Instead, we simply NULL out the client, and when the db
140 // thread calls us back, we'll clean up the check.
[email protected]90a3b242009-11-13 00:28:54141 if ((*i)->client == client)
142 (*i)->client = NULL;
143 }
144
145 // Scan the queued clients store. Clients may be here if they requested a URL
[email protected]fd220e602009-11-14 01:02:37146 // check before the database has finished loading.
[email protected]dec173b2009-11-13 21:53:26147 for (std::deque<QueuedCheck>::iterator it(queued_checks_.begin());
[email protected]03addd92009-11-17 21:38:10148 it != queued_checks_.end(); ) {
149 // In this case it's safe to delete matches entirely since nothing has a
150 // pointer to them.
[email protected]dec173b2009-11-13 21:53:26151 if (it->client == client)
[email protected]03addd92009-11-17 21:38:10152 it = queued_checks_.erase(it);
153 else
154 ++it;
[email protected]90a3b242009-11-13 00:28:54155 }
156}
157
158void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
[email protected]293f19252010-08-18 20:01:18159 const GURL& original_url,
[email protected]90a3b242009-11-13 00:28:54160 ResourceType::Type resource_type,
161 UrlCheckResult result,
162 Client* client,
163 int render_process_host_id,
164 int render_view_id) {
[email protected]4d3b42a2010-10-07 05:00:29165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53166
[email protected]90a3b242009-11-13 00:28:54167 // Check if the user has already ignored our warning for this render_view
168 // and domain.
169 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
170 const WhiteListedEntry& entry = white_listed_entries_[i];
171 if (entry.render_process_host_id == render_process_host_id &&
172 entry.render_view_id == render_view_id &&
173 entry.result == result &&
174 entry.domain ==
175 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
176 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
177 this, &SafeBrowsingService::NotifyClientBlockingComplete,
178 client, true));
179 return;
180 }
181 }
182
183 UnsafeResource resource;
184 resource.url = url;
[email protected]293f19252010-08-18 20:01:18185 resource.original_url = original_url;
[email protected]90a3b242009-11-13 00:28:54186 resource.resource_type = resource_type;
187 resource.threat_type= result;
188 resource.client = client;
189 resource.render_process_host_id = render_process_host_id;
190 resource.render_view_id = render_view_id;
191
192 // The blocking page must be created from the UI thread.
[email protected]4d3b42a2010-10-07 05:00:29193 BrowserThread::PostTask(
194 BrowserThread::UI, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54195 NewRunnableMethod(
196 this, &SafeBrowsingService::DoDisplayBlockingPage, resource));
197}
198
199void SafeBrowsingService::HandleGetHashResults(
200 SafeBrowsingCheck* check,
201 const std::vector<SBFullHashResult>& full_hashes,
202 bool can_cache) {
[email protected]4d3b42a2010-10-07 05:00:29203 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54204 if (checks_.find(check) == checks_.end())
205 return;
206
207 DCHECK(enabled_);
208
209 UMA_HISTOGRAM_LONG_TIMES("SB2.Network", Time::Now() - check->start);
210
211 std::vector<SBPrefix> prefixes = check->prefix_hits;
212 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
213
[email protected]cb2a67e2009-11-17 00:48:53214 if (can_cache && MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:54215 // Cache the GetHash results in memory:
216 database_->CacheHashResults(prefixes, full_hashes);
217 }
218}
219
220void SafeBrowsingService::HandleChunk(const std::string& list,
[email protected]7b1e37102010-03-08 21:43:16221 SBChunkList* chunks) {
[email protected]4d3b42a2010-10-07 05:00:29222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54223 DCHECK(enabled_);
224 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
225 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
226}
227
228void SafeBrowsingService::HandleChunkDelete(
229 std::vector<SBChunkDelete>* chunk_deletes) {
[email protected]4d3b42a2010-10-07 05:00:29230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54231 DCHECK(enabled_);
232 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
233 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
234}
235
236void SafeBrowsingService::UpdateStarted() {
[email protected]4d3b42a2010-10-07 05:00:29237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54238 DCHECK(enabled_);
239 DCHECK(!update_in_progress_);
240 update_in_progress_ = true;
241 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
242 this, &SafeBrowsingService::GetAllChunksFromDatabase));
243}
244
245void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
[email protected]4d3b42a2010-10-07 05:00:29246 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54247 DCHECK(enabled_);
248 if (update_in_progress_) {
249 update_in_progress_ = false;
250 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
251 NewRunnableMethod(this,
252 &SafeBrowsingService::DatabaseUpdateFinished,
253 update_succeeded));
254 }
255}
256
[email protected]894c4e82010-06-29 21:53:18257bool SafeBrowsingService::IsUpdateInProgress() const {
[email protected]4d3b42a2010-10-07 05:00:29258 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]894c4e82010-06-29 21:53:18259 return update_in_progress_;
260}
261
[email protected]90a3b242009-11-13 00:28:54262void SafeBrowsingService::OnBlockingPageDone(
263 const std::vector<UnsafeResource>& resources,
264 bool proceed) {
265 for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
266 iter != resources.end(); ++iter) {
267 const UnsafeResource& resource = *iter;
268 NotifyClientBlockingComplete(resource.client, proceed);
269
270 if (proceed) {
271 // Whitelist this domain and warning type for the given tab.
272 WhiteListedEntry entry;
273 entry.render_process_host_id = resource.render_process_host_id;
274 entry.render_view_id = resource.render_view_id;
275 entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
276 resource.url);
277 entry.result = resource.threat_type;
278 white_listed_entries_.push_back(entry);
279 }
280 }
281}
282
283void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
284 const std::string& wrapped_key) {
285 PrefService* prefs = g_browser_process->local_state();
286 if (prefs) {
[email protected]ddd231e2010-06-29 20:35:19287 prefs->SetString(prefs::kSafeBrowsingClientKey, client_key);
288 prefs->SetString(prefs::kSafeBrowsingWrappedKey, wrapped_key);
[email protected]90a3b242009-11-13 00:28:54289 }
290}
291
292void SafeBrowsingService::OnEnable(bool enabled) {
293 if (enabled)
294 Start();
295 else
296 ShutDown();
297}
298
299// static
300void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
[email protected]20ce516d2010-06-18 02:20:04301 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, "");
302 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, "");
[email protected]90a3b242009-11-13 00:28:54303}
304
[email protected]cb2a67e2009-11-17 00:48:53305void SafeBrowsingService::CloseDatabase() {
[email protected]4d3b42a2010-10-07 05:00:29306 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53307
[email protected]03addd92009-11-17 21:38:10308 // Cases to avoid:
309 // * If |closing_database_| is true, continuing will queue up a second
310 // request, |closing_database_| will be reset after handling the first
311 // request, and if any functions on the db thread recreate the database, we
312 // could start using it on the IO thread and then have the second request
313 // handler delete it out from under us.
314 // * If |database_| is NULL, then either no creation request is in flight, in
315 // which case we don't need to do anything, or one is in flight, in which
316 // case the database will be recreated before our deletion request is
317 // handled, and could be used on the IO thread in that time period, leading
318 // to the same problem as above.
319 // * If |queued_checks_| is non-empty and |database_| is non-NULL, we're
320 // about to be called back (in DatabaseLoadComplete()). This will call
321 // CheckUrl(), which will want the database. Closing the database here
322 // would lead to an infinite loop in DatabaseLoadComplete(), and even if it
323 // didn't, it would be pointless since we'd just want to recreate.
324 //
[email protected]34094312009-12-03 21:40:26325 // The first two cases above are handled by checking DatabaseAvailable().
326 if (!DatabaseAvailable() || !queued_checks_.empty())
[email protected]cb2a67e2009-11-17 00:48:53327 return;
328
329 closing_database_ = true;
330 if (safe_browsing_thread_.get()) {
331 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
332 NewRunnableMethod(this, &SafeBrowsingService::OnCloseDatabase));
333 }
334}
335
[email protected]90a3b242009-11-13 00:28:54336void SafeBrowsingService::ResetDatabase() {
[email protected]4d3b42a2010-10-07 05:00:29337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53338 DCHECK(enabled_);
[email protected]90a3b242009-11-13 00:28:54339 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
340 this, &SafeBrowsingService::OnResetDatabase));
341}
342
343void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
344 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
345}
346
347SafeBrowsingService::~SafeBrowsingService() {
[email protected]cb2a67e2009-11-17 00:48:53348 // We should have already been shut down. If we're still enabled, then the
349 // database isn't going to be closed properly, which could lead to corruption.
350 DCHECK(!enabled_);
[email protected]90a3b242009-11-13 00:28:54351}
352
[email protected]d11f5662009-11-12 20:52:56353void SafeBrowsingService::OnIOInitialize(
354 const std::string& client_key,
355 const std::string& wrapped_key,
356 URLRequestContextGetter* request_context_getter) {
[email protected]4d3b42a2010-10-07 05:00:29357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29358 enabled_ = true;
[email protected]cb2a67e2009-11-17 00:48:53359 MakeDatabaseAvailable();
[email protected]1a871512009-11-06 06:11:18360
361 // On Windows, get the safe browsing client name from the browser
362 // distribution classes in installer util. These classes don't yet have
363 // an analog on non-Windows builds so just keep the name specified here.
364#if defined(OS_WIN)
365 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
366 std::string client_name(dist->GetSafeBrowsingName());
367#else
368#if defined(GOOGLE_CHROME_BUILD)
369 std::string client_name("googlechrome");
370#else
371 std::string client_name("chromium");
372#endif
373#endif
[email protected]894c4e82010-06-29 21:53:18374 CommandLine* cmdline = CommandLine::ForCurrentProcess();
[email protected]6142deb2010-09-08 23:21:18375 bool disable_auto_update =
376 cmdline->HasSwitch(switches::kSbDisableAutoUpdate) ||
377 cmdline->HasSwitch(switches::kDisableBackgroundNetworking);
[email protected]894c4e82010-06-29 21:53:18378 std::string info_url_prefix =
379 cmdline->HasSwitch(switches::kSbInfoURLPrefix) ?
380 cmdline->GetSwitchValueASCII(switches::kSbInfoURLPrefix) :
381 kSbDefaultInfoURLPrefix;
382 std::string mackey_url_prefix =
383 cmdline->HasSwitch(switches::kSbMacKeyURLPrefix) ?
384 cmdline->GetSwitchValueASCII(switches::kSbMacKeyURLPrefix) :
385 kSbDefaultMacKeyURLPrefix;
[email protected]1a871512009-11-06 06:11:18386
initial.commit09911bf2008-07-26 23:55:29387 protocol_manager_ = new SafeBrowsingProtocolManager(this,
[email protected]1a871512009-11-06 06:11:18388 client_name,
initial.commit09911bf2008-07-26 23:55:29389 client_key,
[email protected]d11f5662009-11-12 20:52:56390 wrapped_key,
[email protected]894c4e82010-06-29 21:53:18391 request_context_getter,
392 info_url_prefix,
393 mackey_url_prefix,
394 disable_auto_update);
[email protected]d11f5662009-11-12 20:52:56395
[email protected]dec173b2009-11-13 21:53:26396 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29397}
398
initial.commit09911bf2008-07-26 23:55:29399void SafeBrowsingService::OnIOShutdown() {
[email protected]4d3b42a2010-10-07 05:00:29400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29401 if (!enabled_)
402 return;
403
404 enabled_ = false;
initial.commit09911bf2008-07-26 23:55:29405
406 // This cancels all in-flight GetHash requests.
407 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04408 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29409
[email protected]03addd92009-11-17 21:38:10410 // Delete queued checks, calling back any clients with 'URL_SAFE'.
411 // If we don't do this here we may fail to close the database below.
412 while (!queued_checks_.empty()) {
413 QueuedCheck check = queued_checks_.front();
414 if (check.client)
415 check.client->OnUrlCheckResult(check.url, URL_SAFE);
416 queued_checks_.pop_front();
417 }
418
[email protected]cb2a67e2009-11-17 00:48:53419 // Close the database. We don't simply DeleteSoon() because if a close is
420 // already pending, we'll double-free, and we don't set |database_| to NULL
421 // because if there is still anything running on the db thread, it could
422 // create a new database object (via GetDatabase()) that would then leak.
423 CloseDatabase();
initial.commit09911bf2008-07-26 23:55:29424
425 // Flush the database thread. Any in-progress database check results will be
426 // ignored and cleaned up below.
[email protected]cb2a67e2009-11-17 00:48:53427 //
428 // Note that to avoid leaking the database, we rely on the fact that no new
429 // tasks will be added to the db thread between the call above and this one.
430 // See comments on the declaration of |safe_browsing_thread_|.
431 safe_browsing_thread_.reset();
initial.commit09911bf2008-07-26 23:55:29432
[email protected]03addd92009-11-17 21:38:10433 // Delete pending checks, calling back any clients with 'URL_SAFE'. We have
434 // to do this after the db thread returns because methods on it can have
435 // copies of these pointers, so deleting them might lead to accessing garbage.
initial.commit09911bf2008-07-26 23:55:29436 for (CurrentChecks::iterator it = checks_.begin();
437 it != checks_.end(); ++it) {
438 if ((*it)->client)
439 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
440 delete *it;
441 }
442 checks_.clear();
443
444 gethash_requests_.clear();
445}
446
[email protected]34094312009-12-03 21:40:26447bool SafeBrowsingService::DatabaseAvailable() const {
448 AutoLock lock(database_lock_);
449 return !closing_database_ && (database_ != NULL);
450}
451
[email protected]cb2a67e2009-11-17 00:48:53452bool SafeBrowsingService::MakeDatabaseAvailable() {
[email protected]4d3b42a2010-10-07 05:00:29453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53454 DCHECK(enabled_);
[email protected]34094312009-12-03 21:40:26455 if (DatabaseAvailable())
456 return true;
457 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
458 NewRunnableMethod(this, &SafeBrowsingService::GetDatabase));
459 return false;
[email protected]cb2a67e2009-11-17 00:48:53460}
461
[email protected]90a3b242009-11-13 00:28:54462SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
[email protected]ef5b4822010-09-09 16:45:49463 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54464 if (database_)
465 return database_;
initial.commit09911bf2008-07-26 23:55:29466
[email protected]90a3b242009-11-13 00:28:54467 FilePath path;
468 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
469 DCHECK(result);
470 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29471
[email protected]90a3b242009-11-13 00:28:54472 Time before = Time::Now();
473 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
[email protected]7b1e37102010-03-08 21:43:16474 database->Init(path);
[email protected]34094312009-12-03 21:40:26475 {
476 // Acquiring the lock here guarantees correct ordering between the writes to
477 // the new database object above, and the setting of |databse_| below.
478 AutoLock lock(database_lock_);
479 database_ = database;
480 }
[email protected]613a03b2008-10-24 23:02:00481
[email protected]4d3b42a2010-10-07 05:00:29482 BrowserThread::PostTask(
483 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54484 NewRunnableMethod(this, &SafeBrowsingService::DatabaseLoadComplete));
[email protected]613a03b2008-10-24 23:02:00485
[email protected]cb2a67e2009-11-17 00:48:53486 UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", Time::Now() - before);
[email protected]90a3b242009-11-13 00:28:54487 return database_;
initial.commit09911bf2008-07-26 23:55:29488}
489
[email protected]613a03b2008-10-24 23:02:00490void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
[email protected]4d3b42a2010-10-07 05:00:29491 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29492
493 // If we've been shutdown during the database lookup, this check will already
494 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00495 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29496 return;
497
[email protected]613a03b2008-10-24 23:02:00498 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29499 // We have a partial match so we need to query Google for the full hash.
500 // Clean up will happen in HandleGetHashResults.
501
502 // See if we have a GetHash request already in progress for this particular
503 // prefix. If so, we just append ourselves to the list of interested parties
504 // when the results arrive. We only do this for checks involving one prefix,
505 // since that is the common case (multiple prefixes will issue the request
506 // as normal).
[email protected]613a03b2008-10-24 23:02:00507 if (check->prefix_hits.size() == 1) {
508 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29509 GetHashRequests::iterator it = gethash_requests_.find(prefix);
510 if (it != gethash_requests_.end()) {
511 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00512 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29513 return;
514 }
515
516 // No request in progress, so we're the first for this prefix.
517 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00518 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29519 gethash_requests_[prefix] = requestors;
520 }
521
522 // Reset the start time so that we can measure the network time without the
523 // database time.
[email protected]613a03b2008-10-24 23:02:00524 check->start = Time::Now();
525 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29526 } else {
527 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00528 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29529 }
530}
531
[email protected]90a3b242009-11-13 00:28:54532void SafeBrowsingService::GetAllChunksFromDatabase() {
[email protected]ef5b4822010-09-09 16:45:49533 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
534
[email protected]90a3b242009-11-13 00:28:54535 bool database_error = true;
536 std::vector<SBListChunkRanges> lists;
[email protected]ef5b4822010-09-09 16:45:49537 DCHECK(!database_update_in_progress_);
538 database_update_in_progress_ = true;
[email protected]cb2a67e2009-11-17 00:48:53539 GetDatabase(); // This guarantees that |database_| is non-NULL.
[email protected]c23161342010-08-18 20:34:04540 if (database_->UpdateStarted(&lists)) {
[email protected]cb2a67e2009-11-17 00:48:53541 database_error = false;
542 } else {
543 database_->UpdateFinished(false);
[email protected]90a3b242009-11-13 00:28:54544 }
[email protected]613a03b2008-10-24 23:02:00545
[email protected]4d3b42a2010-10-07 05:00:29546 BrowserThread::PostTask(
547 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54548 NewRunnableMethod(
549 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
550 database_error));
initial.commit09911bf2008-07-26 23:55:29551}
552
[email protected]90a3b242009-11-13 00:28:54553void SafeBrowsingService::OnGetAllChunksFromDatabase(
554 const std::vector<SBListChunkRanges>& lists, bool database_error) {
[email protected]4d3b42a2010-10-07 05:00:29555 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54556 if (enabled_)
557 protocol_manager_->OnGetChunksComplete(lists, database_error);
558}
559
[email protected]90a3b242009-11-13 00:28:54560void SafeBrowsingService::OnChunkInserted() {
[email protected]4d3b42a2010-10-07 05:00:29561 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54562 if (enabled_)
563 protocol_manager_->OnChunkInserted();
564}
565
566void SafeBrowsingService::DatabaseLoadComplete() {
[email protected]4d3b42a2010-10-07 05:00:29567 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54568 if (!enabled_)
initial.commit09911bf2008-07-26 23:55:29569 return;
570
[email protected]03addd92009-11-17 21:38:10571 HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
572 if (queued_checks_.empty())
573 return;
574
575 // If the database isn't already available, calling CheckUrl() in the loop
576 // below will add the check back to the queue, and we'll infinite-loop.
[email protected]34094312009-12-03 21:40:26577 DCHECK(DatabaseAvailable());
[email protected]03addd92009-11-17 21:38:10578 while (!queued_checks_.empty()) {
579 QueuedCheck check = queued_checks_.front();
580 HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
581 // If CheckUrl() determines the URL is safe immediately, it doesn't call the
582 // client's handler function (because normally it's being directly called by
583 // the client). Since we're not the client, we have to convey this result.
584 if (check.client && CheckUrl(check.url, check.client))
585 check.client->OnUrlCheckResult(check.url, URL_SAFE);
586 queued_checks_.pop_front();
587 }
[email protected]90a3b242009-11-13 00:28:54588}
initial.commit09911bf2008-07-26 23:55:29589
[email protected]90a3b242009-11-13 00:28:54590void SafeBrowsingService::HandleChunkForDatabase(
591 const std::string& list_name,
[email protected]7b1e37102010-03-08 21:43:16592 SBChunkList* chunks) {
[email protected]ef5b4822010-09-09 16:45:49593 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16594 if (chunks) {
595 GetDatabase()->InsertChunks(list_name, *chunks);
596 delete chunks;
597 }
[email protected]4d3b42a2010-10-07 05:00:29598 BrowserThread::PostTask(
599 BrowserThread::IO, FROM_HERE,
[email protected]7b1e37102010-03-08 21:43:16600 NewRunnableMethod(this, &SafeBrowsingService::OnChunkInserted));
[email protected]90a3b242009-11-13 00:28:54601}
602
603void SafeBrowsingService::DeleteChunks(
604 std::vector<SBChunkDelete>* chunk_deletes) {
[email protected]ef5b4822010-09-09 16:45:49605 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16606 if (chunk_deletes) {
607 GetDatabase()->DeleteChunks(*chunk_deletes);
608 delete chunk_deletes;
609 }
[email protected]90a3b242009-11-13 00:28:54610}
611
612SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
613 const std::string& list_name) {
614 if (safe_browsing_util::IsPhishingList(list_name)) {
615 return URL_PHISHING;
[email protected]613a03b2008-10-24 23:02:00616 }
[email protected]90a3b242009-11-13 00:28:54617
618 if (safe_browsing_util::IsMalwareList(list_name)) {
619 return URL_MALWARE;
620 }
621
622 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
623 return URL_SAFE;
624}
625
626void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
627 bool proceed) {
628 client->OnBlockingPageComplete(proceed);
629}
630
631void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
[email protected]ef5b4822010-09-09 16:45:49632 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]cb2a67e2009-11-17 00:48:53633 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]ef5b4822010-09-09 16:45:49634 DCHECK(database_update_in_progress_);
635 database_update_in_progress_ = false;
[email protected]90a3b242009-11-13 00:28:54636}
637
638void SafeBrowsingService::Start() {
639 DCHECK(!safe_browsing_thread_.get());
640 safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
641 if (!safe_browsing_thread_->Start())
642 return;
643
644 // Retrieve client MAC keys.
645 PrefService* local_state = g_browser_process->local_state();
646 std::string client_key, wrapped_key;
647 if (local_state) {
648 client_key =
[email protected]ddd231e2010-06-29 20:35:19649 local_state->GetString(prefs::kSafeBrowsingClientKey);
[email protected]90a3b242009-11-13 00:28:54650 wrapped_key =
[email protected]ddd231e2010-06-29 20:35:19651 local_state->GetString(prefs::kSafeBrowsingWrappedKey);
[email protected]90a3b242009-11-13 00:28:54652 }
653
654 // We will issue network fetches using the default profile's request context.
[email protected]41a95c42010-10-20 15:49:15655 scoped_refptr<URLRequestContextGetter> request_context_getter =
[email protected]90a3b242009-11-13 00:28:54656 GetDefaultProfile()->GetRequestContext();
[email protected]90a3b242009-11-13 00:28:54657
[email protected]4d3b42a2010-10-07 05:00:29658 BrowserThread::PostTask(
659 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54660 NewRunnableMethod(
[email protected]0d7e79fa2010-10-08 23:35:47661 this, &SafeBrowsingService::OnIOInitialize, client_key, wrapped_key,
[email protected]90a3b242009-11-13 00:28:54662 request_context_getter));
[email protected]cb2a67e2009-11-17 00:48:53663}
[email protected]90a3b242009-11-13 00:28:54664
[email protected]cb2a67e2009-11-17 00:48:53665void SafeBrowsingService::OnCloseDatabase() {
[email protected]ef5b4822010-09-09 16:45:49666 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]a3aaf3d2009-11-17 22:12:39667 DCHECK(closing_database_);
[email protected]cb2a67e2009-11-17 00:48:53668
669 // Because |closing_database_| is true, nothing on the IO thread will be
670 // accessing the database, so it's safe to delete and then NULL the pointer.
671 delete database_;
672 database_ = NULL;
[email protected]34094312009-12-03 21:40:26673
674 // Acquiring the lock here guarantees correct ordering between the resetting
675 // of |database_| above and of |closing_database_| below, which ensures there
676 // won't be a window during which the IO thread falsely believes the database
677 // is available.
678 AutoLock lock(database_lock_);
[email protected]cb2a67e2009-11-17 00:48:53679 closing_database_ = false;
[email protected]90a3b242009-11-13 00:28:54680}
681
682void SafeBrowsingService::OnResetDatabase() {
[email protected]ef5b4822010-09-09 16:45:49683 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54684 GetDatabase()->ResetDatabase();
[email protected]90a3b242009-11-13 00:28:54685}
686
687void SafeBrowsingService::CacheHashResults(
688 const std::vector<SBPrefix>& prefixes,
689 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]ef5b4822010-09-09 16:45:49690 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54691 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29692}
693
694void SafeBrowsingService::OnHandleGetHashResults(
695 SafeBrowsingCheck* check,
696 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]4d3b42a2010-10-07 05:00:29697 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29698 SBPrefix prefix = check->prefix_hits[0];
699 GetHashRequests::iterator it = gethash_requests_.find(prefix);
700 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
701 HandleOneCheck(check, full_hashes);
702 return;
703 }
704
705 // Call back all interested parties.
706 GetHashRequestors& requestors = it->second;
707 for (GetHashRequestors::iterator r = requestors.begin();
708 r != requestors.end(); ++r) {
709 HandleOneCheck(*r, full_hashes);
710 }
711
712 gethash_requests_.erase(it);
713}
714
715void SafeBrowsingService::HandleOneCheck(
716 SafeBrowsingCheck* check,
717 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]4d3b42a2010-10-07 05:00:29718 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29719 if (check->client) {
720 UrlCheckResult result = URL_SAFE;
721 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
[email protected]09d985f2009-05-11 21:59:58722 if (index != -1) {
initial.commit09911bf2008-07-26 23:55:29723 result = GetResultFromListname(full_hashes[index].list_name);
[email protected]09d985f2009-05-11 21:59:58724 } else {
725 // Log the case where the SafeBrowsing servers return full hashes in the
726 // GetHash response that match the prefix we're looking up, but don't
727 // match the full hash of the URL.
728 if (!full_hashes.empty())
729 UMA_HISTOGRAM_COUNTS("SB2.GetHashServerMiss", 1);
730 }
initial.commit09911bf2008-07-26 23:55:29731
732 // Let the client continue handling the original request.
733 check->client->OnUrlCheckResult(check->url, result);
734 }
735
736 checks_.erase(check);
737 delete check;
738}
739
[email protected]90a3b242009-11-13 00:28:54740void SafeBrowsingService::DoDisplayBlockingPage(
741 const UnsafeResource& resource) {
742 // The tab might have been closed.
743 TabContents* wc =
744 tab_util::GetTabContentsByID(resource.render_process_host_id,
745 resource.render_view_id);
initial.commit09911bf2008-07-26 23:55:29746
[email protected]90a3b242009-11-13 00:28:54747 if (!wc) {
748 // The tab is gone and we did not have a chance at showing the interstitial.
749 // Just act as "Don't Proceed" was chosen.
750 std::vector<UnsafeResource> resources;
751 resources.push_back(resource);
[email protected]4d3b42a2010-10-07 05:00:29752 BrowserThread::PostTask(
753 BrowserThread::IO, FROM_HERE,
[email protected]d83d03aa2009-11-02 21:44:37754 NewRunnableMethod(
[email protected]90a3b242009-11-13 00:28:54755 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
756 return;
initial.commit09911bf2008-07-26 23:55:29757 }
758
[email protected]036237d42010-10-07 22:41:58759 // Report the malicious resource to the SafeBrowsing servers if the user has
760 // opted in to reporting statistics.
[email protected]975bead2009-11-30 21:59:53761 const MetricsService* metrics = g_browser_process->metrics_service();
762 DCHECK(metrics);
763 if (metrics && metrics->reporting_active() &&
[email protected]036237d42010-10-07 22:41:58764 (resource.threat_type == SafeBrowsingService::URL_MALWARE ||
765 resource.threat_type == SafeBrowsingService::URL_PHISHING)) {
[email protected]90a3b242009-11-13 00:28:54766 GURL page_url = wc->GetURL();
767 GURL referrer_url;
768 NavigationEntry* entry = wc->controller().GetActiveEntry();
769 if (entry)
770 referrer_url = entry->referrer();
[email protected]293f19252010-08-18 20:01:18771 bool is_subresource = resource.resource_type != ResourceType::MAIN_FRAME;
[email protected]ee4d3e802010-08-12 23:59:51772
[email protected]293f19252010-08-18 20:01:18773 // When the malicious url is on the main frame, and resource.original_url
774 // is not the same as the resource.url, that means we have a redirect from
775 // resource.original_url to resource.url.
776 // Also, at this point, page_url points to the _previous_ page that we
777 // were on. We replace page_url with resource.original_url and referrer
778 // with page_url.
779 if (!is_subresource &&
780 !resource.original_url.is_empty() &&
781 resource.original_url != resource.url) {
782 referrer_url = page_url;
783 page_url = resource.original_url;
784 }
785
[email protected]036237d42010-10-07 22:41:58786 BrowserThread::PostTask(
787 BrowserThread::IO, FROM_HERE,
788 NewRunnableMethod(
789 this,
790 &SafeBrowsingService::ReportSafeBrowsingHit,
791 resource.url,
792 page_url,
793 referrer_url,
794 is_subresource,
795 resource.threat_type));
initial.commit09911bf2008-07-26 23:55:29796 }
797
[email protected]90a3b242009-11-13 00:28:54798 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
[email protected]613a03b2008-10-24 23:02:00799}
800
[email protected]036237d42010-10-07 22:41:58801void SafeBrowsingService::ReportSafeBrowsingHit(
802 const GURL& malicious_url,
803 const GURL& page_url,
804 const GURL& referrer_url,
805 bool is_subresource,
806 SafeBrowsingService::UrlCheckResult threat_type) {
[email protected]4d3b42a2010-10-07 05:00:29807 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]fd220e602009-11-14 01:02:37808 if (!enabled_)
[email protected]dfdb0de72009-02-19 21:58:14809 return;
810
[email protected]036237d42010-10-07 22:41:58811 DLOG(INFO) << "ReportSafeBrowsingHit: " << malicious_url << " " << page_url
812 << " " << referrer_url << " " << is_subresource
813 << " " << threat_type;
814 protocol_manager_->ReportSafeBrowsingHit(malicious_url, page_url,
815 referrer_url, is_subresource,
816 threat_type);
[email protected]dfdb0de72009-02-19 21:58:14817}