blob: d3dcce79454426572bb11d4c6e090a5c66575acc [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#include "chrome/browser/safe_browsing/safe_browsing_service.h"
6
[email protected]2041cf342010-02-19 03:15:597#include "base/callback.h"
[email protected]894c4e82010-06-29 21:53:188#include "base/command_line.h"
initial.commit09911bf2008-07-26 23:55:299#include "base/path_service.h"
10#include "base/string_util.h"
11#include "chrome/browser/browser_process.h"
[email protected]017a7a112010-10-12 16:38:2712#include "chrome/browser/browser_thread.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"
initial.commit09911bf2008-07-26 23:55:2921#include "chrome/common/chrome_constants.h"
22#include "chrome/common/chrome_paths.h"
[email protected]894c4e82010-06-29 21:53:1823#include "chrome/common/chrome_switches.h"
[email protected]68d2a05f2010-05-07 21:39:5524#include "chrome/common/net/url_request_context_getter.h"
initial.commit09911bf2008-07-26 23:55:2925#include "chrome/common/pref_names.h"
[email protected]dcf7d352009-02-26 01:56:0226#include "chrome/common/url_constants.h"
[email protected]90a3b242009-11-13 00:28:5427#include "net/base/registry_controlled_domain.h"
28
[email protected]1a871512009-11-06 06:11:1829#if defined(OS_WIN)
30#include "chrome/installer/util/browser_distribution.h"
31#endif
initial.commit09911bf2008-07-26 23:55:2932
[email protected]e1acf6f2008-10-27 20:43:3333using base::Time;
34using base::TimeDelta;
35
[email protected]894c4e82010-06-29 21:53:1836// The default URL prefix where browser fetches chunk updates, hashes,
37// and reports malware.
38static const char* const kSbDefaultInfoURLPrefix =
39 "http://safebrowsing.clients.google.com/safebrowsing";
40
41// The default URL prefix where browser fetches MAC client key.
42static const char* const kSbDefaultMacKeyURLPrefix =
43 "https://sb-ssl.google.com/safebrowsing";
44
[email protected]d11f5662009-11-12 20:52:5645static Profile* GetDefaultProfile() {
46 FilePath user_data_dir;
47 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
48 ProfileManager* profile_manager = g_browser_process->profile_manager();
49 return profile_manager->GetDefaultProfile(user_data_dir);
50}
51
[email protected]93aa89c72010-10-20 21:32:0452struct SafeBrowsingService::WhiteListedEntry {
53 int render_process_host_id;
54 int render_view_id;
55 std::string domain;
56 UrlCheckResult result;
57};
58
[email protected]38e08982010-10-22 17:28:4359SafeBrowsingService::UnsafeResource::UnsafeResource()
60 : resource_type(ResourceType::MAIN_FRAME),
61 client(NULL),
62 render_process_host_id(-1),
63 render_view_id(-1) {
64}
65
66SafeBrowsingService::UnsafeResource::~UnsafeResource() {}
67
68SafeBrowsingService::SafeBrowsingCheck::SafeBrowsingCheck()
69 : client(NULL),
70 need_get_hash(false) {
71}
72
73SafeBrowsingService::SafeBrowsingCheck::~SafeBrowsingCheck() {}
74
initial.commit09911bf2008-07-26 23:55:2975SafeBrowsingService::SafeBrowsingService()
[email protected]d83d03aa2009-11-02 21:44:3776 : database_(NULL),
initial.commit09911bf2008-07-26 23:55:2977 protocol_manager_(NULL),
78 enabled_(false),
[email protected]cb2a67e2009-11-17 00:48:5379 update_in_progress_(false),
[email protected]ef5b4822010-09-09 16:45:4980 database_update_in_progress_(false),
[email protected]cb2a67e2009-11-17 00:48:5381 closing_database_(false) {
initial.commit09911bf2008-07-26 23:55:2982}
83
[email protected]d83d03aa2009-11-02 21:44:3784void SafeBrowsingService::Initialize() {
initial.commit09911bf2008-07-26 23:55:2985 // Get the profile's preference for SafeBrowsing.
[email protected]d11f5662009-11-12 20:52:5686 PrefService* pref_service = GetDefaultProfile()->GetPrefs();
initial.commit09911bf2008-07-26 23:55:2987 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
88 Start();
89}
90
initial.commit09911bf2008-07-26 23:55:2991void SafeBrowsingService::ShutDown() {
[email protected]4d3b42a2010-10-07 05:00:2992 BrowserThread::PostTask(
93 BrowserThread::IO, FROM_HERE,
[email protected]d83d03aa2009-11-02 21:44:3794 NewRunnableMethod(this, &SafeBrowsingService::OnIOShutdown));
initial.commit09911bf2008-07-26 23:55:2995}
96
[email protected]90a3b242009-11-13 00:28:5497bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
[email protected]14aab332010-06-02 14:35:4898 return url.SchemeIs(chrome::kFtpScheme) ||
99 url.SchemeIs(chrome::kHttpScheme) ||
[email protected]90a3b242009-11-13 00:28:54100 url.SchemeIs(chrome::kHttpsScheme);
101}
102
103bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
[email protected]4d3b42a2010-10-07 05:00:29104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]dec173b2009-11-13 21:53:26105 if (!enabled_)
[email protected]90a3b242009-11-13 00:28:54106 return true;
107
[email protected]14aab332010-06-02 14:35:48108 if (!CanCheckUrl(url))
109 return true;
110
[email protected]cb2a67e2009-11-17 00:48:53111 if (!MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:54112 QueuedCheck check;
113 check.client = client;
114 check.url = url;
115 queued_checks_.push_back(check);
116 return false;
117 }
118
119 std::string list;
120 std::vector<SBPrefix> prefix_hits;
121 std::vector<SBFullHashResult> full_hits;
122 base::Time check_start = base::Time::Now();
123 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
124 &full_hits,
125 protocol_manager_->last_update());
126
127 UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::Time::Now() - check_start);
128
129 if (!prefix_match)
130 return true; // URL is okay.
131
132 // Needs to be asynchronous, since we could be in the constructor of a
133 // ResourceDispatcherHost event handler which can't pause there.
134 SafeBrowsingCheck* check = new SafeBrowsingCheck();
135 check->url = url;
136 check->client = client;
137 check->result = URL_SAFE;
138 check->need_get_hash = full_hits.empty();
139 check->prefix_hits.swap(prefix_hits);
140 check->full_hits.swap(full_hits);
141 checks_.insert(check);
142
[email protected]4d3b42a2010-10-07 05:00:29143 BrowserThread::PostTask(
144 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54145 NewRunnableMethod(this, &SafeBrowsingService::OnCheckDone, check));
146
147 return false;
148}
149
150void SafeBrowsingService::CancelCheck(Client* client) {
[email protected]4d3b42a2010-10-07 05:00:29151 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54152 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
[email protected]03addd92009-11-17 21:38:10153 // We can't delete matching checks here because the db thread has a copy of
154 // the pointer. Instead, we simply NULL out the client, and when the db
155 // thread calls us back, we'll clean up the check.
[email protected]90a3b242009-11-13 00:28:54156 if ((*i)->client == client)
157 (*i)->client = NULL;
158 }
159
160 // Scan the queued clients store. Clients may be here if they requested a URL
[email protected]fd220e602009-11-14 01:02:37161 // check before the database has finished loading.
[email protected]dec173b2009-11-13 21:53:26162 for (std::deque<QueuedCheck>::iterator it(queued_checks_.begin());
[email protected]03addd92009-11-17 21:38:10163 it != queued_checks_.end(); ) {
164 // In this case it's safe to delete matches entirely since nothing has a
165 // pointer to them.
[email protected]dec173b2009-11-13 21:53:26166 if (it->client == client)
[email protected]03addd92009-11-17 21:38:10167 it = queued_checks_.erase(it);
168 else
169 ++it;
[email protected]90a3b242009-11-13 00:28:54170 }
171}
172
173void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
[email protected]293f19252010-08-18 20:01:18174 const GURL& original_url,
[email protected]90a3b242009-11-13 00:28:54175 ResourceType::Type resource_type,
176 UrlCheckResult result,
177 Client* client,
178 int render_process_host_id,
179 int render_view_id) {
[email protected]4d3b42a2010-10-07 05:00:29180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53181
[email protected]90a3b242009-11-13 00:28:54182 // Check if the user has already ignored our warning for this render_view
183 // and domain.
184 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
185 const WhiteListedEntry& entry = white_listed_entries_[i];
186 if (entry.render_process_host_id == render_process_host_id &&
187 entry.render_view_id == render_view_id &&
188 entry.result == result &&
189 entry.domain ==
190 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
191 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
192 this, &SafeBrowsingService::NotifyClientBlockingComplete,
193 client, true));
194 return;
195 }
196 }
197
198 UnsafeResource resource;
199 resource.url = url;
[email protected]293f19252010-08-18 20:01:18200 resource.original_url = original_url;
[email protected]90a3b242009-11-13 00:28:54201 resource.resource_type = resource_type;
202 resource.threat_type= result;
203 resource.client = client;
204 resource.render_process_host_id = render_process_host_id;
205 resource.render_view_id = render_view_id;
206
207 // The blocking page must be created from the UI thread.
[email protected]4d3b42a2010-10-07 05:00:29208 BrowserThread::PostTask(
209 BrowserThread::UI, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54210 NewRunnableMethod(
211 this, &SafeBrowsingService::DoDisplayBlockingPage, resource));
212}
213
214void SafeBrowsingService::HandleGetHashResults(
215 SafeBrowsingCheck* check,
216 const std::vector<SBFullHashResult>& full_hashes,
217 bool can_cache) {
[email protected]4d3b42a2010-10-07 05:00:29218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54219 if (checks_.find(check) == checks_.end())
220 return;
221
222 DCHECK(enabled_);
223
224 UMA_HISTOGRAM_LONG_TIMES("SB2.Network", Time::Now() - check->start);
225
226 std::vector<SBPrefix> prefixes = check->prefix_hits;
227 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
228
[email protected]cb2a67e2009-11-17 00:48:53229 if (can_cache && MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:54230 // Cache the GetHash results in memory:
231 database_->CacheHashResults(prefixes, full_hashes);
232 }
233}
234
235void SafeBrowsingService::HandleChunk(const std::string& list,
[email protected]7b1e37102010-03-08 21:43:16236 SBChunkList* chunks) {
[email protected]4d3b42a2010-10-07 05:00:29237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54238 DCHECK(enabled_);
239 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
240 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
241}
242
243void SafeBrowsingService::HandleChunkDelete(
244 std::vector<SBChunkDelete>* chunk_deletes) {
[email protected]4d3b42a2010-10-07 05:00:29245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54246 DCHECK(enabled_);
247 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
248 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
249}
250
251void SafeBrowsingService::UpdateStarted() {
[email protected]4d3b42a2010-10-07 05:00:29252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54253 DCHECK(enabled_);
254 DCHECK(!update_in_progress_);
255 update_in_progress_ = true;
256 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
257 this, &SafeBrowsingService::GetAllChunksFromDatabase));
258}
259
260void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
[email protected]4d3b42a2010-10-07 05:00:29261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54262 DCHECK(enabled_);
263 if (update_in_progress_) {
264 update_in_progress_ = false;
265 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
266 NewRunnableMethod(this,
267 &SafeBrowsingService::DatabaseUpdateFinished,
268 update_succeeded));
269 }
270}
271
[email protected]894c4e82010-06-29 21:53:18272bool SafeBrowsingService::IsUpdateInProgress() const {
[email protected]4d3b42a2010-10-07 05:00:29273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]894c4e82010-06-29 21:53:18274 return update_in_progress_;
275}
276
[email protected]90a3b242009-11-13 00:28:54277void SafeBrowsingService::OnBlockingPageDone(
278 const std::vector<UnsafeResource>& resources,
279 bool proceed) {
280 for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
281 iter != resources.end(); ++iter) {
282 const UnsafeResource& resource = *iter;
283 NotifyClientBlockingComplete(resource.client, proceed);
284
285 if (proceed) {
286 // Whitelist this domain and warning type for the given tab.
287 WhiteListedEntry entry;
288 entry.render_process_host_id = resource.render_process_host_id;
289 entry.render_view_id = resource.render_view_id;
290 entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
291 resource.url);
292 entry.result = resource.threat_type;
293 white_listed_entries_.push_back(entry);
294 }
295 }
296}
297
298void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
299 const std::string& wrapped_key) {
300 PrefService* prefs = g_browser_process->local_state();
301 if (prefs) {
[email protected]ddd231e2010-06-29 20:35:19302 prefs->SetString(prefs::kSafeBrowsingClientKey, client_key);
303 prefs->SetString(prefs::kSafeBrowsingWrappedKey, wrapped_key);
[email protected]90a3b242009-11-13 00:28:54304 }
305}
306
307void SafeBrowsingService::OnEnable(bool enabled) {
308 if (enabled)
309 Start();
310 else
311 ShutDown();
312}
313
314// static
315void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
[email protected]20ce516d2010-06-18 02:20:04316 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, "");
317 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, "");
[email protected]90a3b242009-11-13 00:28:54318}
319
[email protected]cb2a67e2009-11-17 00:48:53320void SafeBrowsingService::CloseDatabase() {
[email protected]4d3b42a2010-10-07 05:00:29321 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53322
[email protected]03addd92009-11-17 21:38:10323 // Cases to avoid:
324 // * If |closing_database_| is true, continuing will queue up a second
325 // request, |closing_database_| will be reset after handling the first
326 // request, and if any functions on the db thread recreate the database, we
327 // could start using it on the IO thread and then have the second request
328 // handler delete it out from under us.
329 // * If |database_| is NULL, then either no creation request is in flight, in
330 // which case we don't need to do anything, or one is in flight, in which
331 // case the database will be recreated before our deletion request is
332 // handled, and could be used on the IO thread in that time period, leading
333 // to the same problem as above.
334 // * If |queued_checks_| is non-empty and |database_| is non-NULL, we're
335 // about to be called back (in DatabaseLoadComplete()). This will call
336 // CheckUrl(), which will want the database. Closing the database here
337 // would lead to an infinite loop in DatabaseLoadComplete(), and even if it
338 // didn't, it would be pointless since we'd just want to recreate.
339 //
[email protected]34094312009-12-03 21:40:26340 // The first two cases above are handled by checking DatabaseAvailable().
341 if (!DatabaseAvailable() || !queued_checks_.empty())
[email protected]cb2a67e2009-11-17 00:48:53342 return;
343
344 closing_database_ = true;
345 if (safe_browsing_thread_.get()) {
346 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
347 NewRunnableMethod(this, &SafeBrowsingService::OnCloseDatabase));
348 }
349}
350
[email protected]90a3b242009-11-13 00:28:54351void SafeBrowsingService::ResetDatabase() {
[email protected]4d3b42a2010-10-07 05:00:29352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53353 DCHECK(enabled_);
[email protected]90a3b242009-11-13 00:28:54354 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
355 this, &SafeBrowsingService::OnResetDatabase));
356}
357
358void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
359 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
360}
361
362SafeBrowsingService::~SafeBrowsingService() {
[email protected]cb2a67e2009-11-17 00:48:53363 // We should have already been shut down. If we're still enabled, then the
364 // database isn't going to be closed properly, which could lead to corruption.
365 DCHECK(!enabled_);
[email protected]90a3b242009-11-13 00:28:54366}
367
[email protected]d11f5662009-11-12 20:52:56368void SafeBrowsingService::OnIOInitialize(
369 const std::string& client_key,
370 const std::string& wrapped_key,
371 URLRequestContextGetter* request_context_getter) {
[email protected]4d3b42a2010-10-07 05:00:29372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29373 enabled_ = true;
[email protected]cb2a67e2009-11-17 00:48:53374 MakeDatabaseAvailable();
[email protected]1a871512009-11-06 06:11:18375
376 // On Windows, get the safe browsing client name from the browser
377 // distribution classes in installer util. These classes don't yet have
378 // an analog on non-Windows builds so just keep the name specified here.
379#if defined(OS_WIN)
380 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
381 std::string client_name(dist->GetSafeBrowsingName());
382#else
383#if defined(GOOGLE_CHROME_BUILD)
384 std::string client_name("googlechrome");
385#else
386 std::string client_name("chromium");
387#endif
388#endif
[email protected]894c4e82010-06-29 21:53:18389 CommandLine* cmdline = CommandLine::ForCurrentProcess();
[email protected]6142deb2010-09-08 23:21:18390 bool disable_auto_update =
391 cmdline->HasSwitch(switches::kSbDisableAutoUpdate) ||
392 cmdline->HasSwitch(switches::kDisableBackgroundNetworking);
[email protected]894c4e82010-06-29 21:53:18393 std::string info_url_prefix =
394 cmdline->HasSwitch(switches::kSbInfoURLPrefix) ?
395 cmdline->GetSwitchValueASCII(switches::kSbInfoURLPrefix) :
396 kSbDefaultInfoURLPrefix;
397 std::string mackey_url_prefix =
398 cmdline->HasSwitch(switches::kSbMacKeyURLPrefix) ?
399 cmdline->GetSwitchValueASCII(switches::kSbMacKeyURLPrefix) :
400 kSbDefaultMacKeyURLPrefix;
[email protected]1a871512009-11-06 06:11:18401
initial.commit09911bf2008-07-26 23:55:29402 protocol_manager_ = new SafeBrowsingProtocolManager(this,
[email protected]1a871512009-11-06 06:11:18403 client_name,
initial.commit09911bf2008-07-26 23:55:29404 client_key,
[email protected]d11f5662009-11-12 20:52:56405 wrapped_key,
[email protected]894c4e82010-06-29 21:53:18406 request_context_getter,
407 info_url_prefix,
408 mackey_url_prefix,
409 disable_auto_update);
[email protected]d11f5662009-11-12 20:52:56410
[email protected]dec173b2009-11-13 21:53:26411 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29412}
413
initial.commit09911bf2008-07-26 23:55:29414void SafeBrowsingService::OnIOShutdown() {
[email protected]4d3b42a2010-10-07 05:00:29415 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29416 if (!enabled_)
417 return;
418
419 enabled_ = false;
initial.commit09911bf2008-07-26 23:55:29420
421 // This cancels all in-flight GetHash requests.
422 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04423 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29424
[email protected]03addd92009-11-17 21:38:10425 // Delete queued checks, calling back any clients with 'URL_SAFE'.
426 // If we don't do this here we may fail to close the database below.
427 while (!queued_checks_.empty()) {
428 QueuedCheck check = queued_checks_.front();
429 if (check.client)
430 check.client->OnUrlCheckResult(check.url, URL_SAFE);
431 queued_checks_.pop_front();
432 }
433
[email protected]cb2a67e2009-11-17 00:48:53434 // Close the database. We don't simply DeleteSoon() because if a close is
435 // already pending, we'll double-free, and we don't set |database_| to NULL
436 // because if there is still anything running on the db thread, it could
437 // create a new database object (via GetDatabase()) that would then leak.
438 CloseDatabase();
initial.commit09911bf2008-07-26 23:55:29439
440 // Flush the database thread. Any in-progress database check results will be
441 // ignored and cleaned up below.
[email protected]cb2a67e2009-11-17 00:48:53442 //
443 // Note that to avoid leaking the database, we rely on the fact that no new
444 // tasks will be added to the db thread between the call above and this one.
445 // See comments on the declaration of |safe_browsing_thread_|.
446 safe_browsing_thread_.reset();
initial.commit09911bf2008-07-26 23:55:29447
[email protected]03addd92009-11-17 21:38:10448 // Delete pending checks, calling back any clients with 'URL_SAFE'. We have
449 // to do this after the db thread returns because methods on it can have
450 // copies of these pointers, so deleting them might lead to accessing garbage.
initial.commit09911bf2008-07-26 23:55:29451 for (CurrentChecks::iterator it = checks_.begin();
452 it != checks_.end(); ++it) {
453 if ((*it)->client)
454 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
455 delete *it;
456 }
457 checks_.clear();
458
459 gethash_requests_.clear();
460}
461
[email protected]34094312009-12-03 21:40:26462bool SafeBrowsingService::DatabaseAvailable() const {
463 AutoLock lock(database_lock_);
464 return !closing_database_ && (database_ != NULL);
465}
466
[email protected]cb2a67e2009-11-17 00:48:53467bool SafeBrowsingService::MakeDatabaseAvailable() {
[email protected]4d3b42a2010-10-07 05:00:29468 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53469 DCHECK(enabled_);
[email protected]34094312009-12-03 21:40:26470 if (DatabaseAvailable())
471 return true;
472 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
473 NewRunnableMethod(this, &SafeBrowsingService::GetDatabase));
474 return false;
[email protected]cb2a67e2009-11-17 00:48:53475}
476
[email protected]90a3b242009-11-13 00:28:54477SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
[email protected]ef5b4822010-09-09 16:45:49478 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54479 if (database_)
480 return database_;
initial.commit09911bf2008-07-26 23:55:29481
[email protected]90a3b242009-11-13 00:28:54482 FilePath path;
483 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
484 DCHECK(result);
485 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29486
[email protected]90a3b242009-11-13 00:28:54487 Time before = Time::Now();
488 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
[email protected]7b1e37102010-03-08 21:43:16489 database->Init(path);
[email protected]34094312009-12-03 21:40:26490 {
491 // Acquiring the lock here guarantees correct ordering between the writes to
492 // the new database object above, and the setting of |databse_| below.
493 AutoLock lock(database_lock_);
494 database_ = database;
495 }
[email protected]613a03b2008-10-24 23:02:00496
[email protected]4d3b42a2010-10-07 05:00:29497 BrowserThread::PostTask(
498 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54499 NewRunnableMethod(this, &SafeBrowsingService::DatabaseLoadComplete));
[email protected]613a03b2008-10-24 23:02:00500
[email protected]cb2a67e2009-11-17 00:48:53501 UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", Time::Now() - before);
[email protected]90a3b242009-11-13 00:28:54502 return database_;
initial.commit09911bf2008-07-26 23:55:29503}
504
[email protected]613a03b2008-10-24 23:02:00505void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
[email protected]4d3b42a2010-10-07 05:00:29506 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29507
508 // If we've been shutdown during the database lookup, this check will already
509 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00510 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29511 return;
512
[email protected]613a03b2008-10-24 23:02:00513 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29514 // We have a partial match so we need to query Google for the full hash.
515 // Clean up will happen in HandleGetHashResults.
516
517 // See if we have a GetHash request already in progress for this particular
518 // prefix. If so, we just append ourselves to the list of interested parties
519 // when the results arrive. We only do this for checks involving one prefix,
520 // since that is the common case (multiple prefixes will issue the request
521 // as normal).
[email protected]613a03b2008-10-24 23:02:00522 if (check->prefix_hits.size() == 1) {
523 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29524 GetHashRequests::iterator it = gethash_requests_.find(prefix);
525 if (it != gethash_requests_.end()) {
526 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00527 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29528 return;
529 }
530
531 // No request in progress, so we're the first for this prefix.
532 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00533 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29534 gethash_requests_[prefix] = requestors;
535 }
536
537 // Reset the start time so that we can measure the network time without the
538 // database time.
[email protected]613a03b2008-10-24 23:02:00539 check->start = Time::Now();
540 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29541 } else {
542 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00543 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29544 }
545}
546
[email protected]90a3b242009-11-13 00:28:54547void SafeBrowsingService::GetAllChunksFromDatabase() {
[email protected]ef5b4822010-09-09 16:45:49548 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
549
[email protected]90a3b242009-11-13 00:28:54550 bool database_error = true;
551 std::vector<SBListChunkRanges> lists;
[email protected]ef5b4822010-09-09 16:45:49552 DCHECK(!database_update_in_progress_);
553 database_update_in_progress_ = true;
[email protected]cb2a67e2009-11-17 00:48:53554 GetDatabase(); // This guarantees that |database_| is non-NULL.
[email protected]c23161342010-08-18 20:34:04555 if (database_->UpdateStarted(&lists)) {
[email protected]cb2a67e2009-11-17 00:48:53556 database_error = false;
557 } else {
558 database_->UpdateFinished(false);
[email protected]90a3b242009-11-13 00:28:54559 }
[email protected]613a03b2008-10-24 23:02:00560
[email protected]4d3b42a2010-10-07 05:00:29561 BrowserThread::PostTask(
562 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54563 NewRunnableMethod(
564 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
565 database_error));
initial.commit09911bf2008-07-26 23:55:29566}
567
[email protected]90a3b242009-11-13 00:28:54568void SafeBrowsingService::OnGetAllChunksFromDatabase(
569 const std::vector<SBListChunkRanges>& lists, bool database_error) {
[email protected]4d3b42a2010-10-07 05:00:29570 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54571 if (enabled_)
572 protocol_manager_->OnGetChunksComplete(lists, database_error);
573}
574
[email protected]90a3b242009-11-13 00:28:54575void SafeBrowsingService::OnChunkInserted() {
[email protected]4d3b42a2010-10-07 05:00:29576 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54577 if (enabled_)
578 protocol_manager_->OnChunkInserted();
579}
580
581void SafeBrowsingService::DatabaseLoadComplete() {
[email protected]4d3b42a2010-10-07 05:00:29582 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]90a3b242009-11-13 00:28:54583 if (!enabled_)
initial.commit09911bf2008-07-26 23:55:29584 return;
585
[email protected]03addd92009-11-17 21:38:10586 HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
587 if (queued_checks_.empty())
588 return;
589
590 // If the database isn't already available, calling CheckUrl() in the loop
591 // below will add the check back to the queue, and we'll infinite-loop.
[email protected]34094312009-12-03 21:40:26592 DCHECK(DatabaseAvailable());
[email protected]03addd92009-11-17 21:38:10593 while (!queued_checks_.empty()) {
594 QueuedCheck check = queued_checks_.front();
595 HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
596 // If CheckUrl() determines the URL is safe immediately, it doesn't call the
597 // client's handler function (because normally it's being directly called by
598 // the client). Since we're not the client, we have to convey this result.
599 if (check.client && CheckUrl(check.url, check.client))
600 check.client->OnUrlCheckResult(check.url, URL_SAFE);
601 queued_checks_.pop_front();
602 }
[email protected]90a3b242009-11-13 00:28:54603}
initial.commit09911bf2008-07-26 23:55:29604
[email protected]90a3b242009-11-13 00:28:54605void SafeBrowsingService::HandleChunkForDatabase(
606 const std::string& list_name,
[email protected]7b1e37102010-03-08 21:43:16607 SBChunkList* chunks) {
[email protected]ef5b4822010-09-09 16:45:49608 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16609 if (chunks) {
610 GetDatabase()->InsertChunks(list_name, *chunks);
611 delete chunks;
612 }
[email protected]4d3b42a2010-10-07 05:00:29613 BrowserThread::PostTask(
614 BrowserThread::IO, FROM_HERE,
[email protected]7b1e37102010-03-08 21:43:16615 NewRunnableMethod(this, &SafeBrowsingService::OnChunkInserted));
[email protected]90a3b242009-11-13 00:28:54616}
617
618void SafeBrowsingService::DeleteChunks(
619 std::vector<SBChunkDelete>* chunk_deletes) {
[email protected]ef5b4822010-09-09 16:45:49620 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16621 if (chunk_deletes) {
622 GetDatabase()->DeleteChunks(*chunk_deletes);
623 delete chunk_deletes;
624 }
[email protected]90a3b242009-11-13 00:28:54625}
626
627SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
628 const std::string& list_name) {
629 if (safe_browsing_util::IsPhishingList(list_name)) {
630 return URL_PHISHING;
[email protected]613a03b2008-10-24 23:02:00631 }
[email protected]90a3b242009-11-13 00:28:54632
633 if (safe_browsing_util::IsMalwareList(list_name)) {
634 return URL_MALWARE;
635 }
636
[email protected]53b7bf912010-10-25 18:33:19637 DVLOG(1) << "Unknown safe browsing list " << list_name;
[email protected]90a3b242009-11-13 00:28:54638 return URL_SAFE;
639}
640
641void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
642 bool proceed) {
643 client->OnBlockingPageComplete(proceed);
644}
645
646void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
[email protected]ef5b4822010-09-09 16:45:49647 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]cb2a67e2009-11-17 00:48:53648 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]ef5b4822010-09-09 16:45:49649 DCHECK(database_update_in_progress_);
650 database_update_in_progress_ = false;
[email protected]90a3b242009-11-13 00:28:54651}
652
653void SafeBrowsingService::Start() {
654 DCHECK(!safe_browsing_thread_.get());
655 safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
656 if (!safe_browsing_thread_->Start())
657 return;
658
659 // Retrieve client MAC keys.
660 PrefService* local_state = g_browser_process->local_state();
661 std::string client_key, wrapped_key;
662 if (local_state) {
663 client_key =
[email protected]ddd231e2010-06-29 20:35:19664 local_state->GetString(prefs::kSafeBrowsingClientKey);
[email protected]90a3b242009-11-13 00:28:54665 wrapped_key =
[email protected]ddd231e2010-06-29 20:35:19666 local_state->GetString(prefs::kSafeBrowsingWrappedKey);
[email protected]90a3b242009-11-13 00:28:54667 }
668
669 // We will issue network fetches using the default profile's request context.
[email protected]41a95c42010-10-20 15:49:15670 scoped_refptr<URLRequestContextGetter> request_context_getter =
[email protected]90a3b242009-11-13 00:28:54671 GetDefaultProfile()->GetRequestContext();
[email protected]90a3b242009-11-13 00:28:54672
[email protected]4d3b42a2010-10-07 05:00:29673 BrowserThread::PostTask(
674 BrowserThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54675 NewRunnableMethod(
[email protected]0d7e79fa2010-10-08 23:35:47676 this, &SafeBrowsingService::OnIOInitialize, client_key, wrapped_key,
[email protected]90a3b242009-11-13 00:28:54677 request_context_getter));
[email protected]cb2a67e2009-11-17 00:48:53678}
[email protected]90a3b242009-11-13 00:28:54679
[email protected]cb2a67e2009-11-17 00:48:53680void SafeBrowsingService::OnCloseDatabase() {
[email protected]ef5b4822010-09-09 16:45:49681 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]a3aaf3d2009-11-17 22:12:39682 DCHECK(closing_database_);
[email protected]cb2a67e2009-11-17 00:48:53683
684 // Because |closing_database_| is true, nothing on the IO thread will be
685 // accessing the database, so it's safe to delete and then NULL the pointer.
686 delete database_;
687 database_ = NULL;
[email protected]34094312009-12-03 21:40:26688
689 // Acquiring the lock here guarantees correct ordering between the resetting
690 // of |database_| above and of |closing_database_| below, which ensures there
691 // won't be a window during which the IO thread falsely believes the database
692 // is available.
693 AutoLock lock(database_lock_);
[email protected]cb2a67e2009-11-17 00:48:53694 closing_database_ = false;
[email protected]90a3b242009-11-13 00:28:54695}
696
697void SafeBrowsingService::OnResetDatabase() {
[email protected]ef5b4822010-09-09 16:45:49698 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54699 GetDatabase()->ResetDatabase();
[email protected]90a3b242009-11-13 00:28:54700}
701
702void SafeBrowsingService::CacheHashResults(
703 const std::vector<SBPrefix>& prefixes,
704 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]ef5b4822010-09-09 16:45:49705 DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54706 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29707}
708
709void SafeBrowsingService::OnHandleGetHashResults(
710 SafeBrowsingCheck* check,
711 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]4d3b42a2010-10-07 05:00:29712 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29713 SBPrefix prefix = check->prefix_hits[0];
714 GetHashRequests::iterator it = gethash_requests_.find(prefix);
715 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
716 HandleOneCheck(check, full_hashes);
717 return;
718 }
719
720 // Call back all interested parties.
721 GetHashRequestors& requestors = it->second;
722 for (GetHashRequestors::iterator r = requestors.begin();
723 r != requestors.end(); ++r) {
724 HandleOneCheck(*r, full_hashes);
725 }
726
727 gethash_requests_.erase(it);
728}
729
730void SafeBrowsingService::HandleOneCheck(
731 SafeBrowsingCheck* check,
732 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]4d3b42a2010-10-07 05:00:29733 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
initial.commit09911bf2008-07-26 23:55:29734 if (check->client) {
735 UrlCheckResult result = URL_SAFE;
736 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
[email protected]09d985f2009-05-11 21:59:58737 if (index != -1) {
initial.commit09911bf2008-07-26 23:55:29738 result = GetResultFromListname(full_hashes[index].list_name);
[email protected]09d985f2009-05-11 21:59:58739 } else {
740 // Log the case where the SafeBrowsing servers return full hashes in the
741 // GetHash response that match the prefix we're looking up, but don't
742 // match the full hash of the URL.
743 if (!full_hashes.empty())
744 UMA_HISTOGRAM_COUNTS("SB2.GetHashServerMiss", 1);
745 }
initial.commit09911bf2008-07-26 23:55:29746
747 // Let the client continue handling the original request.
748 check->client->OnUrlCheckResult(check->url, result);
749 }
750
751 checks_.erase(check);
752 delete check;
753}
754
[email protected]90a3b242009-11-13 00:28:54755void SafeBrowsingService::DoDisplayBlockingPage(
756 const UnsafeResource& resource) {
757 // The tab might have been closed.
758 TabContents* wc =
759 tab_util::GetTabContentsByID(resource.render_process_host_id,
760 resource.render_view_id);
initial.commit09911bf2008-07-26 23:55:29761
[email protected]90a3b242009-11-13 00:28:54762 if (!wc) {
763 // The tab is gone and we did not have a chance at showing the interstitial.
764 // Just act as "Don't Proceed" was chosen.
765 std::vector<UnsafeResource> resources;
766 resources.push_back(resource);
[email protected]4d3b42a2010-10-07 05:00:29767 BrowserThread::PostTask(
768 BrowserThread::IO, FROM_HERE,
[email protected]d83d03aa2009-11-02 21:44:37769 NewRunnableMethod(
[email protected]90a3b242009-11-13 00:28:54770 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
771 return;
initial.commit09911bf2008-07-26 23:55:29772 }
773
[email protected]036237d42010-10-07 22:41:58774 // Report the malicious resource to the SafeBrowsing servers if the user has
775 // opted in to reporting statistics.
[email protected]975bead2009-11-30 21:59:53776 const MetricsService* metrics = g_browser_process->metrics_service();
777 DCHECK(metrics);
778 if (metrics && metrics->reporting_active() &&
[email protected]036237d42010-10-07 22:41:58779 (resource.threat_type == SafeBrowsingService::URL_MALWARE ||
780 resource.threat_type == SafeBrowsingService::URL_PHISHING)) {
[email protected]90a3b242009-11-13 00:28:54781 GURL page_url = wc->GetURL();
782 GURL referrer_url;
783 NavigationEntry* entry = wc->controller().GetActiveEntry();
784 if (entry)
785 referrer_url = entry->referrer();
[email protected]293f19252010-08-18 20:01:18786 bool is_subresource = resource.resource_type != ResourceType::MAIN_FRAME;
[email protected]ee4d3e802010-08-12 23:59:51787
[email protected]293f19252010-08-18 20:01:18788 // When the malicious url is on the main frame, and resource.original_url
789 // is not the same as the resource.url, that means we have a redirect from
790 // resource.original_url to resource.url.
791 // Also, at this point, page_url points to the _previous_ page that we
792 // were on. We replace page_url with resource.original_url and referrer
793 // with page_url.
794 if (!is_subresource &&
795 !resource.original_url.is_empty() &&
796 resource.original_url != resource.url) {
797 referrer_url = page_url;
798 page_url = resource.original_url;
799 }
800
[email protected]036237d42010-10-07 22:41:58801 BrowserThread::PostTask(
802 BrowserThread::IO, FROM_HERE,
803 NewRunnableMethod(
804 this,
805 &SafeBrowsingService::ReportSafeBrowsingHit,
806 resource.url,
807 page_url,
808 referrer_url,
809 is_subresource,
810 resource.threat_type));
initial.commit09911bf2008-07-26 23:55:29811 }
812
[email protected]90a3b242009-11-13 00:28:54813 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
[email protected]613a03b2008-10-24 23:02:00814}
815
[email protected]036237d42010-10-07 22:41:58816void SafeBrowsingService::ReportSafeBrowsingHit(
817 const GURL& malicious_url,
818 const GURL& page_url,
819 const GURL& referrer_url,
820 bool is_subresource,
821 SafeBrowsingService::UrlCheckResult threat_type) {
[email protected]4d3b42a2010-10-07 05:00:29822 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
[email protected]fd220e602009-11-14 01:02:37823 if (!enabled_)
[email protected]dfdb0de72009-02-19 21:58:14824 return;
825
[email protected]53b7bf912010-10-25 18:33:19826 DVLOG(1) << "ReportSafeBrowsingHit: " << malicious_url << " " << page_url
827 << " " << referrer_url << " " << is_subresource << " "
828 << threat_type;
[email protected]036237d42010-10-07 22:41:58829 protocol_manager_->ReportSafeBrowsingHit(malicious_url, page_url,
830 referrer_url, is_subresource,
831 threat_type);
[email protected]dfdb0de72009-02-19 21:58:14832}