blob: 5448f9389a12818275b29ead5393016d5743b893 [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
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),
[email protected]ef5b4822010-09-09 16:45:4958 database_update_in_progress_(false),
[email protected]cb2a67e2009-11-17 00:48:5359 closing_database_(false) {
initial.commit09911bf2008-07-26 23:55:2960}
61
[email protected]d83d03aa2009-11-02 21:44:3762void SafeBrowsingService::Initialize() {
initial.commit09911bf2008-07-26 23:55:2963 // Get the profile's preference for SafeBrowsing.
[email protected]d11f5662009-11-12 20:52:5664 PrefService* pref_service = GetDefaultProfile()->GetPrefs();
initial.commit09911bf2008-07-26 23:55:2965 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
66 Start();
67}
68
initial.commit09911bf2008-07-26 23:55:2969void SafeBrowsingService::ShutDown() {
[email protected]4d3b42a2010-10-07 05:00:2970 BrowserThread::PostTask(
71 BrowserThread::IO, FROM_HERE,
[email protected]d83d03aa2009-11-02 21:44:3772 NewRunnableMethod(this, &SafeBrowsingService::OnIOShutdown));
initial.commit09911bf2008-07-26 23:55:2973}
74
[email protected]90a3b242009-11-13 00:28:5475bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
[email protected]14aab332010-06-02 14:35:4876 return url.SchemeIs(chrome::kFtpScheme) ||
77 url.SchemeIs(chrome::kHttpScheme) ||
[email protected]90a3b242009-11-13 00:28:5478 url.SchemeIs(chrome::kHttpsScheme);
79}
80
81bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
[email protected]4d3b42a2010-10-07 05:00:2982 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]dec173b2009-11-13 21:53:2683 if (!enabled_)
[email protected]90a3b242009-11-13 00:28:5484 return true;
85
[email protected]14aab332010-06-02 14:35:4886 if (!CanCheckUrl(url))
87 return true;
88
[email protected]cb2a67e2009-11-17 00:48:5389 if (!MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:5490 QueuedCheck check;
91 check.client = client;
92 check.url = url;
93 queued_checks_.push_back(check);
94 return false;
95 }
96
97 std::string list;
98 std::vector<SBPrefix> prefix_hits;
99 std::vector<SBFullHashResult> full_hits;
100 base::Time check_start = base::Time::Now();
101 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
102 &full_hits,
103 protocol_manager_->last_update());
104
105 UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::Time::Now() - check_start);
106
107 if (!prefix_match)
108 return true; // URL is okay.
109
110 // Needs to be asynchronous, since we could be in the constructor of a
111 // ResourceDispatcherHost event handler which can't pause there.
112 SafeBrowsingCheck* check = new SafeBrowsingCheck();
113 check->url = url;
114 check->client = client;
115 check->result = URL_SAFE;
116 check->need_get_hash = full_hits.empty();
117 check->prefix_hits.swap(prefix_hits);
118 check->full_hits.swap(full_hits);
119 checks_.insert(check);
120
[email protected]4d3b42a2010-10-07 05:00:29121 BrowserThread::PostTask(
122 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54123 NewRunnableMethod(this, &SafeBrowsingService::OnCheckDone, check));
124
125 return false;
126}
127
128void SafeBrowsingService::CancelCheck(Client* client) {
[email protected]4d3b42a2010-10-07 05:00:29129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54130 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
[email protected]03addd92009-11-17 21:38:10131 // We can't delete matching checks here because the db thread has a copy of
132 // the pointer. Instead, we simply NULL out the client, and when the db
133 // thread calls us back, we'll clean up the check.
[email protected]90a3b242009-11-13 00:28:54134 if ((*i)->client == client)
135 (*i)->client = NULL;
136 }
137
138 // Scan the queued clients store. Clients may be here if they requested a URL
[email protected]fd220e602009-11-14 01:02:37139 // check before the database has finished loading.
[email protected]dec173b2009-11-13 21:53:26140 for (std::deque<QueuedCheck>::iterator it(queued_checks_.begin());
[email protected]03addd92009-11-17 21:38:10141 it != queued_checks_.end(); ) {
142 // In this case it's safe to delete matches entirely since nothing has a
143 // pointer to them.
[email protected]dec173b2009-11-13 21:53:26144 if (it->client == client)
[email protected]03addd92009-11-17 21:38:10145 it = queued_checks_.erase(it);
146 else
147 ++it;
[email protected]90a3b242009-11-13 00:28:54148 }
149}
150
151void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
[email protected]293f19252010-08-18 20:01:18152 const GURL& original_url,
[email protected]90a3b242009-11-13 00:28:54153 ResourceType::Type resource_type,
154 UrlCheckResult result,
155 Client* client,
156 int render_process_host_id,
157 int render_view_id) {
[email protected]4d3b42a2010-10-07 05:00:29158 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53159
[email protected]90a3b242009-11-13 00:28:54160 // Check if the user has already ignored our warning for this render_view
161 // and domain.
162 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
163 const WhiteListedEntry& entry = white_listed_entries_[i];
164 if (entry.render_process_host_id == render_process_host_id &&
165 entry.render_view_id == render_view_id &&
166 entry.result == result &&
167 entry.domain ==
168 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
169 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
170 this, &SafeBrowsingService::NotifyClientBlockingComplete,
171 client, true));
172 return;
173 }
174 }
175
176 UnsafeResource resource;
177 resource.url = url;
[email protected]293f19252010-08-18 20:01:18178 resource.original_url = original_url;
[email protected]90a3b242009-11-13 00:28:54179 resource.resource_type = resource_type;
180 resource.threat_type= result;
181 resource.client = client;
182 resource.render_process_host_id = render_process_host_id;
183 resource.render_view_id = render_view_id;
184
185 // The blocking page must be created from the UI thread.
[email protected]4d3b42a2010-10-07 05:00:29186 BrowserThread::PostTask(
187 BrowserThread::UI, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54188 NewRunnableMethod(
189 this, &SafeBrowsingService::DoDisplayBlockingPage, resource));
190}
191
192void SafeBrowsingService::HandleGetHashResults(
193 SafeBrowsingCheck* check,
194 const std::vector<SBFullHashResult>& full_hashes,
195 bool can_cache) {
[email protected]4d3b42a2010-10-07 05:00:29196 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54197 if (checks_.find(check) == checks_.end())
198 return;
199
200 DCHECK(enabled_);
201
202 UMA_HISTOGRAM_LONG_TIMES("SB2.Network", Time::Now() - check->start);
203
204 std::vector<SBPrefix> prefixes = check->prefix_hits;
205 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
206
[email protected]cb2a67e2009-11-17 00:48:53207 if (can_cache && MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:54208 // Cache the GetHash results in memory:
209 database_->CacheHashResults(prefixes, full_hashes);
210 }
211}
212
213void SafeBrowsingService::HandleChunk(const std::string& list,
[email protected]7b1e37102010-03-08 21:43:16214 SBChunkList* chunks) {
[email protected]4d3b42a2010-10-07 05:00:29215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54216 DCHECK(enabled_);
217 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
218 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
219}
220
221void SafeBrowsingService::HandleChunkDelete(
222 std::vector<SBChunkDelete>* chunk_deletes) {
[email protected]4d3b42a2010-10-07 05:00:29223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54224 DCHECK(enabled_);
225 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
226 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
227}
228
229void SafeBrowsingService::UpdateStarted() {
[email protected]4d3b42a2010-10-07 05:00:29230 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54231 DCHECK(enabled_);
232 DCHECK(!update_in_progress_);
233 update_in_progress_ = true;
234 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
235 this, &SafeBrowsingService::GetAllChunksFromDatabase));
236}
237
238void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
[email protected]4d3b42a2010-10-07 05:00:29239 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54240 DCHECK(enabled_);
241 if (update_in_progress_) {
242 update_in_progress_ = false;
243 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
244 NewRunnableMethod(this,
245 &SafeBrowsingService::DatabaseUpdateFinished,
246 update_succeeded));
247 }
248}
249
[email protected]894c4e82010-06-29 21:53:18250bool SafeBrowsingService::IsUpdateInProgress() const {
[email protected]4d3b42a2010-10-07 05:00:29251 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]894c4e82010-06-29 21:53:18252 return update_in_progress_;
253}
254
[email protected]90a3b242009-11-13 00:28:54255void SafeBrowsingService::OnBlockingPageDone(
256 const std::vector<UnsafeResource>& resources,
257 bool proceed) {
258 for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
259 iter != resources.end(); ++iter) {
260 const UnsafeResource& resource = *iter;
261 NotifyClientBlockingComplete(resource.client, proceed);
262
263 if (proceed) {
264 // Whitelist this domain and warning type for the given tab.
265 WhiteListedEntry entry;
266 entry.render_process_host_id = resource.render_process_host_id;
267 entry.render_view_id = resource.render_view_id;
268 entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
269 resource.url);
270 entry.result = resource.threat_type;
271 white_listed_entries_.push_back(entry);
272 }
273 }
274}
275
276void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
277 const std::string& wrapped_key) {
278 PrefService* prefs = g_browser_process->local_state();
279 if (prefs) {
[email protected]ddd231e2010-06-29 20:35:19280 prefs->SetString(prefs::kSafeBrowsingClientKey, client_key);
281 prefs->SetString(prefs::kSafeBrowsingWrappedKey, wrapped_key);
[email protected]90a3b242009-11-13 00:28:54282 }
283}
284
285void SafeBrowsingService::OnEnable(bool enabled) {
286 if (enabled)
287 Start();
288 else
289 ShutDown();
290}
291
292// static
293void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
[email protected]20ce516d2010-06-18 02:20:04294 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, "");
295 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, "");
[email protected]90a3b242009-11-13 00:28:54296}
297
[email protected]cb2a67e2009-11-17 00:48:53298void SafeBrowsingService::CloseDatabase() {
[email protected]4d3b42a2010-10-07 05:00:29299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53300
[email protected]03addd92009-11-17 21:38:10301 // Cases to avoid:
302 // * If |closing_database_| is true, continuing will queue up a second
303 // request, |closing_database_| will be reset after handling the first
304 // request, and if any functions on the db thread recreate the database, we
305 // could start using it on the IO thread and then have the second request
306 // handler delete it out from under us.
307 // * If |database_| is NULL, then either no creation request is in flight, in
308 // which case we don't need to do anything, or one is in flight, in which
309 // case the database will be recreated before our deletion request is
310 // handled, and could be used on the IO thread in that time period, leading
311 // to the same problem as above.
312 // * If |queued_checks_| is non-empty and |database_| is non-NULL, we're
313 // about to be called back (in DatabaseLoadComplete()). This will call
314 // CheckUrl(), which will want the database. Closing the database here
315 // would lead to an infinite loop in DatabaseLoadComplete(), and even if it
316 // didn't, it would be pointless since we'd just want to recreate.
317 //
[email protected]34094312009-12-03 21:40:26318 // The first two cases above are handled by checking DatabaseAvailable().
319 if (!DatabaseAvailable() || !queued_checks_.empty())
[email protected]cb2a67e2009-11-17 00:48:53320 return;
321
322 closing_database_ = true;
323 if (safe_browsing_thread_.get()) {
324 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
325 NewRunnableMethod(this, &SafeBrowsingService::OnCloseDatabase));
326 }
327}
328
[email protected]90a3b242009-11-13 00:28:54329void SafeBrowsingService::ResetDatabase() {
[email protected]4d3b42a2010-10-07 05:00:29330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53331 DCHECK(enabled_);
[email protected]90a3b242009-11-13 00:28:54332 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
333 this, &SafeBrowsingService::OnResetDatabase));
334}
335
336void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
337 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
338}
339
340SafeBrowsingService::~SafeBrowsingService() {
[email protected]cb2a67e2009-11-17 00:48:53341 // We should have already been shut down. If we're still enabled, then the
342 // database isn't going to be closed properly, which could lead to corruption.
343 DCHECK(!enabled_);
[email protected]90a3b242009-11-13 00:28:54344}
345
[email protected]d11f5662009-11-12 20:52:56346void SafeBrowsingService::OnIOInitialize(
347 const std::string& client_key,
348 const std::string& wrapped_key,
349 URLRequestContextGetter* request_context_getter) {
[email protected]4d3b42a2010-10-07 05:00:29350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29351 enabled_ = true;
[email protected]cb2a67e2009-11-17 00:48:53352 MakeDatabaseAvailable();
[email protected]1a871512009-11-06 06:11:18353
354 // On Windows, get the safe browsing client name from the browser
355 // distribution classes in installer util. These classes don't yet have
356 // an analog on non-Windows builds so just keep the name specified here.
357#if defined(OS_WIN)
358 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
359 std::string client_name(dist->GetSafeBrowsingName());
360#else
361#if defined(GOOGLE_CHROME_BUILD)
362 std::string client_name("googlechrome");
363#else
364 std::string client_name("chromium");
365#endif
366#endif
[email protected]894c4e82010-06-29 21:53:18367 CommandLine* cmdline = CommandLine::ForCurrentProcess();
[email protected]6142deb2010-09-08 23:21:18368 bool disable_auto_update =
369 cmdline->HasSwitch(switches::kSbDisableAutoUpdate) ||
370 cmdline->HasSwitch(switches::kDisableBackgroundNetworking);
[email protected]894c4e82010-06-29 21:53:18371 std::string info_url_prefix =
372 cmdline->HasSwitch(switches::kSbInfoURLPrefix) ?
373 cmdline->GetSwitchValueASCII(switches::kSbInfoURLPrefix) :
374 kSbDefaultInfoURLPrefix;
375 std::string mackey_url_prefix =
376 cmdline->HasSwitch(switches::kSbMacKeyURLPrefix) ?
377 cmdline->GetSwitchValueASCII(switches::kSbMacKeyURLPrefix) :
378 kSbDefaultMacKeyURLPrefix;
[email protected]1a871512009-11-06 06:11:18379
initial.commit09911bf2008-07-26 23:55:29380 protocol_manager_ = new SafeBrowsingProtocolManager(this,
[email protected]1a871512009-11-06 06:11:18381 client_name,
initial.commit09911bf2008-07-26 23:55:29382 client_key,
[email protected]d11f5662009-11-12 20:52:56383 wrapped_key,
[email protected]894c4e82010-06-29 21:53:18384 request_context_getter,
385 info_url_prefix,
386 mackey_url_prefix,
387 disable_auto_update);
[email protected]d11f5662009-11-12 20:52:56388
[email protected]0d7e79fa2010-10-08 23:35:47389 // Balance the reference added by Start().
390 request_context_getter->Release();
391
[email protected]dec173b2009-11-13 21:53:26392 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29393}
394
initial.commit09911bf2008-07-26 23:55:29395void SafeBrowsingService::OnIOShutdown() {
[email protected]4d3b42a2010-10-07 05:00:29396 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29397 if (!enabled_)
398 return;
399
400 enabled_ = false;
initial.commit09911bf2008-07-26 23:55:29401
402 // This cancels all in-flight GetHash requests.
403 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04404 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29405
[email protected]03addd92009-11-17 21:38:10406 // Delete queued checks, calling back any clients with 'URL_SAFE'.
407 // If we don't do this here we may fail to close the database below.
408 while (!queued_checks_.empty()) {
409 QueuedCheck check = queued_checks_.front();
410 if (check.client)
411 check.client->OnUrlCheckResult(check.url, URL_SAFE);
412 queued_checks_.pop_front();
413 }
414
[email protected]cb2a67e2009-11-17 00:48:53415 // Close the database. We don't simply DeleteSoon() because if a close is
416 // already pending, we'll double-free, and we don't set |database_| to NULL
417 // because if there is still anything running on the db thread, it could
418 // create a new database object (via GetDatabase()) that would then leak.
419 CloseDatabase();
initial.commit09911bf2008-07-26 23:55:29420
421 // Flush the database thread. Any in-progress database check results will be
422 // ignored and cleaned up below.
[email protected]cb2a67e2009-11-17 00:48:53423 //
424 // Note that to avoid leaking the database, we rely on the fact that no new
425 // tasks will be added to the db thread between the call above and this one.
426 // See comments on the declaration of |safe_browsing_thread_|.
427 safe_browsing_thread_.reset();
initial.commit09911bf2008-07-26 23:55:29428
[email protected]03addd92009-11-17 21:38:10429 // Delete pending checks, calling back any clients with 'URL_SAFE'. We have
430 // to do this after the db thread returns because methods on it can have
431 // copies of these pointers, so deleting them might lead to accessing garbage.
initial.commit09911bf2008-07-26 23:55:29432 for (CurrentChecks::iterator it = checks_.begin();
433 it != checks_.end(); ++it) {
434 if ((*it)->client)
435 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
436 delete *it;
437 }
438 checks_.clear();
439
440 gethash_requests_.clear();
441}
442
[email protected]34094312009-12-03 21:40:26443bool SafeBrowsingService::DatabaseAvailable() const {
444 AutoLock lock(database_lock_);
445 return !closing_database_ && (database_ != NULL);
446}
447
[email protected]cb2a67e2009-11-17 00:48:53448bool SafeBrowsingService::MakeDatabaseAvailable() {
[email protected]4d3b42a2010-10-07 05:00:29449 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53450 DCHECK(enabled_);
[email protected]34094312009-12-03 21:40:26451 if (DatabaseAvailable())
452 return true;
453 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
454 NewRunnableMethod(this, &SafeBrowsingService::GetDatabase));
455 return false;
[email protected]cb2a67e2009-11-17 00:48:53456}
457
[email protected]90a3b242009-11-13 00:28:54458SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
[email protected]ef5b4822010-09-09 16:45:49459 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54460 if (database_)
461 return database_;
initial.commit09911bf2008-07-26 23:55:29462
[email protected]90a3b242009-11-13 00:28:54463 FilePath path;
464 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
465 DCHECK(result);
466 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29467
[email protected]90a3b242009-11-13 00:28:54468 Time before = Time::Now();
469 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
[email protected]7b1e37102010-03-08 21:43:16470 database->Init(path);
[email protected]34094312009-12-03 21:40:26471 {
472 // Acquiring the lock here guarantees correct ordering between the writes to
473 // the new database object above, and the setting of |databse_| below.
474 AutoLock lock(database_lock_);
475 database_ = database;
476 }
[email protected]613a03b2008-10-24 23:02:00477
[email protected]4d3b42a2010-10-07 05:00:29478 BrowserThread::PostTask(
479 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54480 NewRunnableMethod(this, &SafeBrowsingService::DatabaseLoadComplete));
[email protected]613a03b2008-10-24 23:02:00481
[email protected]cb2a67e2009-11-17 00:48:53482 UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", Time::Now() - before);
[email protected]90a3b242009-11-13 00:28:54483 return database_;
initial.commit09911bf2008-07-26 23:55:29484}
485
[email protected]613a03b2008-10-24 23:02:00486void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
[email protected]4d3b42a2010-10-07 05:00:29487 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29488
489 // If we've been shutdown during the database lookup, this check will already
490 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00491 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29492 return;
493
[email protected]613a03b2008-10-24 23:02:00494 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29495 // We have a partial match so we need to query Google for the full hash.
496 // Clean up will happen in HandleGetHashResults.
497
498 // See if we have a GetHash request already in progress for this particular
499 // prefix. If so, we just append ourselves to the list of interested parties
500 // when the results arrive. We only do this for checks involving one prefix,
501 // since that is the common case (multiple prefixes will issue the request
502 // as normal).
[email protected]613a03b2008-10-24 23:02:00503 if (check->prefix_hits.size() == 1) {
504 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29505 GetHashRequests::iterator it = gethash_requests_.find(prefix);
506 if (it != gethash_requests_.end()) {
507 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00508 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29509 return;
510 }
511
512 // No request in progress, so we're the first for this prefix.
513 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00514 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29515 gethash_requests_[prefix] = requestors;
516 }
517
518 // Reset the start time so that we can measure the network time without the
519 // database time.
[email protected]613a03b2008-10-24 23:02:00520 check->start = Time::Now();
521 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29522 } else {
523 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00524 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29525 }
526}
527
[email protected]90a3b242009-11-13 00:28:54528void SafeBrowsingService::GetAllChunksFromDatabase() {
[email protected]ef5b4822010-09-09 16:45:49529 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
530
[email protected]90a3b242009-11-13 00:28:54531 bool database_error = true;
532 std::vector<SBListChunkRanges> lists;
[email protected]ef5b4822010-09-09 16:45:49533 DCHECK(!database_update_in_progress_);
534 database_update_in_progress_ = true;
[email protected]cb2a67e2009-11-17 00:48:53535 GetDatabase(); // This guarantees that |database_| is non-NULL.
[email protected]c23161342010-08-18 20:34:04536 if (database_->UpdateStarted(&lists)) {
[email protected]cb2a67e2009-11-17 00:48:53537 database_error = false;
538 } else {
539 database_->UpdateFinished(false);
[email protected]90a3b242009-11-13 00:28:54540 }
[email protected]613a03b2008-10-24 23:02:00541
[email protected]4d3b42a2010-10-07 05:00:29542 BrowserThread::PostTask(
543 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54544 NewRunnableMethod(
545 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
546 database_error));
initial.commit09911bf2008-07-26 23:55:29547}
548
[email protected]90a3b242009-11-13 00:28:54549void SafeBrowsingService::OnGetAllChunksFromDatabase(
550 const std::vector<SBListChunkRanges>& lists, bool database_error) {
[email protected]4d3b42a2010-10-07 05:00:29551 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54552 if (enabled_)
553 protocol_manager_->OnGetChunksComplete(lists, database_error);
554}
555
[email protected]90a3b242009-11-13 00:28:54556void SafeBrowsingService::OnChunkInserted() {
[email protected]4d3b42a2010-10-07 05:00:29557 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54558 if (enabled_)
559 protocol_manager_->OnChunkInserted();
560}
561
562void SafeBrowsingService::DatabaseLoadComplete() {
[email protected]4d3b42a2010-10-07 05:00:29563 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54564 if (!enabled_)
initial.commit09911bf2008-07-26 23:55:29565 return;
566
[email protected]03addd92009-11-17 21:38:10567 HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
568 if (queued_checks_.empty())
569 return;
570
571 // If the database isn't already available, calling CheckUrl() in the loop
572 // below will add the check back to the queue, and we'll infinite-loop.
[email protected]34094312009-12-03 21:40:26573 DCHECK(DatabaseAvailable());
[email protected]03addd92009-11-17 21:38:10574 while (!queued_checks_.empty()) {
575 QueuedCheck check = queued_checks_.front();
576 HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
577 // If CheckUrl() determines the URL is safe immediately, it doesn't call the
578 // client's handler function (because normally it's being directly called by
579 // the client). Since we're not the client, we have to convey this result.
580 if (check.client && CheckUrl(check.url, check.client))
581 check.client->OnUrlCheckResult(check.url, URL_SAFE);
582 queued_checks_.pop_front();
583 }
[email protected]90a3b242009-11-13 00:28:54584}
initial.commit09911bf2008-07-26 23:55:29585
[email protected]90a3b242009-11-13 00:28:54586void SafeBrowsingService::HandleChunkForDatabase(
587 const std::string& list_name,
[email protected]7b1e37102010-03-08 21:43:16588 SBChunkList* chunks) {
[email protected]ef5b4822010-09-09 16:45:49589 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16590 if (chunks) {
591 GetDatabase()->InsertChunks(list_name, *chunks);
592 delete chunks;
593 }
[email protected]4d3b42a2010-10-07 05:00:29594 BrowserThread::PostTask(
595 BrowserThread::IO, FROM_HERE,
[email protected]7b1e37102010-03-08 21:43:16596 NewRunnableMethod(this, &SafeBrowsingService::OnChunkInserted));
[email protected]90a3b242009-11-13 00:28:54597}
598
599void SafeBrowsingService::DeleteChunks(
600 std::vector<SBChunkDelete>* chunk_deletes) {
[email protected]ef5b4822010-09-09 16:45:49601 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16602 if (chunk_deletes) {
603 GetDatabase()->DeleteChunks(*chunk_deletes);
604 delete chunk_deletes;
605 }
[email protected]90a3b242009-11-13 00:28:54606}
607
608SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
609 const std::string& list_name) {
610 if (safe_browsing_util::IsPhishingList(list_name)) {
611 return URL_PHISHING;
[email protected]613a03b2008-10-24 23:02:00612 }
[email protected]90a3b242009-11-13 00:28:54613
614 if (safe_browsing_util::IsMalwareList(list_name)) {
615 return URL_MALWARE;
616 }
617
618 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
619 return URL_SAFE;
620}
621
622void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
623 bool proceed) {
624 client->OnBlockingPageComplete(proceed);
625}
626
627void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
[email protected]ef5b4822010-09-09 16:45:49628 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]cb2a67e2009-11-17 00:48:53629 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]ef5b4822010-09-09 16:45:49630 DCHECK(database_update_in_progress_);
631 database_update_in_progress_ = false;
[email protected]90a3b242009-11-13 00:28:54632}
633
634void SafeBrowsingService::Start() {
635 DCHECK(!safe_browsing_thread_.get());
636 safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
637 if (!safe_browsing_thread_->Start())
638 return;
639
640 // Retrieve client MAC keys.
641 PrefService* local_state = g_browser_process->local_state();
642 std::string client_key, wrapped_key;
643 if (local_state) {
644 client_key =
[email protected]ddd231e2010-06-29 20:35:19645 local_state->GetString(prefs::kSafeBrowsingClientKey);
[email protected]90a3b242009-11-13 00:28:54646 wrapped_key =
[email protected]ddd231e2010-06-29 20:35:19647 local_state->GetString(prefs::kSafeBrowsingWrappedKey);
[email protected]90a3b242009-11-13 00:28:54648 }
649
650 // We will issue network fetches using the default profile's request context.
[email protected]0d7e79fa2010-10-08 23:35:47651 URLRequestContextGetter* request_context_getter =
[email protected]90a3b242009-11-13 00:28:54652 GetDefaultProfile()->GetRequestContext();
[email protected]0d7e79fa2010-10-08 23:35:47653 request_context_getter->AddRef(); // Balanced in OnIOInitialize.
[email protected]90a3b242009-11-13 00:28:54654
[email protected]4d3b42a2010-10-07 05:00:29655 BrowserThread::PostTask(
656 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54657 NewRunnableMethod(
[email protected]0d7e79fa2010-10-08 23:35:47658 this, &SafeBrowsingService::OnIOInitialize, client_key, wrapped_key,
[email protected]90a3b242009-11-13 00:28:54659 request_context_getter));
[email protected]cb2a67e2009-11-17 00:48:53660}
[email protected]90a3b242009-11-13 00:28:54661
[email protected]cb2a67e2009-11-17 00:48:53662void SafeBrowsingService::OnCloseDatabase() {
[email protected]ef5b4822010-09-09 16:45:49663 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]a3aaf3d2009-11-17 22:12:39664 DCHECK(closing_database_);
[email protected]cb2a67e2009-11-17 00:48:53665
666 // Because |closing_database_| is true, nothing on the IO thread will be
667 // accessing the database, so it's safe to delete and then NULL the pointer.
668 delete database_;
669 database_ = NULL;
[email protected]34094312009-12-03 21:40:26670
671 // Acquiring the lock here guarantees correct ordering between the resetting
672 // of |database_| above and of |closing_database_| below, which ensures there
673 // won't be a window during which the IO thread falsely believes the database
674 // is available.
675 AutoLock lock(database_lock_);
[email protected]cb2a67e2009-11-17 00:48:53676 closing_database_ = false;
[email protected]90a3b242009-11-13 00:28:54677}
678
679void SafeBrowsingService::OnResetDatabase() {
[email protected]ef5b4822010-09-09 16:45:49680 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54681 GetDatabase()->ResetDatabase();
[email protected]90a3b242009-11-13 00:28:54682}
683
684void SafeBrowsingService::CacheHashResults(
685 const std::vector<SBPrefix>& prefixes,
686 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]ef5b4822010-09-09 16:45:49687 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54688 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29689}
690
691void SafeBrowsingService::OnHandleGetHashResults(
692 SafeBrowsingCheck* check,
693 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]4d3b42a2010-10-07 05:00:29694 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29695 SBPrefix prefix = check->prefix_hits[0];
696 GetHashRequests::iterator it = gethash_requests_.find(prefix);
697 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
698 HandleOneCheck(check, full_hashes);
699 return;
700 }
701
702 // Call back all interested parties.
703 GetHashRequestors& requestors = it->second;
704 for (GetHashRequestors::iterator r = requestors.begin();
705 r != requestors.end(); ++r) {
706 HandleOneCheck(*r, full_hashes);
707 }
708
709 gethash_requests_.erase(it);
710}
711
712void SafeBrowsingService::HandleOneCheck(
713 SafeBrowsingCheck* check,
714 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]4d3b42a2010-10-07 05:00:29715 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29716 if (check->client) {
717 UrlCheckResult result = URL_SAFE;
718 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
[email protected]09d985f2009-05-11 21:59:58719 if (index != -1) {
initial.commit09911bf2008-07-26 23:55:29720 result = GetResultFromListname(full_hashes[index].list_name);
[email protected]09d985f2009-05-11 21:59:58721 } else {
722 // Log the case where the SafeBrowsing servers return full hashes in the
723 // GetHash response that match the prefix we're looking up, but don't
724 // match the full hash of the URL.
725 if (!full_hashes.empty())
726 UMA_HISTOGRAM_COUNTS("SB2.GetHashServerMiss", 1);
727 }
initial.commit09911bf2008-07-26 23:55:29728
729 // Let the client continue handling the original request.
730 check->client->OnUrlCheckResult(check->url, result);
731 }
732
733 checks_.erase(check);
734 delete check;
735}
736
[email protected]90a3b242009-11-13 00:28:54737void SafeBrowsingService::DoDisplayBlockingPage(
738 const UnsafeResource& resource) {
739 // The tab might have been closed.
740 TabContents* wc =
741 tab_util::GetTabContentsByID(resource.render_process_host_id,
742 resource.render_view_id);
initial.commit09911bf2008-07-26 23:55:29743
[email protected]90a3b242009-11-13 00:28:54744 if (!wc) {
745 // The tab is gone and we did not have a chance at showing the interstitial.
746 // Just act as "Don't Proceed" was chosen.
747 std::vector<UnsafeResource> resources;
748 resources.push_back(resource);
[email protected]4d3b42a2010-10-07 05:00:29749 BrowserThread::PostTask(
750 BrowserThread::IO, FROM_HERE,
[email protected]d83d03aa2009-11-02 21:44:37751 NewRunnableMethod(
[email protected]90a3b242009-11-13 00:28:54752 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
753 return;
initial.commit09911bf2008-07-26 23:55:29754 }
755
[email protected]036237d42010-10-07 22:41:58756 // Report the malicious resource to the SafeBrowsing servers if the user has
757 // opted in to reporting statistics.
[email protected]975bead2009-11-30 21:59:53758 const MetricsService* metrics = g_browser_process->metrics_service();
759 DCHECK(metrics);
760 if (metrics && metrics->reporting_active() &&
[email protected]036237d42010-10-07 22:41:58761 (resource.threat_type == SafeBrowsingService::URL_MALWARE ||
762 resource.threat_type == SafeBrowsingService::URL_PHISHING)) {
[email protected]90a3b242009-11-13 00:28:54763 GURL page_url = wc->GetURL();
764 GURL referrer_url;
765 NavigationEntry* entry = wc->controller().GetActiveEntry();
766 if (entry)
767 referrer_url = entry->referrer();
[email protected]293f19252010-08-18 20:01:18768 bool is_subresource = resource.resource_type != ResourceType::MAIN_FRAME;
[email protected]ee4d3e802010-08-12 23:59:51769
[email protected]293f19252010-08-18 20:01:18770 // When the malicious url is on the main frame, and resource.original_url
771 // is not the same as the resource.url, that means we have a redirect from
772 // resource.original_url to resource.url.
773 // Also, at this point, page_url points to the _previous_ page that we
774 // were on. We replace page_url with resource.original_url and referrer
775 // with page_url.
776 if (!is_subresource &&
777 !resource.original_url.is_empty() &&
778 resource.original_url != resource.url) {
779 referrer_url = page_url;
780 page_url = resource.original_url;
781 }
782
[email protected]036237d42010-10-07 22:41:58783 BrowserThread::PostTask(
784 BrowserThread::IO, FROM_HERE,
785 NewRunnableMethod(
786 this,
787 &SafeBrowsingService::ReportSafeBrowsingHit,
788 resource.url,
789 page_url,
790 referrer_url,
791 is_subresource,
792 resource.threat_type));
initial.commit09911bf2008-07-26 23:55:29793 }
794
[email protected]90a3b242009-11-13 00:28:54795 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
[email protected]613a03b2008-10-24 23:02:00796}
797
[email protected]036237d42010-10-07 22:41:58798void SafeBrowsingService::ReportSafeBrowsingHit(
799 const GURL& malicious_url,
800 const GURL& page_url,
801 const GURL& referrer_url,
802 bool is_subresource,
803 SafeBrowsingService::UrlCheckResult threat_type) {
[email protected]4d3b42a2010-10-07 05:00:29804 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]fd220e602009-11-14 01:02:37805 if (!enabled_)
[email protected]dfdb0de72009-02-19 21:58:14806 return;
807
[email protected]036237d42010-10-07 22:41:58808 DLOG(INFO) << "ReportSafeBrowsingHit: " << malicious_url << " " << page_url
809 << " " << referrer_url << " " << is_subresource
810 << " " << threat_type;
811 protocol_manager_->ReportSafeBrowsingHit(malicious_url, page_url,
812 referrer_url, is_subresource,
813 threat_type);
[email protected]dfdb0de72009-02-19 21:58:14814}