blob: 5f2d663f848cf3865ccc5897ead3905f93547683 [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]975bead2009-11-30 21:59:5313#include "chrome/browser/metrics/metrics_service.h"
[email protected]37858e52010-08-26 00:22:0214#include "chrome/browser/prefs/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,
[email protected]293f19252010-08-18 20:01:18151 const GURL& original_url,
[email protected]90a3b242009-11-13 00:28:54152 ResourceType::Type resource_type,
153 UrlCheckResult result,
154 Client* client,
155 int render_process_host_id,
156 int render_view_id) {
[email protected]cb2a67e2009-11-17 00:48:53157 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
158
[email protected]90a3b242009-11-13 00:28:54159 // Check if the user has already ignored our warning for this render_view
160 // and domain.
161 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
162 const WhiteListedEntry& entry = white_listed_entries_[i];
163 if (entry.render_process_host_id == render_process_host_id &&
164 entry.render_view_id == render_view_id &&
165 entry.result == result &&
166 entry.domain ==
167 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
168 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
169 this, &SafeBrowsingService::NotifyClientBlockingComplete,
170 client, true));
171 return;
172 }
173 }
174
175 UnsafeResource resource;
176 resource.url = url;
[email protected]293f19252010-08-18 20:01:18177 resource.original_url = original_url;
[email protected]90a3b242009-11-13 00:28:54178 resource.resource_type = resource_type;
179 resource.threat_type= result;
180 resource.client = client;
181 resource.render_process_host_id = render_process_host_id;
182 resource.render_view_id = render_view_id;
183
184 // The blocking page must be created from the UI thread.
185 ChromeThread::PostTask(
186 ChromeThread::UI, FROM_HERE,
187 NewRunnableMethod(
188 this, &SafeBrowsingService::DoDisplayBlockingPage, resource));
189}
190
191void SafeBrowsingService::HandleGetHashResults(
192 SafeBrowsingCheck* check,
193 const std::vector<SBFullHashResult>& full_hashes,
194 bool can_cache) {
[email protected]cb2a67e2009-11-17 00:48:53195 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]90a3b242009-11-13 00:28:54196 if (checks_.find(check) == checks_.end())
197 return;
198
199 DCHECK(enabled_);
200
201 UMA_HISTOGRAM_LONG_TIMES("SB2.Network", Time::Now() - check->start);
202
203 std::vector<SBPrefix> prefixes = check->prefix_hits;
204 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
205
[email protected]cb2a67e2009-11-17 00:48:53206 if (can_cache && MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:54207 // Cache the GetHash results in memory:
208 database_->CacheHashResults(prefixes, full_hashes);
209 }
210}
211
212void SafeBrowsingService::HandleChunk(const std::string& list,
[email protected]7b1e37102010-03-08 21:43:16213 SBChunkList* chunks) {
[email protected]90a3b242009-11-13 00:28:54214 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
215 DCHECK(enabled_);
216 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
217 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
218}
219
220void SafeBrowsingService::HandleChunkDelete(
221 std::vector<SBChunkDelete>* chunk_deletes) {
222 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
223 DCHECK(enabled_);
224 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
225 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
226}
227
228void SafeBrowsingService::UpdateStarted() {
229 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
230 DCHECK(enabled_);
231 DCHECK(!update_in_progress_);
232 update_in_progress_ = true;
233 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
234 this, &SafeBrowsingService::GetAllChunksFromDatabase));
235}
236
237void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
238 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
239 DCHECK(enabled_);
240 if (update_in_progress_) {
241 update_in_progress_ = false;
242 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
243 NewRunnableMethod(this,
244 &SafeBrowsingService::DatabaseUpdateFinished,
245 update_succeeded));
246 }
247}
248
[email protected]894c4e82010-06-29 21:53:18249bool SafeBrowsingService::IsUpdateInProgress() const {
250 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
251 return update_in_progress_;
252}
253
[email protected]90a3b242009-11-13 00:28:54254void SafeBrowsingService::OnBlockingPageDone(
255 const std::vector<UnsafeResource>& resources,
256 bool proceed) {
257 for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
258 iter != resources.end(); ++iter) {
259 const UnsafeResource& resource = *iter;
260 NotifyClientBlockingComplete(resource.client, proceed);
261
262 if (proceed) {
263 // Whitelist this domain and warning type for the given tab.
264 WhiteListedEntry entry;
265 entry.render_process_host_id = resource.render_process_host_id;
266 entry.render_view_id = resource.render_view_id;
267 entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
268 resource.url);
269 entry.result = resource.threat_type;
270 white_listed_entries_.push_back(entry);
271 }
272 }
273}
274
275void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
276 const std::string& wrapped_key) {
277 PrefService* prefs = g_browser_process->local_state();
278 if (prefs) {
[email protected]ddd231e2010-06-29 20:35:19279 prefs->SetString(prefs::kSafeBrowsingClientKey, client_key);
280 prefs->SetString(prefs::kSafeBrowsingWrappedKey, wrapped_key);
[email protected]90a3b242009-11-13 00:28:54281 }
282}
283
284void SafeBrowsingService::OnEnable(bool enabled) {
285 if (enabled)
286 Start();
287 else
288 ShutDown();
289}
290
291// static
292void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
[email protected]20ce516d2010-06-18 02:20:04293 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, "");
294 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, "");
[email protected]90a3b242009-11-13 00:28:54295}
296
[email protected]cb2a67e2009-11-17 00:48:53297void SafeBrowsingService::CloseDatabase() {
298 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
299
[email protected]03addd92009-11-17 21:38:10300 // Cases to avoid:
301 // * If |closing_database_| is true, continuing will queue up a second
302 // request, |closing_database_| will be reset after handling the first
303 // request, and if any functions on the db thread recreate the database, we
304 // could start using it on the IO thread and then have the second request
305 // handler delete it out from under us.
306 // * If |database_| is NULL, then either no creation request is in flight, in
307 // which case we don't need to do anything, or one is in flight, in which
308 // case the database will be recreated before our deletion request is
309 // handled, and could be used on the IO thread in that time period, leading
310 // to the same problem as above.
311 // * If |queued_checks_| is non-empty and |database_| is non-NULL, we're
312 // about to be called back (in DatabaseLoadComplete()). This will call
313 // CheckUrl(), which will want the database. Closing the database here
314 // would lead to an infinite loop in DatabaseLoadComplete(), and even if it
315 // didn't, it would be pointless since we'd just want to recreate.
316 //
[email protected]34094312009-12-03 21:40:26317 // The first two cases above are handled by checking DatabaseAvailable().
318 if (!DatabaseAvailable() || !queued_checks_.empty())
[email protected]cb2a67e2009-11-17 00:48:53319 return;
320
321 closing_database_ = true;
322 if (safe_browsing_thread_.get()) {
323 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
324 NewRunnableMethod(this, &SafeBrowsingService::OnCloseDatabase));
325 }
326}
327
[email protected]90a3b242009-11-13 00:28:54328void SafeBrowsingService::ResetDatabase() {
329 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53330 DCHECK(enabled_);
[email protected]90a3b242009-11-13 00:28:54331 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
332 this, &SafeBrowsingService::OnResetDatabase));
333}
334
335void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
336 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
337}
338
339SafeBrowsingService::~SafeBrowsingService() {
[email protected]cb2a67e2009-11-17 00:48:53340 // We should have already been shut down. If we're still enabled, then the
341 // database isn't going to be closed properly, which could lead to corruption.
342 DCHECK(!enabled_);
[email protected]90a3b242009-11-13 00:28:54343}
344
[email protected]d11f5662009-11-12 20:52:56345void SafeBrowsingService::OnIOInitialize(
346 const std::string& client_key,
347 const std::string& wrapped_key,
348 URLRequestContextGetter* request_context_getter) {
[email protected]d83d03aa2009-11-02 21:44:37349 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29350 enabled_ = true;
[email protected]cb2a67e2009-11-17 00:48:53351 MakeDatabaseAvailable();
[email protected]1a871512009-11-06 06:11:18352
353 // On Windows, get the safe browsing client name from the browser
354 // distribution classes in installer util. These classes don't yet have
355 // an analog on non-Windows builds so just keep the name specified here.
356#if defined(OS_WIN)
357 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
358 std::string client_name(dist->GetSafeBrowsingName());
359#else
360#if defined(GOOGLE_CHROME_BUILD)
361 std::string client_name("googlechrome");
362#else
363 std::string client_name("chromium");
364#endif
365#endif
[email protected]894c4e82010-06-29 21:53:18366 CommandLine* cmdline = CommandLine::ForCurrentProcess();
[email protected]6142deb2010-09-08 23:21:18367 bool disable_auto_update =
368 cmdline->HasSwitch(switches::kSbDisableAutoUpdate) ||
369 cmdline->HasSwitch(switches::kDisableBackgroundNetworking);
[email protected]894c4e82010-06-29 21:53:18370 std::string info_url_prefix =
371 cmdline->HasSwitch(switches::kSbInfoURLPrefix) ?
372 cmdline->GetSwitchValueASCII(switches::kSbInfoURLPrefix) :
373 kSbDefaultInfoURLPrefix;
374 std::string mackey_url_prefix =
375 cmdline->HasSwitch(switches::kSbMacKeyURLPrefix) ?
376 cmdline->GetSwitchValueASCII(switches::kSbMacKeyURLPrefix) :
377 kSbDefaultMacKeyURLPrefix;
[email protected]1a871512009-11-06 06:11:18378
initial.commit09911bf2008-07-26 23:55:29379 protocol_manager_ = new SafeBrowsingProtocolManager(this,
[email protected]1a871512009-11-06 06:11:18380 client_name,
initial.commit09911bf2008-07-26 23:55:29381 client_key,
[email protected]d11f5662009-11-12 20:52:56382 wrapped_key,
[email protected]894c4e82010-06-29 21:53:18383 request_context_getter,
384 info_url_prefix,
385 mackey_url_prefix,
386 disable_auto_update);
[email protected]d11f5662009-11-12 20:52:56387
388 // Balance the reference added by Start().
389 request_context_getter->Release();
390
[email protected]dec173b2009-11-13 21:53:26391 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29392}
393
initial.commit09911bf2008-07-26 23:55:29394void SafeBrowsingService::OnIOShutdown() {
[email protected]d83d03aa2009-11-02 21:44:37395 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29396 if (!enabled_)
397 return;
398
399 enabled_ = false;
initial.commit09911bf2008-07-26 23:55:29400
401 // This cancels all in-flight GetHash requests.
402 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04403 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29404
[email protected]03addd92009-11-17 21:38:10405 // Delete queued checks, calling back any clients with 'URL_SAFE'.
406 // If we don't do this here we may fail to close the database below.
407 while (!queued_checks_.empty()) {
408 QueuedCheck check = queued_checks_.front();
409 if (check.client)
410 check.client->OnUrlCheckResult(check.url, URL_SAFE);
411 queued_checks_.pop_front();
412 }
413
[email protected]cb2a67e2009-11-17 00:48:53414 // Close the database. We don't simply DeleteSoon() because if a close is
415 // already pending, we'll double-free, and we don't set |database_| to NULL
416 // because if there is still anything running on the db thread, it could
417 // create a new database object (via GetDatabase()) that would then leak.
418 CloseDatabase();
initial.commit09911bf2008-07-26 23:55:29419
420 // Flush the database thread. Any in-progress database check results will be
421 // ignored and cleaned up below.
[email protected]cb2a67e2009-11-17 00:48:53422 //
423 // Note that to avoid leaking the database, we rely on the fact that no new
424 // tasks will be added to the db thread between the call above and this one.
425 // See comments on the declaration of |safe_browsing_thread_|.
426 safe_browsing_thread_.reset();
initial.commit09911bf2008-07-26 23:55:29427
[email protected]03addd92009-11-17 21:38:10428 // Delete pending checks, calling back any clients with 'URL_SAFE'. We have
429 // to do this after the db thread returns because methods on it can have
430 // copies of these pointers, so deleting them might lead to accessing garbage.
initial.commit09911bf2008-07-26 23:55:29431 for (CurrentChecks::iterator it = checks_.begin();
432 it != checks_.end(); ++it) {
433 if ((*it)->client)
434 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
435 delete *it;
436 }
437 checks_.clear();
438
439 gethash_requests_.clear();
440}
441
[email protected]34094312009-12-03 21:40:26442bool SafeBrowsingService::DatabaseAvailable() const {
443 AutoLock lock(database_lock_);
444 return !closing_database_ && (database_ != NULL);
445}
446
[email protected]cb2a67e2009-11-17 00:48:53447bool SafeBrowsingService::MakeDatabaseAvailable() {
448 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
449 DCHECK(enabled_);
[email protected]34094312009-12-03 21:40:26450 if (DatabaseAvailable())
451 return true;
452 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
453 NewRunnableMethod(this, &SafeBrowsingService::GetDatabase));
454 return false;
[email protected]cb2a67e2009-11-17 00:48:53455}
456
[email protected]90a3b242009-11-13 00:28:54457SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
458 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
459 if (database_)
460 return database_;
initial.commit09911bf2008-07-26 23:55:29461
[email protected]90a3b242009-11-13 00:28:54462 FilePath path;
463 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
464 DCHECK(result);
465 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29466
[email protected]90a3b242009-11-13 00:28:54467 Time before = Time::Now();
468 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
[email protected]7b1e37102010-03-08 21:43:16469 database->Init(path);
[email protected]34094312009-12-03 21:40:26470 {
471 // Acquiring the lock here guarantees correct ordering between the writes to
472 // the new database object above, and the setting of |databse_| below.
473 AutoLock lock(database_lock_);
474 database_ = database;
475 }
[email protected]613a03b2008-10-24 23:02:00476
[email protected]d83d03aa2009-11-02 21:44:37477 ChromeThread::PostTask(
478 ChromeThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54479 NewRunnableMethod(this, &SafeBrowsingService::DatabaseLoadComplete));
[email protected]613a03b2008-10-24 23:02:00480
[email protected]cb2a67e2009-11-17 00:48:53481 UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", Time::Now() - before);
[email protected]90a3b242009-11-13 00:28:54482 return database_;
initial.commit09911bf2008-07-26 23:55:29483}
484
[email protected]613a03b2008-10-24 23:02:00485void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
[email protected]d83d03aa2009-11-02 21:44:37486 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29487
488 // If we've been shutdown during the database lookup, this check will already
489 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00490 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29491 return;
492
[email protected]613a03b2008-10-24 23:02:00493 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29494 // We have a partial match so we need to query Google for the full hash.
495 // Clean up will happen in HandleGetHashResults.
496
497 // See if we have a GetHash request already in progress for this particular
498 // prefix. If so, we just append ourselves to the list of interested parties
499 // when the results arrive. We only do this for checks involving one prefix,
500 // since that is the common case (multiple prefixes will issue the request
501 // as normal).
[email protected]613a03b2008-10-24 23:02:00502 if (check->prefix_hits.size() == 1) {
503 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29504 GetHashRequests::iterator it = gethash_requests_.find(prefix);
505 if (it != gethash_requests_.end()) {
506 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00507 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29508 return;
509 }
510
511 // No request in progress, so we're the first for this prefix.
512 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00513 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29514 gethash_requests_[prefix] = requestors;
515 }
516
517 // Reset the start time so that we can measure the network time without the
518 // database time.
[email protected]613a03b2008-10-24 23:02:00519 check->start = Time::Now();
520 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29521 } else {
522 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00523 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29524 }
525}
526
[email protected]90a3b242009-11-13 00:28:54527void SafeBrowsingService::GetAllChunksFromDatabase() {
[email protected]f3724ea2009-05-08 22:09:56528 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54529 bool database_error = true;
530 std::vector<SBListChunkRanges> lists;
[email protected]cb2a67e2009-11-17 00:48:53531 GetDatabase(); // This guarantees that |database_| is non-NULL.
[email protected]c23161342010-08-18 20:34:04532 if (database_->UpdateStarted(&lists)) {
[email protected]cb2a67e2009-11-17 00:48:53533 database_error = false;
534 } else {
535 database_->UpdateFinished(false);
[email protected]90a3b242009-11-13 00:28:54536 }
[email protected]613a03b2008-10-24 23:02:00537
[email protected]d83d03aa2009-11-02 21:44:37538 ChromeThread::PostTask(
539 ChromeThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54540 NewRunnableMethod(
541 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
542 database_error));
initial.commit09911bf2008-07-26 23:55:29543}
544
[email protected]90a3b242009-11-13 00:28:54545void SafeBrowsingService::OnGetAllChunksFromDatabase(
546 const std::vector<SBListChunkRanges>& lists, bool database_error) {
547 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
548 if (enabled_)
549 protocol_manager_->OnGetChunksComplete(lists, database_error);
550}
551
[email protected]90a3b242009-11-13 00:28:54552void SafeBrowsingService::OnChunkInserted() {
553 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
554 if (enabled_)
555 protocol_manager_->OnChunkInserted();
556}
557
558void SafeBrowsingService::DatabaseLoadComplete() {
559 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
560 if (!enabled_)
initial.commit09911bf2008-07-26 23:55:29561 return;
562
[email protected]03addd92009-11-17 21:38:10563 HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
564 if (queued_checks_.empty())
565 return;
566
567 // If the database isn't already available, calling CheckUrl() in the loop
568 // below will add the check back to the queue, and we'll infinite-loop.
[email protected]34094312009-12-03 21:40:26569 DCHECK(DatabaseAvailable());
[email protected]03addd92009-11-17 21:38:10570 while (!queued_checks_.empty()) {
571 QueuedCheck check = queued_checks_.front();
572 HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
573 // If CheckUrl() determines the URL is safe immediately, it doesn't call the
574 // client's handler function (because normally it's being directly called by
575 // the client). Since we're not the client, we have to convey this result.
576 if (check.client && CheckUrl(check.url, check.client))
577 check.client->OnUrlCheckResult(check.url, URL_SAFE);
578 queued_checks_.pop_front();
579 }
[email protected]90a3b242009-11-13 00:28:54580}
initial.commit09911bf2008-07-26 23:55:29581
[email protected]90a3b242009-11-13 00:28:54582void SafeBrowsingService::HandleChunkForDatabase(
583 const std::string& list_name,
[email protected]7b1e37102010-03-08 21:43:16584 SBChunkList* chunks) {
[email protected]90a3b242009-11-13 00:28:54585 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16586 if (chunks) {
587 GetDatabase()->InsertChunks(list_name, *chunks);
588 delete chunks;
589 }
590 ChromeThread::PostTask(
591 ChromeThread::IO, FROM_HERE,
592 NewRunnableMethod(this, &SafeBrowsingService::OnChunkInserted));
[email protected]90a3b242009-11-13 00:28:54593}
594
595void SafeBrowsingService::DeleteChunks(
596 std::vector<SBChunkDelete>* chunk_deletes) {
597 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16598 if (chunk_deletes) {
599 GetDatabase()->DeleteChunks(*chunk_deletes);
600 delete chunk_deletes;
601 }
[email protected]90a3b242009-11-13 00:28:54602}
603
604SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
605 const std::string& list_name) {
606 if (safe_browsing_util::IsPhishingList(list_name)) {
607 return URL_PHISHING;
[email protected]613a03b2008-10-24 23:02:00608 }
[email protected]90a3b242009-11-13 00:28:54609
610 if (safe_browsing_util::IsMalwareList(list_name)) {
611 return URL_MALWARE;
612 }
613
614 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
615 return URL_SAFE;
616}
617
618void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
619 bool proceed) {
620 client->OnBlockingPageComplete(proceed);
621}
622
623void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
624 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]cb2a67e2009-11-17 00:48:53625 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]90a3b242009-11-13 00:28:54626}
627
628void SafeBrowsingService::Start() {
629 DCHECK(!safe_browsing_thread_.get());
630 safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
631 if (!safe_browsing_thread_->Start())
632 return;
633
634 // Retrieve client MAC keys.
635 PrefService* local_state = g_browser_process->local_state();
636 std::string client_key, wrapped_key;
637 if (local_state) {
638 client_key =
[email protected]ddd231e2010-06-29 20:35:19639 local_state->GetString(prefs::kSafeBrowsingClientKey);
[email protected]90a3b242009-11-13 00:28:54640 wrapped_key =
[email protected]ddd231e2010-06-29 20:35:19641 local_state->GetString(prefs::kSafeBrowsingWrappedKey);
[email protected]90a3b242009-11-13 00:28:54642 }
643
644 // We will issue network fetches using the default profile's request context.
645 URLRequestContextGetter* request_context_getter =
646 GetDefaultProfile()->GetRequestContext();
647 request_context_getter->AddRef(); // Balanced in OnIOInitialize.
648
649 ChromeThread::PostTask(
650 ChromeThread::IO, FROM_HERE,
651 NewRunnableMethod(
652 this, &SafeBrowsingService::OnIOInitialize, client_key, wrapped_key,
653 request_context_getter));
[email protected]cb2a67e2009-11-17 00:48:53654}
[email protected]90a3b242009-11-13 00:28:54655
[email protected]cb2a67e2009-11-17 00:48:53656void SafeBrowsingService::OnCloseDatabase() {
657 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]a3aaf3d2009-11-17 22:12:39658 DCHECK(closing_database_);
[email protected]cb2a67e2009-11-17 00:48:53659
660 // Because |closing_database_| is true, nothing on the IO thread will be
661 // accessing the database, so it's safe to delete and then NULL the pointer.
662 delete database_;
663 database_ = NULL;
[email protected]34094312009-12-03 21:40:26664
665 // Acquiring the lock here guarantees correct ordering between the resetting
666 // of |database_| above and of |closing_database_| below, which ensures there
667 // won't be a window during which the IO thread falsely believes the database
668 // is available.
669 AutoLock lock(database_lock_);
[email protected]cb2a67e2009-11-17 00:48:53670 closing_database_ = false;
[email protected]90a3b242009-11-13 00:28:54671}
672
673void SafeBrowsingService::OnResetDatabase() {
674 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
675 GetDatabase()->ResetDatabase();
[email protected]90a3b242009-11-13 00:28:54676}
677
678void SafeBrowsingService::CacheHashResults(
679 const std::vector<SBPrefix>& prefixes,
680 const std::vector<SBFullHashResult>& full_hashes) {
681 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
682 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29683}
684
685void SafeBrowsingService::OnHandleGetHashResults(
686 SafeBrowsingCheck* check,
687 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]cb2a67e2009-11-17 00:48:53688 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29689 SBPrefix prefix = check->prefix_hits[0];
690 GetHashRequests::iterator it = gethash_requests_.find(prefix);
691 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
692 HandleOneCheck(check, full_hashes);
693 return;
694 }
695
696 // Call back all interested parties.
697 GetHashRequestors& requestors = it->second;
698 for (GetHashRequestors::iterator r = requestors.begin();
699 r != requestors.end(); ++r) {
700 HandleOneCheck(*r, full_hashes);
701 }
702
703 gethash_requests_.erase(it);
704}
705
706void SafeBrowsingService::HandleOneCheck(
707 SafeBrowsingCheck* check,
708 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]cb2a67e2009-11-17 00:48:53709 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29710 if (check->client) {
711 UrlCheckResult result = URL_SAFE;
712 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
[email protected]09d985f2009-05-11 21:59:58713 if (index != -1) {
initial.commit09911bf2008-07-26 23:55:29714 result = GetResultFromListname(full_hashes[index].list_name);
[email protected]09d985f2009-05-11 21:59:58715 } else {
716 // Log the case where the SafeBrowsing servers return full hashes in the
717 // GetHash response that match the prefix we're looking up, but don't
718 // match the full hash of the URL.
719 if (!full_hashes.empty())
720 UMA_HISTOGRAM_COUNTS("SB2.GetHashServerMiss", 1);
721 }
initial.commit09911bf2008-07-26 23:55:29722
723 // Let the client continue handling the original request.
724 check->client->OnUrlCheckResult(check->url, result);
725 }
726
727 checks_.erase(check);
728 delete check;
729}
730
[email protected]90a3b242009-11-13 00:28:54731void SafeBrowsingService::DoDisplayBlockingPage(
732 const UnsafeResource& resource) {
733 // The tab might have been closed.
734 TabContents* wc =
735 tab_util::GetTabContentsByID(resource.render_process_host_id,
736 resource.render_view_id);
initial.commit09911bf2008-07-26 23:55:29737
[email protected]90a3b242009-11-13 00:28:54738 if (!wc) {
739 // The tab is gone and we did not have a chance at showing the interstitial.
740 // Just act as "Don't Proceed" was chosen.
741 std::vector<UnsafeResource> resources;
742 resources.push_back(resource);
743 ChromeThread::PostTask(
[email protected]d83d03aa2009-11-02 21:44:37744 ChromeThread::IO, FROM_HERE,
745 NewRunnableMethod(
[email protected]90a3b242009-11-13 00:28:54746 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
747 return;
initial.commit09911bf2008-07-26 23:55:29748 }
749
[email protected]293f19252010-08-18 20:01:18750 // Report the malware resource to the SafeBrowsing servers if we have a
751 // malware resource on a safe page and only if the user has opted in to
[email protected]90a3b242009-11-13 00:28:54752 // reporting statistics.
[email protected]975bead2009-11-30 21:59:53753 const MetricsService* metrics = g_browser_process->metrics_service();
754 DCHECK(metrics);
755 if (metrics && metrics->reporting_active() &&
[email protected]90a3b242009-11-13 00:28:54756 resource.threat_type == SafeBrowsingService::URL_MALWARE) {
757 GURL page_url = wc->GetURL();
758 GURL referrer_url;
759 NavigationEntry* entry = wc->controller().GetActiveEntry();
760 if (entry)
761 referrer_url = entry->referrer();
[email protected]293f19252010-08-18 20:01:18762 bool is_subresource = resource.resource_type != ResourceType::MAIN_FRAME;
[email protected]ee4d3e802010-08-12 23:59:51763
[email protected]293f19252010-08-18 20:01:18764 // When the malicious url is on the main frame, and resource.original_url
765 // is not the same as the resource.url, that means we have a redirect from
766 // resource.original_url to resource.url.
767 // Also, at this point, page_url points to the _previous_ page that we
768 // were on. We replace page_url with resource.original_url and referrer
769 // with page_url.
770 if (!is_subresource &&
771 !resource.original_url.is_empty() &&
772 resource.original_url != resource.url) {
773 referrer_url = page_url;
774 page_url = resource.original_url;
775 }
776
777 if ((!page_url.is_empty() && resource.url != page_url) ||
778 !referrer_url.is_empty()) {
[email protected]ee4d3e802010-08-12 23:59:51779 ChromeThread::PostTask(
780 ChromeThread::IO, FROM_HERE,
781 NewRunnableMethod(this,
782 &SafeBrowsingService::ReportMalware,
783 resource.url,
784 page_url,
785 referrer_url,
786 is_subresource));
787 }
initial.commit09911bf2008-07-26 23:55:29788 }
789
[email protected]90a3b242009-11-13 00:28:54790 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
[email protected]613a03b2008-10-24 23:02:00791}
792
[email protected]dfdb0de72009-02-19 21:58:14793void SafeBrowsingService::ReportMalware(const GURL& malware_url,
794 const GURL& page_url,
[email protected]ee4d3e802010-08-12 23:59:51795 const GURL& referrer_url,
796 bool is_subresource) {
[email protected]d83d03aa2009-11-02 21:44:37797 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]dfdb0de72009-02-19 21:58:14798
[email protected]fd220e602009-11-14 01:02:37799 if (!enabled_)
[email protected]dfdb0de72009-02-19 21:58:14800 return;
801
[email protected]34094312009-12-03 21:40:26802 if (DatabaseAvailable()) {
[email protected]fd220e602009-11-14 01:02:37803 // Check if 'page_url' is already blacklisted (exists in our cache). Only
[email protected]ee4d3e802010-08-12 23:59:51804 // report if it's not there. This can happen if the user has ignored
805 // the warning for page_url and is now hitting a warning for a resource.
[email protected]fd220e602009-11-14 01:02:37806 std::string list;
807 std::vector<SBPrefix> prefix_hits;
808 std::vector<SBFullHashResult> full_hits;
809 database_->ContainsUrl(page_url, &list, &prefix_hits, &full_hits,
810 protocol_manager_->last_update());
811 if (!full_hits.empty())
812 return;
813 }
[email protected]dfdb0de72009-02-19 21:58:14814
[email protected]293f19252010-08-18 20:01:18815 DLOG(INFO) << "ReportMalware: " << malware_url << " " << page_url << " " <<
816 referrer_url << " " << is_subresource;
817
[email protected]ee4d3e802010-08-12 23:59:51818 protocol_manager_->ReportMalware(malware_url, page_url, referrer_url,
819 is_subresource);
[email protected]dfdb0de72009-02-19 21:58:14820}