blob: 236791f15eae37b31db1a56f55223666fb379738 [file] [log] [blame]
[email protected]4d9ae4a2009-10-01 17:59:381// Copyright (c) 2009 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
initial.commit09911bf2008-07-26 23:55:298#include "base/path_service.h"
9#include "base/string_util.h"
10#include "chrome/browser/browser_process.h"
[email protected]d11f5662009-11-12 20:52:5611#include "chrome/browser/net/url_request_context_getter.h"
initial.commit09911bf2008-07-26 23:55:2912#include "chrome/browser/profile_manager.h"
[email protected]51065cb2009-02-19 00:25:2313#include "chrome/browser/safe_browsing/protocol_manager.h"
[email protected]5b7c7d3c2009-02-19 00:06:2214#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
initial.commit09911bf2008-07-26 23:55:2915#include "chrome/browser/safe_browsing/safe_browsing_database.h"
[email protected]f3ec7742009-01-15 00:59:1616#include "chrome/browser/tab_contents/tab_util.h"
[email protected]57c6a652009-05-04 07:58:3417#include "chrome/browser/tab_contents/tab_contents.h"
initial.commit09911bf2008-07-26 23:55:2918#include "chrome/common/chrome_constants.h"
19#include "chrome/common/chrome_paths.h"
initial.commit09911bf2008-07-26 23:55:2920#include "chrome/common/pref_names.h"
[email protected]b1748b1d82009-11-30 20:32:5621#include "chrome/common/pref_service.h"
[email protected]dcf7d352009-02-26 01:56:0222#include "chrome/common/url_constants.h"
[email protected]90a3b242009-11-13 00:28:5423#include "net/base/registry_controlled_domain.h"
24
[email protected]1a871512009-11-06 06:11:1825#if defined(OS_WIN)
26#include "chrome/installer/util/browser_distribution.h"
27#endif
initial.commit09911bf2008-07-26 23:55:2928
[email protected]e1acf6f2008-10-27 20:43:3329using base::Time;
30using base::TimeDelta;
31
[email protected]d11f5662009-11-12 20:52:5632static Profile* GetDefaultProfile() {
33 FilePath user_data_dir;
34 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
35 ProfileManager* profile_manager = g_browser_process->profile_manager();
36 return profile_manager->GetDefaultProfile(user_data_dir);
37}
38
initial.commit09911bf2008-07-26 23:55:2939SafeBrowsingService::SafeBrowsingService()
[email protected]d83d03aa2009-11-02 21:44:3740 : database_(NULL),
initial.commit09911bf2008-07-26 23:55:2941 protocol_manager_(NULL),
42 enabled_(false),
[email protected]cb2a67e2009-11-17 00:48:5343 update_in_progress_(false),
44 closing_database_(false) {
initial.commit09911bf2008-07-26 23:55:2945}
46
[email protected]d83d03aa2009-11-02 21:44:3747void SafeBrowsingService::Initialize() {
initial.commit09911bf2008-07-26 23:55:2948 // Get the profile's preference for SafeBrowsing.
[email protected]d11f5662009-11-12 20:52:5649 PrefService* pref_service = GetDefaultProfile()->GetPrefs();
initial.commit09911bf2008-07-26 23:55:2950 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
51 Start();
52}
53
initial.commit09911bf2008-07-26 23:55:2954void SafeBrowsingService::ShutDown() {
[email protected]d83d03aa2009-11-02 21:44:3755 ChromeThread::PostTask(
56 ChromeThread::IO, FROM_HERE,
57 NewRunnableMethod(this, &SafeBrowsingService::OnIOShutdown));
initial.commit09911bf2008-07-26 23:55:2958}
59
[email protected]90a3b242009-11-13 00:28:5460bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
61 return url.SchemeIs(chrome::kHttpScheme) ||
62 url.SchemeIs(chrome::kHttpsScheme);
63}
64
65bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
66 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]dec173b2009-11-13 21:53:2667 if (!enabled_)
[email protected]90a3b242009-11-13 00:28:5468 return true;
69
[email protected]cb2a67e2009-11-17 00:48:5370 if (!MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:5471 QueuedCheck check;
72 check.client = client;
73 check.url = url;
74 queued_checks_.push_back(check);
75 return false;
76 }
77
78 std::string list;
79 std::vector<SBPrefix> prefix_hits;
80 std::vector<SBFullHashResult> full_hits;
81 base::Time check_start = base::Time::Now();
82 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
83 &full_hits,
84 protocol_manager_->last_update());
85
86 UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::Time::Now() - check_start);
87
88 if (!prefix_match)
89 return true; // URL is okay.
90
91 // Needs to be asynchronous, since we could be in the constructor of a
92 // ResourceDispatcherHost event handler which can't pause there.
93 SafeBrowsingCheck* check = new SafeBrowsingCheck();
94 check->url = url;
95 check->client = client;
96 check->result = URL_SAFE;
97 check->need_get_hash = full_hits.empty();
98 check->prefix_hits.swap(prefix_hits);
99 check->full_hits.swap(full_hits);
100 checks_.insert(check);
101
102 ChromeThread::PostTask(
103 ChromeThread::IO, FROM_HERE,
104 NewRunnableMethod(this, &SafeBrowsingService::OnCheckDone, check));
105
106 return false;
107}
108
109void SafeBrowsingService::CancelCheck(Client* client) {
110 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]90a3b242009-11-13 00:28:54111 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
[email protected]03addd92009-11-17 21:38:10112 // We can't delete matching checks here because the db thread has a copy of
113 // the pointer. Instead, we simply NULL out the client, and when the db
114 // thread calls us back, we'll clean up the check.
[email protected]90a3b242009-11-13 00:28:54115 if ((*i)->client == client)
116 (*i)->client = NULL;
117 }
118
119 // Scan the queued clients store. Clients may be here if they requested a URL
[email protected]fd220e602009-11-14 01:02:37120 // check before the database has finished loading.
[email protected]dec173b2009-11-13 21:53:26121 for (std::deque<QueuedCheck>::iterator it(queued_checks_.begin());
[email protected]03addd92009-11-17 21:38:10122 it != queued_checks_.end(); ) {
123 // In this case it's safe to delete matches entirely since nothing has a
124 // pointer to them.
[email protected]dec173b2009-11-13 21:53:26125 if (it->client == client)
[email protected]03addd92009-11-17 21:38:10126 it = queued_checks_.erase(it);
127 else
128 ++it;
[email protected]90a3b242009-11-13 00:28:54129 }
130}
131
132void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
133 ResourceType::Type resource_type,
134 UrlCheckResult result,
135 Client* client,
136 int render_process_host_id,
137 int render_view_id) {
[email protected]cb2a67e2009-11-17 00:48:53138 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
139
[email protected]90a3b242009-11-13 00:28:54140 // Check if the user has already ignored our warning for this render_view
141 // and domain.
142 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
143 const WhiteListedEntry& entry = white_listed_entries_[i];
144 if (entry.render_process_host_id == render_process_host_id &&
145 entry.render_view_id == render_view_id &&
146 entry.result == result &&
147 entry.domain ==
148 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
149 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
150 this, &SafeBrowsingService::NotifyClientBlockingComplete,
151 client, true));
152 return;
153 }
154 }
155
156 UnsafeResource resource;
157 resource.url = url;
158 resource.resource_type = resource_type;
159 resource.threat_type= result;
160 resource.client = client;
161 resource.render_process_host_id = render_process_host_id;
162 resource.render_view_id = render_view_id;
163
164 // The blocking page must be created from the UI thread.
165 ChromeThread::PostTask(
166 ChromeThread::UI, FROM_HERE,
167 NewRunnableMethod(
168 this, &SafeBrowsingService::DoDisplayBlockingPage, resource));
169}
170
171void SafeBrowsingService::HandleGetHashResults(
172 SafeBrowsingCheck* check,
173 const std::vector<SBFullHashResult>& full_hashes,
174 bool can_cache) {
[email protected]cb2a67e2009-11-17 00:48:53175 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]90a3b242009-11-13 00:28:54176 if (checks_.find(check) == checks_.end())
177 return;
178
179 DCHECK(enabled_);
180
181 UMA_HISTOGRAM_LONG_TIMES("SB2.Network", Time::Now() - check->start);
182
183 std::vector<SBPrefix> prefixes = check->prefix_hits;
184 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
185
[email protected]cb2a67e2009-11-17 00:48:53186 if (can_cache && MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:54187 // Cache the GetHash results in memory:
188 database_->CacheHashResults(prefixes, full_hashes);
189 }
190}
191
192void SafeBrowsingService::HandleChunk(const std::string& list,
193 std::deque<SBChunk>* chunks) {
194 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
195 DCHECK(enabled_);
196 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
197 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
198}
199
200void SafeBrowsingService::HandleChunkDelete(
201 std::vector<SBChunkDelete>* chunk_deletes) {
202 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
203 DCHECK(enabled_);
204 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
205 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
206}
207
208void SafeBrowsingService::UpdateStarted() {
209 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
210 DCHECK(enabled_);
211 DCHECK(!update_in_progress_);
212 update_in_progress_ = true;
213 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
214 this, &SafeBrowsingService::GetAllChunksFromDatabase));
215}
216
217void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
218 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
219 DCHECK(enabled_);
220 if (update_in_progress_) {
221 update_in_progress_ = false;
222 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
223 NewRunnableMethod(this,
224 &SafeBrowsingService::DatabaseUpdateFinished,
225 update_succeeded));
226 }
227}
228
229void SafeBrowsingService::OnBlockingPageDone(
230 const std::vector<UnsafeResource>& resources,
231 bool proceed) {
232 for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
233 iter != resources.end(); ++iter) {
234 const UnsafeResource& resource = *iter;
235 NotifyClientBlockingComplete(resource.client, proceed);
236
237 if (proceed) {
238 // Whitelist this domain and warning type for the given tab.
239 WhiteListedEntry entry;
240 entry.render_process_host_id = resource.render_process_host_id;
241 entry.render_view_id = resource.render_view_id;
242 entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
243 resource.url);
244 entry.result = resource.threat_type;
245 white_listed_entries_.push_back(entry);
246 }
247 }
248}
249
250void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
251 const std::string& wrapped_key) {
252 PrefService* prefs = g_browser_process->local_state();
253 if (prefs) {
254 prefs->SetString(prefs::kSafeBrowsingClientKey, ASCIIToWide(client_key));
255 prefs->SetString(prefs::kSafeBrowsingWrappedKey, ASCIIToWide(wrapped_key));
256 }
257}
258
259void SafeBrowsingService::OnEnable(bool enabled) {
260 if (enabled)
261 Start();
262 else
263 ShutDown();
264}
265
266// static
267void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
268 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, L"");
269 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, L"");
270}
271
[email protected]cb2a67e2009-11-17 00:48:53272void SafeBrowsingService::CloseDatabase() {
273 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
274
[email protected]03addd92009-11-17 21:38:10275 // Cases to avoid:
276 // * If |closing_database_| is true, continuing will queue up a second
277 // request, |closing_database_| will be reset after handling the first
278 // request, and if any functions on the db thread recreate the database, we
279 // could start using it on the IO thread and then have the second request
280 // handler delete it out from under us.
281 // * If |database_| is NULL, then either no creation request is in flight, in
282 // which case we don't need to do anything, or one is in flight, in which
283 // case the database will be recreated before our deletion request is
284 // handled, and could be used on the IO thread in that time period, leading
285 // to the same problem as above.
286 // * If |queued_checks_| is non-empty and |database_| is non-NULL, we're
287 // about to be called back (in DatabaseLoadComplete()). This will call
288 // CheckUrl(), which will want the database. Closing the database here
289 // would lead to an infinite loop in DatabaseLoadComplete(), and even if it
290 // didn't, it would be pointless since we'd just want to recreate.
291 //
292 // The first two cases above are handled by checking database_available().
293 if (!database_available() || !queued_checks_.empty())
[email protected]cb2a67e2009-11-17 00:48:53294 return;
295
296 closing_database_ = true;
297 if (safe_browsing_thread_.get()) {
298 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
299 NewRunnableMethod(this, &SafeBrowsingService::OnCloseDatabase));
300 }
301}
302
[email protected]90a3b242009-11-13 00:28:54303void SafeBrowsingService::ResetDatabase() {
304 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53305 DCHECK(enabled_);
[email protected]90a3b242009-11-13 00:28:54306 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
307 this, &SafeBrowsingService::OnResetDatabase));
308}
309
310void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
311 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
312}
313
314SafeBrowsingService::~SafeBrowsingService() {
[email protected]cb2a67e2009-11-17 00:48:53315 // We should have already been shut down. If we're still enabled, then the
316 // database isn't going to be closed properly, which could lead to corruption.
317 DCHECK(!enabled_);
[email protected]90a3b242009-11-13 00:28:54318}
319
[email protected]d11f5662009-11-12 20:52:56320void SafeBrowsingService::OnIOInitialize(
321 const std::string& client_key,
322 const std::string& wrapped_key,
323 URLRequestContextGetter* request_context_getter) {
[email protected]d83d03aa2009-11-02 21:44:37324 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29325 enabled_ = true;
[email protected]cb2a67e2009-11-17 00:48:53326 MakeDatabaseAvailable();
[email protected]1a871512009-11-06 06:11:18327
328 // On Windows, get the safe browsing client name from the browser
329 // distribution classes in installer util. These classes don't yet have
330 // an analog on non-Windows builds so just keep the name specified here.
331#if defined(OS_WIN)
332 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
333 std::string client_name(dist->GetSafeBrowsingName());
334#else
335#if defined(GOOGLE_CHROME_BUILD)
336 std::string client_name("googlechrome");
337#else
338 std::string client_name("chromium");
339#endif
340#endif
341
initial.commit09911bf2008-07-26 23:55:29342 protocol_manager_ = new SafeBrowsingProtocolManager(this,
[email protected]1a871512009-11-06 06:11:18343 client_name,
initial.commit09911bf2008-07-26 23:55:29344 client_key,
[email protected]d11f5662009-11-12 20:52:56345 wrapped_key,
346 request_context_getter);
347
348 // Balance the reference added by Start().
349 request_context_getter->Release();
350
[email protected]dec173b2009-11-13 21:53:26351 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29352}
353
initial.commit09911bf2008-07-26 23:55:29354void SafeBrowsingService::OnIOShutdown() {
[email protected]d83d03aa2009-11-02 21:44:37355 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29356 if (!enabled_)
357 return;
358
359 enabled_ = false;
initial.commit09911bf2008-07-26 23:55:29360
361 // This cancels all in-flight GetHash requests.
362 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04363 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29364
[email protected]03addd92009-11-17 21:38:10365 // Delete queued checks, calling back any clients with 'URL_SAFE'.
366 // If we don't do this here we may fail to close the database below.
367 while (!queued_checks_.empty()) {
368 QueuedCheck check = queued_checks_.front();
369 if (check.client)
370 check.client->OnUrlCheckResult(check.url, URL_SAFE);
371 queued_checks_.pop_front();
372 }
373
[email protected]cb2a67e2009-11-17 00:48:53374 // Close the database. We don't simply DeleteSoon() because if a close is
375 // already pending, we'll double-free, and we don't set |database_| to NULL
376 // because if there is still anything running on the db thread, it could
377 // create a new database object (via GetDatabase()) that would then leak.
378 CloseDatabase();
initial.commit09911bf2008-07-26 23:55:29379
380 // Flush the database thread. Any in-progress database check results will be
381 // ignored and cleaned up below.
[email protected]cb2a67e2009-11-17 00:48:53382 //
383 // Note that to avoid leaking the database, we rely on the fact that no new
384 // tasks will be added to the db thread between the call above and this one.
385 // See comments on the declaration of |safe_browsing_thread_|.
386 safe_browsing_thread_.reset();
initial.commit09911bf2008-07-26 23:55:29387
[email protected]03addd92009-11-17 21:38:10388 // Delete pending checks, calling back any clients with 'URL_SAFE'. We have
389 // to do this after the db thread returns because methods on it can have
390 // copies of these pointers, so deleting them might lead to accessing garbage.
initial.commit09911bf2008-07-26 23:55:29391 for (CurrentChecks::iterator it = checks_.begin();
392 it != checks_.end(); ++it) {
393 if ((*it)->client)
394 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
395 delete *it;
396 }
397 checks_.clear();
398
399 gethash_requests_.clear();
400}
401
[email protected]cb2a67e2009-11-17 00:48:53402bool SafeBrowsingService::MakeDatabaseAvailable() {
403 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
404 DCHECK(enabled_);
405 if (!database_) {
406 DCHECK(!closing_database_);
407 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
408 NewRunnableMethod(this, &SafeBrowsingService::GetDatabase));
409 }
[email protected]03addd92009-11-17 21:38:10410 return database_available();
[email protected]cb2a67e2009-11-17 00:48:53411}
412
[email protected]90a3b242009-11-13 00:28:54413SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
414 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
415 if (database_)
416 return database_;
initial.commit09911bf2008-07-26 23:55:29417
[email protected]90a3b242009-11-13 00:28:54418 FilePath path;
419 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
420 DCHECK(result);
421 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29422
[email protected]90a3b242009-11-13 00:28:54423 Time before = Time::Now();
424 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
425 Callback0::Type* chunk_callback =
426 NewCallback(this, &SafeBrowsingService::ChunkInserted);
427 database->Init(path, chunk_callback);
428 database_ = database;
[email protected]613a03b2008-10-24 23:02:00429
[email protected]d83d03aa2009-11-02 21:44:37430 ChromeThread::PostTask(
431 ChromeThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54432 NewRunnableMethod(this, &SafeBrowsingService::DatabaseLoadComplete));
[email protected]613a03b2008-10-24 23:02:00433
[email protected]cb2a67e2009-11-17 00:48:53434 UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", Time::Now() - before);
[email protected]90a3b242009-11-13 00:28:54435 return database_;
initial.commit09911bf2008-07-26 23:55:29436}
437
[email protected]613a03b2008-10-24 23:02:00438void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
[email protected]d83d03aa2009-11-02 21:44:37439 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29440
441 // If we've been shutdown during the database lookup, this check will already
442 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00443 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29444 return;
445
[email protected]613a03b2008-10-24 23:02:00446 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29447 // We have a partial match so we need to query Google for the full hash.
448 // Clean up will happen in HandleGetHashResults.
449
450 // See if we have a GetHash request already in progress for this particular
451 // prefix. If so, we just append ourselves to the list of interested parties
452 // when the results arrive. We only do this for checks involving one prefix,
453 // since that is the common case (multiple prefixes will issue the request
454 // as normal).
[email protected]613a03b2008-10-24 23:02:00455 if (check->prefix_hits.size() == 1) {
456 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29457 GetHashRequests::iterator it = gethash_requests_.find(prefix);
458 if (it != gethash_requests_.end()) {
459 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00460 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29461 return;
462 }
463
464 // No request in progress, so we're the first for this prefix.
465 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00466 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29467 gethash_requests_[prefix] = requestors;
468 }
469
470 // Reset the start time so that we can measure the network time without the
471 // database time.
[email protected]613a03b2008-10-24 23:02:00472 check->start = Time::Now();
473 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29474 } else {
475 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00476 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29477 }
478}
479
[email protected]90a3b242009-11-13 00:28:54480void SafeBrowsingService::GetAllChunksFromDatabase() {
[email protected]f3724ea2009-05-08 22:09:56481 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54482 bool database_error = true;
483 std::vector<SBListChunkRanges> lists;
[email protected]cb2a67e2009-11-17 00:48:53484 GetDatabase(); // This guarantees that |database_| is non-NULL.
485 if (database_->UpdateStarted()) {
486 database_->GetListsInfo(&lists);
487 database_error = false;
488 } else {
489 database_->UpdateFinished(false);
[email protected]90a3b242009-11-13 00:28:54490 }
[email protected]613a03b2008-10-24 23:02:00491
[email protected]d83d03aa2009-11-02 21:44:37492 ChromeThread::PostTask(
493 ChromeThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54494 NewRunnableMethod(
495 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
496 database_error));
initial.commit09911bf2008-07-26 23:55:29497}
498
[email protected]90a3b242009-11-13 00:28:54499void SafeBrowsingService::OnGetAllChunksFromDatabase(
500 const std::vector<SBListChunkRanges>& lists, bool database_error) {
501 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
502 if (enabled_)
503 protocol_manager_->OnGetChunksComplete(lists, database_error);
504}
505
506void SafeBrowsingService::ChunkInserted() {
507 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
508 ChromeThread::PostTask(
509 ChromeThread::IO, FROM_HERE,
510 NewRunnableMethod(this, &SafeBrowsingService::OnChunkInserted));
511}
512
513void SafeBrowsingService::OnChunkInserted() {
514 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
515 if (enabled_)
516 protocol_manager_->OnChunkInserted();
517}
518
519void SafeBrowsingService::DatabaseLoadComplete() {
520 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
521 if (!enabled_)
initial.commit09911bf2008-07-26 23:55:29522 return;
523
[email protected]03addd92009-11-17 21:38:10524 HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
525 if (queued_checks_.empty())
526 return;
527
528 // If the database isn't already available, calling CheckUrl() in the loop
529 // below will add the check back to the queue, and we'll infinite-loop.
530 DCHECK(database_available());
531 while (!queued_checks_.empty()) {
532 QueuedCheck check = queued_checks_.front();
533 HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
534 // If CheckUrl() determines the URL is safe immediately, it doesn't call the
535 // client's handler function (because normally it's being directly called by
536 // the client). Since we're not the client, we have to convey this result.
537 if (check.client && CheckUrl(check.url, check.client))
538 check.client->OnUrlCheckResult(check.url, URL_SAFE);
539 queued_checks_.pop_front();
540 }
[email protected]90a3b242009-11-13 00:28:54541}
initial.commit09911bf2008-07-26 23:55:29542
[email protected]90a3b242009-11-13 00:28:54543void SafeBrowsingService::HandleChunkForDatabase(
544 const std::string& list_name,
545 std::deque<SBChunk>* chunks) {
546 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54547 GetDatabase()->InsertChunks(list_name, chunks);
548}
549
550void SafeBrowsingService::DeleteChunks(
551 std::vector<SBChunkDelete>* chunk_deletes) {
552 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54553 GetDatabase()->DeleteChunks(chunk_deletes);
554}
555
556SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
557 const std::string& list_name) {
558 if (safe_browsing_util::IsPhishingList(list_name)) {
559 return URL_PHISHING;
[email protected]613a03b2008-10-24 23:02:00560 }
[email protected]90a3b242009-11-13 00:28:54561
562 if (safe_browsing_util::IsMalwareList(list_name)) {
563 return URL_MALWARE;
564 }
565
566 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
567 return URL_SAFE;
568}
569
570void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
571 bool proceed) {
572 client->OnBlockingPageComplete(proceed);
573}
574
575void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
576 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]cb2a67e2009-11-17 00:48:53577 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]90a3b242009-11-13 00:28:54578}
579
580void SafeBrowsingService::Start() {
581 DCHECK(!safe_browsing_thread_.get());
582 safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
583 if (!safe_browsing_thread_->Start())
584 return;
585
586 // Retrieve client MAC keys.
587 PrefService* local_state = g_browser_process->local_state();
588 std::string client_key, wrapped_key;
589 if (local_state) {
590 client_key =
591 WideToASCII(local_state->GetString(prefs::kSafeBrowsingClientKey));
592 wrapped_key =
593 WideToASCII(local_state->GetString(prefs::kSafeBrowsingWrappedKey));
594 }
595
596 // We will issue network fetches using the default profile's request context.
597 URLRequestContextGetter* request_context_getter =
598 GetDefaultProfile()->GetRequestContext();
599 request_context_getter->AddRef(); // Balanced in OnIOInitialize.
600
601 ChromeThread::PostTask(
602 ChromeThread::IO, FROM_HERE,
603 NewRunnableMethod(
604 this, &SafeBrowsingService::OnIOInitialize, client_key, wrapped_key,
605 request_context_getter));
[email protected]cb2a67e2009-11-17 00:48:53606}
[email protected]90a3b242009-11-13 00:28:54607
[email protected]cb2a67e2009-11-17 00:48:53608void SafeBrowsingService::OnCloseDatabase() {
609 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]a3aaf3d2009-11-17 22:12:39610 DCHECK(closing_database_);
[email protected]cb2a67e2009-11-17 00:48:53611
612 // Because |closing_database_| is true, nothing on the IO thread will be
613 // accessing the database, so it's safe to delete and then NULL the pointer.
614 delete database_;
615 database_ = NULL;
616 closing_database_ = false;
[email protected]90a3b242009-11-13 00:28:54617}
618
619void SafeBrowsingService::OnResetDatabase() {
620 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
621 GetDatabase()->ResetDatabase();
[email protected]90a3b242009-11-13 00:28:54622}
623
624void SafeBrowsingService::CacheHashResults(
625 const std::vector<SBPrefix>& prefixes,
626 const std::vector<SBFullHashResult>& full_hashes) {
627 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
628 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29629}
630
631void SafeBrowsingService::OnHandleGetHashResults(
632 SafeBrowsingCheck* check,
633 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]cb2a67e2009-11-17 00:48:53634 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29635 SBPrefix prefix = check->prefix_hits[0];
636 GetHashRequests::iterator it = gethash_requests_.find(prefix);
637 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
638 HandleOneCheck(check, full_hashes);
639 return;
640 }
641
642 // Call back all interested parties.
643 GetHashRequestors& requestors = it->second;
644 for (GetHashRequestors::iterator r = requestors.begin();
645 r != requestors.end(); ++r) {
646 HandleOneCheck(*r, full_hashes);
647 }
648
649 gethash_requests_.erase(it);
650}
651
652void SafeBrowsingService::HandleOneCheck(
653 SafeBrowsingCheck* check,
654 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]cb2a67e2009-11-17 00:48:53655 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29656 if (check->client) {
657 UrlCheckResult result = URL_SAFE;
658 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
[email protected]09d985f2009-05-11 21:59:58659 if (index != -1) {
initial.commit09911bf2008-07-26 23:55:29660 result = GetResultFromListname(full_hashes[index].list_name);
[email protected]09d985f2009-05-11 21:59:58661 } else {
662 // Log the case where the SafeBrowsing servers return full hashes in the
663 // GetHash response that match the prefix we're looking up, but don't
664 // match the full hash of the URL.
665 if (!full_hashes.empty())
666 UMA_HISTOGRAM_COUNTS("SB2.GetHashServerMiss", 1);
667 }
initial.commit09911bf2008-07-26 23:55:29668
669 // Let the client continue handling the original request.
670 check->client->OnUrlCheckResult(check->url, result);
671 }
672
673 checks_.erase(check);
674 delete check;
675}
676
[email protected]90a3b242009-11-13 00:28:54677void SafeBrowsingService::DoDisplayBlockingPage(
678 const UnsafeResource& resource) {
679 // The tab might have been closed.
680 TabContents* wc =
681 tab_util::GetTabContentsByID(resource.render_process_host_id,
682 resource.render_view_id);
initial.commit09911bf2008-07-26 23:55:29683
[email protected]90a3b242009-11-13 00:28:54684 if (!wc) {
685 // The tab is gone and we did not have a chance at showing the interstitial.
686 // Just act as "Don't Proceed" was chosen.
687 std::vector<UnsafeResource> resources;
688 resources.push_back(resource);
689 ChromeThread::PostTask(
[email protected]d83d03aa2009-11-02 21:44:37690 ChromeThread::IO, FROM_HERE,
691 NewRunnableMethod(
[email protected]90a3b242009-11-13 00:28:54692 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
693 return;
initial.commit09911bf2008-07-26 23:55:29694 }
695
[email protected]90a3b242009-11-13 00:28:54696 // Report the malware sub-resource to the SafeBrowsing servers if we have a
697 // malware sub-resource on a safe page and only if the user has opted in to
698 // reporting statistics.
699 PrefService* prefs = g_browser_process->local_state();
700 DCHECK(prefs);
701 if (prefs && prefs->GetBoolean(prefs::kMetricsReportingEnabled) &&
702 resource.resource_type != ResourceType::MAIN_FRAME &&
703 resource.threat_type == SafeBrowsingService::URL_MALWARE) {
704 GURL page_url = wc->GetURL();
705 GURL referrer_url;
706 NavigationEntry* entry = wc->controller().GetActiveEntry();
707 if (entry)
708 referrer_url = entry->referrer();
709 ChromeThread::PostTask(
710 ChromeThread::IO, FROM_HERE,
711 NewRunnableMethod(this,
712 &SafeBrowsingService::ReportMalware,
713 resource.url,
714 page_url,
715 referrer_url));
initial.commit09911bf2008-07-26 23:55:29716 }
717
[email protected]90a3b242009-11-13 00:28:54718 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
[email protected]613a03b2008-10-24 23:02:00719}
720
[email protected]dfdb0de72009-02-19 21:58:14721void SafeBrowsingService::ReportMalware(const GURL& malware_url,
722 const GURL& page_url,
723 const GURL& referrer_url) {
[email protected]d83d03aa2009-11-02 21:44:37724 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]dfdb0de72009-02-19 21:58:14725
[email protected]fd220e602009-11-14 01:02:37726 if (!enabled_)
[email protected]dfdb0de72009-02-19 21:58:14727 return;
728
[email protected]fd220e602009-11-14 01:02:37729 if (database_) {
730 // Check if 'page_url' is already blacklisted (exists in our cache). Only
731 // report if it's not there.
732 std::string list;
733 std::vector<SBPrefix> prefix_hits;
734 std::vector<SBFullHashResult> full_hits;
735 database_->ContainsUrl(page_url, &list, &prefix_hits, &full_hits,
736 protocol_manager_->last_update());
737 if (!full_hits.empty())
738 return;
739 }
[email protected]dfdb0de72009-02-19 21:58:14740
[email protected]fd220e602009-11-14 01:02:37741 protocol_manager_->ReportMalware(malware_url, page_url, referrer_url);
[email protected]dfdb0de72009-02-19 21:58:14742}