blob: 781d7613de88457bd90b437d953c4aca07bc7bdb [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]dec173b2009-11-13 21:53:26389 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29390}
391
initial.commit09911bf2008-07-26 23:55:29392void SafeBrowsingService::OnIOShutdown() {
[email protected]4d3b42a2010-10-07 05:00:29393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29394 if (!enabled_)
395 return;
396
397 enabled_ = false;
initial.commit09911bf2008-07-26 23:55:29398
399 // This cancels all in-flight GetHash requests.
400 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04401 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29402
[email protected]03addd92009-11-17 21:38:10403 // Delete queued checks, calling back any clients with 'URL_SAFE'.
404 // If we don't do this here we may fail to close the database below.
405 while (!queued_checks_.empty()) {
406 QueuedCheck check = queued_checks_.front();
407 if (check.client)
408 check.client->OnUrlCheckResult(check.url, URL_SAFE);
409 queued_checks_.pop_front();
410 }
411
[email protected]cb2a67e2009-11-17 00:48:53412 // Close the database. We don't simply DeleteSoon() because if a close is
413 // already pending, we'll double-free, and we don't set |database_| to NULL
414 // because if there is still anything running on the db thread, it could
415 // create a new database object (via GetDatabase()) that would then leak.
416 CloseDatabase();
initial.commit09911bf2008-07-26 23:55:29417
418 // Flush the database thread. Any in-progress database check results will be
419 // ignored and cleaned up below.
[email protected]cb2a67e2009-11-17 00:48:53420 //
421 // Note that to avoid leaking the database, we rely on the fact that no new
422 // tasks will be added to the db thread between the call above and this one.
423 // See comments on the declaration of |safe_browsing_thread_|.
424 safe_browsing_thread_.reset();
initial.commit09911bf2008-07-26 23:55:29425
[email protected]03addd92009-11-17 21:38:10426 // Delete pending checks, calling back any clients with 'URL_SAFE'. We have
427 // to do this after the db thread returns because methods on it can have
428 // copies of these pointers, so deleting them might lead to accessing garbage.
initial.commit09911bf2008-07-26 23:55:29429 for (CurrentChecks::iterator it = checks_.begin();
430 it != checks_.end(); ++it) {
431 if ((*it)->client)
432 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
433 delete *it;
434 }
435 checks_.clear();
436
437 gethash_requests_.clear();
438}
439
[email protected]34094312009-12-03 21:40:26440bool SafeBrowsingService::DatabaseAvailable() const {
441 AutoLock lock(database_lock_);
442 return !closing_database_ && (database_ != NULL);
443}
444
[email protected]cb2a67e2009-11-17 00:48:53445bool SafeBrowsingService::MakeDatabaseAvailable() {
[email protected]4d3b42a2010-10-07 05:00:29446 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53447 DCHECK(enabled_);
[email protected]34094312009-12-03 21:40:26448 if (DatabaseAvailable())
449 return true;
450 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
451 NewRunnableMethod(this, &SafeBrowsingService::GetDatabase));
452 return false;
[email protected]cb2a67e2009-11-17 00:48:53453}
454
[email protected]90a3b242009-11-13 00:28:54455SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
[email protected]ef5b4822010-09-09 16:45:49456 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54457 if (database_)
458 return database_;
initial.commit09911bf2008-07-26 23:55:29459
[email protected]90a3b242009-11-13 00:28:54460 FilePath path;
461 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
462 DCHECK(result);
463 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29464
[email protected]90a3b242009-11-13 00:28:54465 Time before = Time::Now();
466 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
[email protected]7b1e37102010-03-08 21:43:16467 database->Init(path);
[email protected]34094312009-12-03 21:40:26468 {
469 // Acquiring the lock here guarantees correct ordering between the writes to
470 // the new database object above, and the setting of |databse_| below.
471 AutoLock lock(database_lock_);
472 database_ = database;
473 }
[email protected]613a03b2008-10-24 23:02:00474
[email protected]4d3b42a2010-10-07 05:00:29475 BrowserThread::PostTask(
476 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54477 NewRunnableMethod(this, &SafeBrowsingService::DatabaseLoadComplete));
[email protected]613a03b2008-10-24 23:02:00478
[email protected]cb2a67e2009-11-17 00:48:53479 UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", Time::Now() - before);
[email protected]90a3b242009-11-13 00:28:54480 return database_;
initial.commit09911bf2008-07-26 23:55:29481}
482
[email protected]613a03b2008-10-24 23:02:00483void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
[email protected]4d3b42a2010-10-07 05:00:29484 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29485
486 // If we've been shutdown during the database lookup, this check will already
487 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00488 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29489 return;
490
[email protected]613a03b2008-10-24 23:02:00491 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29492 // We have a partial match so we need to query Google for the full hash.
493 // Clean up will happen in HandleGetHashResults.
494
495 // See if we have a GetHash request already in progress for this particular
496 // prefix. If so, we just append ourselves to the list of interested parties
497 // when the results arrive. We only do this for checks involving one prefix,
498 // since that is the common case (multiple prefixes will issue the request
499 // as normal).
[email protected]613a03b2008-10-24 23:02:00500 if (check->prefix_hits.size() == 1) {
501 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29502 GetHashRequests::iterator it = gethash_requests_.find(prefix);
503 if (it != gethash_requests_.end()) {
504 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00505 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29506 return;
507 }
508
509 // No request in progress, so we're the first for this prefix.
510 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00511 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29512 gethash_requests_[prefix] = requestors;
513 }
514
515 // Reset the start time so that we can measure the network time without the
516 // database time.
[email protected]613a03b2008-10-24 23:02:00517 check->start = Time::Now();
518 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29519 } else {
520 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00521 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29522 }
523}
524
[email protected]90a3b242009-11-13 00:28:54525void SafeBrowsingService::GetAllChunksFromDatabase() {
[email protected]ef5b4822010-09-09 16:45:49526 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
527
[email protected]90a3b242009-11-13 00:28:54528 bool database_error = true;
529 std::vector<SBListChunkRanges> lists;
[email protected]ef5b4822010-09-09 16:45:49530 DCHECK(!database_update_in_progress_);
531 database_update_in_progress_ = true;
[email protected]cb2a67e2009-11-17 00:48:53532 GetDatabase(); // This guarantees that |database_| is non-NULL.
[email protected]c23161342010-08-18 20:34:04533 if (database_->UpdateStarted(&lists)) {
[email protected]cb2a67e2009-11-17 00:48:53534 database_error = false;
535 } else {
536 database_->UpdateFinished(false);
[email protected]90a3b242009-11-13 00:28:54537 }
[email protected]613a03b2008-10-24 23:02:00538
[email protected]4d3b42a2010-10-07 05:00:29539 BrowserThread::PostTask(
540 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54541 NewRunnableMethod(
542 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
543 database_error));
initial.commit09911bf2008-07-26 23:55:29544}
545
[email protected]90a3b242009-11-13 00:28:54546void SafeBrowsingService::OnGetAllChunksFromDatabase(
547 const std::vector<SBListChunkRanges>& lists, bool database_error) {
[email protected]4d3b42a2010-10-07 05:00:29548 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54549 if (enabled_)
550 protocol_manager_->OnGetChunksComplete(lists, database_error);
551}
552
[email protected]90a3b242009-11-13 00:28:54553void SafeBrowsingService::OnChunkInserted() {
[email protected]4d3b42a2010-10-07 05:00:29554 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54555 if (enabled_)
556 protocol_manager_->OnChunkInserted();
557}
558
559void SafeBrowsingService::DatabaseLoadComplete() {
[email protected]4d3b42a2010-10-07 05:00:29560 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54561 if (!enabled_)
initial.commit09911bf2008-07-26 23:55:29562 return;
563
[email protected]03addd92009-11-17 21:38:10564 HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
565 if (queued_checks_.empty())
566 return;
567
568 // If the database isn't already available, calling CheckUrl() in the loop
569 // below will add the check back to the queue, and we'll infinite-loop.
[email protected]34094312009-12-03 21:40:26570 DCHECK(DatabaseAvailable());
[email protected]03addd92009-11-17 21:38:10571 while (!queued_checks_.empty()) {
572 QueuedCheck check = queued_checks_.front();
573 HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
574 // If CheckUrl() determines the URL is safe immediately, it doesn't call the
575 // client's handler function (because normally it's being directly called by
576 // the client). Since we're not the client, we have to convey this result.
577 if (check.client && CheckUrl(check.url, check.client))
578 check.client->OnUrlCheckResult(check.url, URL_SAFE);
579 queued_checks_.pop_front();
580 }
[email protected]90a3b242009-11-13 00:28:54581}
initial.commit09911bf2008-07-26 23:55:29582
[email protected]90a3b242009-11-13 00:28:54583void SafeBrowsingService::HandleChunkForDatabase(
584 const std::string& list_name,
[email protected]7b1e37102010-03-08 21:43:16585 SBChunkList* chunks) {
[email protected]ef5b4822010-09-09 16:45:49586 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16587 if (chunks) {
588 GetDatabase()->InsertChunks(list_name, *chunks);
589 delete chunks;
590 }
[email protected]4d3b42a2010-10-07 05:00:29591 BrowserThread::PostTask(
592 BrowserThread::IO, FROM_HERE,
[email protected]7b1e37102010-03-08 21:43:16593 NewRunnableMethod(this, &SafeBrowsingService::OnChunkInserted));
[email protected]90a3b242009-11-13 00:28:54594}
595
596void SafeBrowsingService::DeleteChunks(
597 std::vector<SBChunkDelete>* chunk_deletes) {
[email protected]ef5b4822010-09-09 16:45:49598 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16599 if (chunk_deletes) {
600 GetDatabase()->DeleteChunks(*chunk_deletes);
601 delete chunk_deletes;
602 }
[email protected]90a3b242009-11-13 00:28:54603}
604
605SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
606 const std::string& list_name) {
607 if (safe_browsing_util::IsPhishingList(list_name)) {
608 return URL_PHISHING;
[email protected]613a03b2008-10-24 23:02:00609 }
[email protected]90a3b242009-11-13 00:28:54610
611 if (safe_browsing_util::IsMalwareList(list_name)) {
612 return URL_MALWARE;
613 }
614
615 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
616 return URL_SAFE;
617}
618
619void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
620 bool proceed) {
621 client->OnBlockingPageComplete(proceed);
622}
623
624void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
[email protected]ef5b4822010-09-09 16:45:49625 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]cb2a67e2009-11-17 00:48:53626 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]ef5b4822010-09-09 16:45:49627 DCHECK(database_update_in_progress_);
628 database_update_in_progress_ = false;
[email protected]90a3b242009-11-13 00:28:54629}
630
631void SafeBrowsingService::Start() {
632 DCHECK(!safe_browsing_thread_.get());
633 safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
634 if (!safe_browsing_thread_->Start())
635 return;
636
637 // Retrieve client MAC keys.
638 PrefService* local_state = g_browser_process->local_state();
639 std::string client_key, wrapped_key;
640 if (local_state) {
641 client_key =
[email protected]ddd231e2010-06-29 20:35:19642 local_state->GetString(prefs::kSafeBrowsingClientKey);
[email protected]90a3b242009-11-13 00:28:54643 wrapped_key =
[email protected]ddd231e2010-06-29 20:35:19644 local_state->GetString(prefs::kSafeBrowsingWrappedKey);
[email protected]90a3b242009-11-13 00:28:54645 }
646
647 // We will issue network fetches using the default profile's request context.
[email protected]41a95c42010-10-20 15:49:15648 scoped_refptr<URLRequestContextGetter> request_context_getter =
[email protected]90a3b242009-11-13 00:28:54649 GetDefaultProfile()->GetRequestContext();
[email protected]90a3b242009-11-13 00:28:54650
[email protected]4d3b42a2010-10-07 05:00:29651 BrowserThread::PostTask(
652 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54653 NewRunnableMethod(
[email protected]0d7e79fa2010-10-08 23:35:47654 this, &SafeBrowsingService::OnIOInitialize, client_key, wrapped_key,
[email protected]90a3b242009-11-13 00:28:54655 request_context_getter));
[email protected]cb2a67e2009-11-17 00:48:53656}
[email protected]90a3b242009-11-13 00:28:54657
[email protected]cb2a67e2009-11-17 00:48:53658void SafeBrowsingService::OnCloseDatabase() {
[email protected]ef5b4822010-09-09 16:45:49659 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]a3aaf3d2009-11-17 22:12:39660 DCHECK(closing_database_);
[email protected]cb2a67e2009-11-17 00:48:53661
662 // Because |closing_database_| is true, nothing on the IO thread will be
663 // accessing the database, so it's safe to delete and then NULL the pointer.
664 delete database_;
665 database_ = NULL;
[email protected]34094312009-12-03 21:40:26666
667 // Acquiring the lock here guarantees correct ordering between the resetting
668 // of |database_| above and of |closing_database_| below, which ensures there
669 // won't be a window during which the IO thread falsely believes the database
670 // is available.
671 AutoLock lock(database_lock_);
[email protected]cb2a67e2009-11-17 00:48:53672 closing_database_ = false;
[email protected]90a3b242009-11-13 00:28:54673}
674
675void SafeBrowsingService::OnResetDatabase() {
[email protected]ef5b4822010-09-09 16:45:49676 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54677 GetDatabase()->ResetDatabase();
[email protected]90a3b242009-11-13 00:28:54678}
679
680void SafeBrowsingService::CacheHashResults(
681 const std::vector<SBPrefix>& prefixes,
682 const std::vector<SBFullHashResult>& full_hashes) {
[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()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29685}
686
687void SafeBrowsingService::OnHandleGetHashResults(
688 SafeBrowsingCheck* check,
689 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]4d3b42a2010-10-07 05:00:29690 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29691 SBPrefix prefix = check->prefix_hits[0];
692 GetHashRequests::iterator it = gethash_requests_.find(prefix);
693 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
694 HandleOneCheck(check, full_hashes);
695 return;
696 }
697
698 // Call back all interested parties.
699 GetHashRequestors& requestors = it->second;
700 for (GetHashRequestors::iterator r = requestors.begin();
701 r != requestors.end(); ++r) {
702 HandleOneCheck(*r, full_hashes);
703 }
704
705 gethash_requests_.erase(it);
706}
707
708void SafeBrowsingService::HandleOneCheck(
709 SafeBrowsingCheck* check,
710 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]4d3b42a2010-10-07 05:00:29711 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29712 if (check->client) {
713 UrlCheckResult result = URL_SAFE;
714 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
[email protected]09d985f2009-05-11 21:59:58715 if (index != -1) {
initial.commit09911bf2008-07-26 23:55:29716 result = GetResultFromListname(full_hashes[index].list_name);
[email protected]09d985f2009-05-11 21:59:58717 } else {
718 // Log the case where the SafeBrowsing servers return full hashes in the
719 // GetHash response that match the prefix we're looking up, but don't
720 // match the full hash of the URL.
721 if (!full_hashes.empty())
722 UMA_HISTOGRAM_COUNTS("SB2.GetHashServerMiss", 1);
723 }
initial.commit09911bf2008-07-26 23:55:29724
725 // Let the client continue handling the original request.
726 check->client->OnUrlCheckResult(check->url, result);
727 }
728
729 checks_.erase(check);
730 delete check;
731}
732
[email protected]90a3b242009-11-13 00:28:54733void SafeBrowsingService::DoDisplayBlockingPage(
734 const UnsafeResource& resource) {
735 // The tab might have been closed.
736 TabContents* wc =
737 tab_util::GetTabContentsByID(resource.render_process_host_id,
738 resource.render_view_id);
initial.commit09911bf2008-07-26 23:55:29739
[email protected]90a3b242009-11-13 00:28:54740 if (!wc) {
741 // The tab is gone and we did not have a chance at showing the interstitial.
742 // Just act as "Don't Proceed" was chosen.
743 std::vector<UnsafeResource> resources;
744 resources.push_back(resource);
[email protected]4d3b42a2010-10-07 05:00:29745 BrowserThread::PostTask(
746 BrowserThread::IO, FROM_HERE,
[email protected]d83d03aa2009-11-02 21:44:37747 NewRunnableMethod(
[email protected]90a3b242009-11-13 00:28:54748 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
749 return;
initial.commit09911bf2008-07-26 23:55:29750 }
751
[email protected]036237d42010-10-07 22:41:58752 // Report the malicious resource to the SafeBrowsing servers if the user has
753 // opted in to reporting statistics.
[email protected]975bead2009-11-30 21:59:53754 const MetricsService* metrics = g_browser_process->metrics_service();
755 DCHECK(metrics);
756 if (metrics && metrics->reporting_active() &&
[email protected]036237d42010-10-07 22:41:58757 (resource.threat_type == SafeBrowsingService::URL_MALWARE ||
758 resource.threat_type == SafeBrowsingService::URL_PHISHING)) {
[email protected]90a3b242009-11-13 00:28:54759 GURL page_url = wc->GetURL();
760 GURL referrer_url;
761 NavigationEntry* entry = wc->controller().GetActiveEntry();
762 if (entry)
763 referrer_url = entry->referrer();
[email protected]293f19252010-08-18 20:01:18764 bool is_subresource = resource.resource_type != ResourceType::MAIN_FRAME;
[email protected]ee4d3e802010-08-12 23:59:51765
[email protected]293f19252010-08-18 20:01:18766 // When the malicious url is on the main frame, and resource.original_url
767 // is not the same as the resource.url, that means we have a redirect from
768 // resource.original_url to resource.url.
769 // Also, at this point, page_url points to the _previous_ page that we
770 // were on. We replace page_url with resource.original_url and referrer
771 // with page_url.
772 if (!is_subresource &&
773 !resource.original_url.is_empty() &&
774 resource.original_url != resource.url) {
775 referrer_url = page_url;
776 page_url = resource.original_url;
777 }
778
[email protected]036237d42010-10-07 22:41:58779 BrowserThread::PostTask(
780 BrowserThread::IO, FROM_HERE,
781 NewRunnableMethod(
782 this,
783 &SafeBrowsingService::ReportSafeBrowsingHit,
784 resource.url,
785 page_url,
786 referrer_url,
787 is_subresource,
788 resource.threat_type));
initial.commit09911bf2008-07-26 23:55:29789 }
790
[email protected]90a3b242009-11-13 00:28:54791 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
[email protected]613a03b2008-10-24 23:02:00792}
793
[email protected]036237d42010-10-07 22:41:58794void SafeBrowsingService::ReportSafeBrowsingHit(
795 const GURL& malicious_url,
796 const GURL& page_url,
797 const GURL& referrer_url,
798 bool is_subresource,
799 SafeBrowsingService::UrlCheckResult threat_type) {
[email protected]4d3b42a2010-10-07 05:00:29800 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]fd220e602009-11-14 01:02:37801 if (!enabled_)
[email protected]dfdb0de72009-02-19 21:58:14802 return;
803
[email protected]036237d42010-10-07 22:41:58804 DLOG(INFO) << "ReportSafeBrowsingHit: " << malicious_url << " " << page_url
805 << " " << referrer_url << " " << is_subresource
806 << " " << threat_type;
807 protocol_manager_->ReportSafeBrowsingHit(malicious_url, page_url,
808 referrer_url, is_subresource,
809 threat_type);
[email protected]dfdb0de72009-02-19 21:58:14810}