blob: a119e092ba77ce01983681ff90688989b427f59e [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]dcf7d352009-02-26 01:56:0221#include "chrome/common/url_constants.h"
[email protected]90a3b242009-11-13 00:28:5422#include "net/base/registry_controlled_domain.h"
23
[email protected]1a871512009-11-06 06:11:1824#if defined(OS_WIN)
25#include "chrome/installer/util/browser_distribution.h"
26#endif
initial.commit09911bf2008-07-26 23:55:2927
[email protected]e1acf6f2008-10-27 20:43:3328using base::Time;
29using base::TimeDelta;
30
[email protected]d11f5662009-11-12 20:52:5631static Profile* GetDefaultProfile() {
32 FilePath user_data_dir;
33 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
34 ProfileManager* profile_manager = g_browser_process->profile_manager();
35 return profile_manager->GetDefaultProfile(user_data_dir);
36}
37
initial.commit09911bf2008-07-26 23:55:2938SafeBrowsingService::SafeBrowsingService()
[email protected]d83d03aa2009-11-02 21:44:3739 : database_(NULL),
initial.commit09911bf2008-07-26 23:55:2940 protocol_manager_(NULL),
41 enabled_(false),
[email protected]cb2a67e2009-11-17 00:48:5342 update_in_progress_(false),
43 closing_database_(false) {
initial.commit09911bf2008-07-26 23:55:2944}
45
[email protected]d83d03aa2009-11-02 21:44:3746void SafeBrowsingService::Initialize() {
initial.commit09911bf2008-07-26 23:55:2947 // Get the profile's preference for SafeBrowsing.
[email protected]d11f5662009-11-12 20:52:5648 PrefService* pref_service = GetDefaultProfile()->GetPrefs();
initial.commit09911bf2008-07-26 23:55:2949 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
50 Start();
51}
52
initial.commit09911bf2008-07-26 23:55:2953void SafeBrowsingService::ShutDown() {
[email protected]d83d03aa2009-11-02 21:44:3754 ChromeThread::PostTask(
55 ChromeThread::IO, FROM_HERE,
56 NewRunnableMethod(this, &SafeBrowsingService::OnIOShutdown));
initial.commit09911bf2008-07-26 23:55:2957}
58
[email protected]90a3b242009-11-13 00:28:5459bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
60 return url.SchemeIs(chrome::kHttpScheme) ||
61 url.SchemeIs(chrome::kHttpsScheme);
62}
63
64bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
65 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]dec173b2009-11-13 21:53:2666 if (!enabled_)
[email protected]90a3b242009-11-13 00:28:5467 return true;
68
[email protected]cb2a67e2009-11-17 00:48:5369 if (!MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:5470 QueuedCheck check;
71 check.client = client;
72 check.url = url;
73 queued_checks_.push_back(check);
74 return false;
75 }
76
77 std::string list;
78 std::vector<SBPrefix> prefix_hits;
79 std::vector<SBFullHashResult> full_hits;
80 base::Time check_start = base::Time::Now();
81 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
82 &full_hits,
83 protocol_manager_->last_update());
84
85 UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::Time::Now() - check_start);
86
87 if (!prefix_match)
88 return true; // URL is okay.
89
90 // Needs to be asynchronous, since we could be in the constructor of a
91 // ResourceDispatcherHost event handler which can't pause there.
92 SafeBrowsingCheck* check = new SafeBrowsingCheck();
93 check->url = url;
94 check->client = client;
95 check->result = URL_SAFE;
96 check->need_get_hash = full_hits.empty();
97 check->prefix_hits.swap(prefix_hits);
98 check->full_hits.swap(full_hits);
99 checks_.insert(check);
100
101 ChromeThread::PostTask(
102 ChromeThread::IO, FROM_HERE,
103 NewRunnableMethod(this, &SafeBrowsingService::OnCheckDone, check));
104
105 return false;
106}
107
108void SafeBrowsingService::CancelCheck(Client* client) {
109 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]90a3b242009-11-13 00:28:54110 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
[email protected]03addd92009-11-17 21:38:10111 // We can't delete matching checks here because the db thread has a copy of
112 // the pointer. Instead, we simply NULL out the client, and when the db
113 // thread calls us back, we'll clean up the check.
[email protected]90a3b242009-11-13 00:28:54114 if ((*i)->client == client)
115 (*i)->client = NULL;
116 }
117
118 // Scan the queued clients store. Clients may be here if they requested a URL
[email protected]fd220e602009-11-14 01:02:37119 // check before the database has finished loading.
[email protected]dec173b2009-11-13 21:53:26120 for (std::deque<QueuedCheck>::iterator it(queued_checks_.begin());
[email protected]03addd92009-11-17 21:38:10121 it != queued_checks_.end(); ) {
122 // In this case it's safe to delete matches entirely since nothing has a
123 // pointer to them.
[email protected]dec173b2009-11-13 21:53:26124 if (it->client == client)
[email protected]03addd92009-11-17 21:38:10125 it = queued_checks_.erase(it);
126 else
127 ++it;
[email protected]90a3b242009-11-13 00:28:54128 }
129}
130
131void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
132 ResourceType::Type resource_type,
133 UrlCheckResult result,
134 Client* client,
135 int render_process_host_id,
136 int render_view_id) {
[email protected]cb2a67e2009-11-17 00:48:53137 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
138
[email protected]90a3b242009-11-13 00:28:54139 // Check if the user has already ignored our warning for this render_view
140 // and domain.
141 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
142 const WhiteListedEntry& entry = white_listed_entries_[i];
143 if (entry.render_process_host_id == render_process_host_id &&
144 entry.render_view_id == render_view_id &&
145 entry.result == result &&
146 entry.domain ==
147 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
148 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
149 this, &SafeBrowsingService::NotifyClientBlockingComplete,
150 client, true));
151 return;
152 }
153 }
154
155 UnsafeResource resource;
156 resource.url = url;
157 resource.resource_type = resource_type;
158 resource.threat_type= result;
159 resource.client = client;
160 resource.render_process_host_id = render_process_host_id;
161 resource.render_view_id = render_view_id;
162
163 // The blocking page must be created from the UI thread.
164 ChromeThread::PostTask(
165 ChromeThread::UI, FROM_HERE,
166 NewRunnableMethod(
167 this, &SafeBrowsingService::DoDisplayBlockingPage, resource));
168}
169
170void SafeBrowsingService::HandleGetHashResults(
171 SafeBrowsingCheck* check,
172 const std::vector<SBFullHashResult>& full_hashes,
173 bool can_cache) {
[email protected]cb2a67e2009-11-17 00:48:53174 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]90a3b242009-11-13 00:28:54175 if (checks_.find(check) == checks_.end())
176 return;
177
178 DCHECK(enabled_);
179
180 UMA_HISTOGRAM_LONG_TIMES("SB2.Network", Time::Now() - check->start);
181
182 std::vector<SBPrefix> prefixes = check->prefix_hits;
183 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
184
[email protected]cb2a67e2009-11-17 00:48:53185 if (can_cache && MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:54186 // Cache the GetHash results in memory:
187 database_->CacheHashResults(prefixes, full_hashes);
188 }
189}
190
191void SafeBrowsingService::HandleChunk(const std::string& list,
192 std::deque<SBChunk>* chunks) {
193 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
194 DCHECK(enabled_);
195 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
196 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
197}
198
199void SafeBrowsingService::HandleChunkDelete(
200 std::vector<SBChunkDelete>* chunk_deletes) {
201 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
202 DCHECK(enabled_);
203 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
204 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
205}
206
207void SafeBrowsingService::UpdateStarted() {
208 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
209 DCHECK(enabled_);
210 DCHECK(!update_in_progress_);
211 update_in_progress_ = true;
212 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
213 this, &SafeBrowsingService::GetAllChunksFromDatabase));
214}
215
216void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
217 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
218 DCHECK(enabled_);
219 if (update_in_progress_) {
220 update_in_progress_ = false;
221 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
222 NewRunnableMethod(this,
223 &SafeBrowsingService::DatabaseUpdateFinished,
224 update_succeeded));
225 }
226}
227
228void SafeBrowsingService::OnBlockingPageDone(
229 const std::vector<UnsafeResource>& resources,
230 bool proceed) {
231 for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
232 iter != resources.end(); ++iter) {
233 const UnsafeResource& resource = *iter;
234 NotifyClientBlockingComplete(resource.client, proceed);
235
236 if (proceed) {
237 // Whitelist this domain and warning type for the given tab.
238 WhiteListedEntry entry;
239 entry.render_process_host_id = resource.render_process_host_id;
240 entry.render_view_id = resource.render_view_id;
241 entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
242 resource.url);
243 entry.result = resource.threat_type;
244 white_listed_entries_.push_back(entry);
245 }
246 }
247}
248
249void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
250 const std::string& wrapped_key) {
251 PrefService* prefs = g_browser_process->local_state();
252 if (prefs) {
253 prefs->SetString(prefs::kSafeBrowsingClientKey, ASCIIToWide(client_key));
254 prefs->SetString(prefs::kSafeBrowsingWrappedKey, ASCIIToWide(wrapped_key));
255 }
256}
257
258void SafeBrowsingService::OnEnable(bool enabled) {
259 if (enabled)
260 Start();
261 else
262 ShutDown();
263}
264
265// static
266void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
267 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, L"");
268 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, L"");
269}
270
[email protected]cb2a67e2009-11-17 00:48:53271void SafeBrowsingService::CloseDatabase() {
272 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
273
[email protected]03addd92009-11-17 21:38:10274 // Cases to avoid:
275 // * If |closing_database_| is true, continuing will queue up a second
276 // request, |closing_database_| will be reset after handling the first
277 // request, and if any functions on the db thread recreate the database, we
278 // could start using it on the IO thread and then have the second request
279 // handler delete it out from under us.
280 // * If |database_| is NULL, then either no creation request is in flight, in
281 // which case we don't need to do anything, or one is in flight, in which
282 // case the database will be recreated before our deletion request is
283 // handled, and could be used on the IO thread in that time period, leading
284 // to the same problem as above.
285 // * If |queued_checks_| is non-empty and |database_| is non-NULL, we're
286 // about to be called back (in DatabaseLoadComplete()). This will call
287 // CheckUrl(), which will want the database. Closing the database here
288 // would lead to an infinite loop in DatabaseLoadComplete(), and even if it
289 // didn't, it would be pointless since we'd just want to recreate.
290 //
291 // The first two cases above are handled by checking database_available().
292 if (!database_available() || !queued_checks_.empty())
[email protected]cb2a67e2009-11-17 00:48:53293 return;
294
295 closing_database_ = true;
296 if (safe_browsing_thread_.get()) {
297 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
298 NewRunnableMethod(this, &SafeBrowsingService::OnCloseDatabase));
299 }
300}
301
[email protected]90a3b242009-11-13 00:28:54302void SafeBrowsingService::ResetDatabase() {
303 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53304 DCHECK(enabled_);
[email protected]90a3b242009-11-13 00:28:54305 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
306 this, &SafeBrowsingService::OnResetDatabase));
307}
308
309void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
310 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
311}
312
313SafeBrowsingService::~SafeBrowsingService() {
[email protected]cb2a67e2009-11-17 00:48:53314 // We should have already been shut down. If we're still enabled, then the
315 // database isn't going to be closed properly, which could lead to corruption.
316 DCHECK(!enabled_);
[email protected]90a3b242009-11-13 00:28:54317}
318
[email protected]d11f5662009-11-12 20:52:56319void SafeBrowsingService::OnIOInitialize(
320 const std::string& client_key,
321 const std::string& wrapped_key,
322 URLRequestContextGetter* request_context_getter) {
[email protected]d83d03aa2009-11-02 21:44:37323 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29324 enabled_ = true;
[email protected]cb2a67e2009-11-17 00:48:53325 MakeDatabaseAvailable();
[email protected]1a871512009-11-06 06:11:18326
327 // On Windows, get the safe browsing client name from the browser
328 // distribution classes in installer util. These classes don't yet have
329 // an analog on non-Windows builds so just keep the name specified here.
330#if defined(OS_WIN)
331 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
332 std::string client_name(dist->GetSafeBrowsingName());
333#else
334#if defined(GOOGLE_CHROME_BUILD)
335 std::string client_name("googlechrome");
336#else
337 std::string client_name("chromium");
338#endif
339#endif
340
initial.commit09911bf2008-07-26 23:55:29341 protocol_manager_ = new SafeBrowsingProtocolManager(this,
[email protected]1a871512009-11-06 06:11:18342 client_name,
initial.commit09911bf2008-07-26 23:55:29343 client_key,
[email protected]d11f5662009-11-12 20:52:56344 wrapped_key,
345 request_context_getter);
346
347 // Balance the reference added by Start().
348 request_context_getter->Release();
349
[email protected]dec173b2009-11-13 21:53:26350 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29351}
352
initial.commit09911bf2008-07-26 23:55:29353void SafeBrowsingService::OnIOShutdown() {
[email protected]d83d03aa2009-11-02 21:44:37354 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29355 if (!enabled_)
356 return;
357
358 enabled_ = false;
initial.commit09911bf2008-07-26 23:55:29359
360 // This cancels all in-flight GetHash requests.
361 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04362 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29363
[email protected]03addd92009-11-17 21:38:10364 // Delete queued checks, calling back any clients with 'URL_SAFE'.
365 // If we don't do this here we may fail to close the database below.
366 while (!queued_checks_.empty()) {
367 QueuedCheck check = queued_checks_.front();
368 if (check.client)
369 check.client->OnUrlCheckResult(check.url, URL_SAFE);
370 queued_checks_.pop_front();
371 }
372
[email protected]cb2a67e2009-11-17 00:48:53373 // Close the database. We don't simply DeleteSoon() because if a close is
374 // already pending, we'll double-free, and we don't set |database_| to NULL
375 // because if there is still anything running on the db thread, it could
376 // create a new database object (via GetDatabase()) that would then leak.
377 CloseDatabase();
initial.commit09911bf2008-07-26 23:55:29378
379 // Flush the database thread. Any in-progress database check results will be
380 // ignored and cleaned up below.
[email protected]cb2a67e2009-11-17 00:48:53381 //
382 // Note that to avoid leaking the database, we rely on the fact that no new
383 // tasks will be added to the db thread between the call above and this one.
384 // See comments on the declaration of |safe_browsing_thread_|.
385 safe_browsing_thread_.reset();
initial.commit09911bf2008-07-26 23:55:29386
[email protected]03addd92009-11-17 21:38:10387 // Delete pending checks, calling back any clients with 'URL_SAFE'. We have
388 // to do this after the db thread returns because methods on it can have
389 // copies of these pointers, so deleting them might lead to accessing garbage.
initial.commit09911bf2008-07-26 23:55:29390 for (CurrentChecks::iterator it = checks_.begin();
391 it != checks_.end(); ++it) {
392 if ((*it)->client)
393 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
394 delete *it;
395 }
396 checks_.clear();
397
398 gethash_requests_.clear();
399}
400
[email protected]cb2a67e2009-11-17 00:48:53401bool SafeBrowsingService::MakeDatabaseAvailable() {
402 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
403 DCHECK(enabled_);
404 if (!database_) {
405 DCHECK(!closing_database_);
406 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
407 NewRunnableMethod(this, &SafeBrowsingService::GetDatabase));
408 }
[email protected]03addd92009-11-17 21:38:10409 return database_available();
[email protected]cb2a67e2009-11-17 00:48:53410}
411
[email protected]90a3b242009-11-13 00:28:54412SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
413 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
414 if (database_)
415 return database_;
initial.commit09911bf2008-07-26 23:55:29416
[email protected]90a3b242009-11-13 00:28:54417 FilePath path;
418 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
419 DCHECK(result);
420 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29421
[email protected]90a3b242009-11-13 00:28:54422 Time before = Time::Now();
423 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
424 Callback0::Type* chunk_callback =
425 NewCallback(this, &SafeBrowsingService::ChunkInserted);
426 database->Init(path, chunk_callback);
427 database_ = database;
[email protected]613a03b2008-10-24 23:02:00428
[email protected]d83d03aa2009-11-02 21:44:37429 ChromeThread::PostTask(
430 ChromeThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54431 NewRunnableMethod(this, &SafeBrowsingService::DatabaseLoadComplete));
[email protected]613a03b2008-10-24 23:02:00432
[email protected]cb2a67e2009-11-17 00:48:53433 UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", Time::Now() - before);
[email protected]90a3b242009-11-13 00:28:54434 return database_;
initial.commit09911bf2008-07-26 23:55:29435}
436
[email protected]613a03b2008-10-24 23:02:00437void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
[email protected]d83d03aa2009-11-02 21:44:37438 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29439
440 // If we've been shutdown during the database lookup, this check will already
441 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00442 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29443 return;
444
[email protected]613a03b2008-10-24 23:02:00445 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29446 // We have a partial match so we need to query Google for the full hash.
447 // Clean up will happen in HandleGetHashResults.
448
449 // See if we have a GetHash request already in progress for this particular
450 // prefix. If so, we just append ourselves to the list of interested parties
451 // when the results arrive. We only do this for checks involving one prefix,
452 // since that is the common case (multiple prefixes will issue the request
453 // as normal).
[email protected]613a03b2008-10-24 23:02:00454 if (check->prefix_hits.size() == 1) {
455 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29456 GetHashRequests::iterator it = gethash_requests_.find(prefix);
457 if (it != gethash_requests_.end()) {
458 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00459 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29460 return;
461 }
462
463 // No request in progress, so we're the first for this prefix.
464 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00465 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29466 gethash_requests_[prefix] = requestors;
467 }
468
469 // Reset the start time so that we can measure the network time without the
470 // database time.
[email protected]613a03b2008-10-24 23:02:00471 check->start = Time::Now();
472 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29473 } else {
474 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00475 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29476 }
477}
478
[email protected]90a3b242009-11-13 00:28:54479void SafeBrowsingService::GetAllChunksFromDatabase() {
[email protected]f3724ea2009-05-08 22:09:56480 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54481 bool database_error = true;
482 std::vector<SBListChunkRanges> lists;
[email protected]cb2a67e2009-11-17 00:48:53483 GetDatabase(); // This guarantees that |database_| is non-NULL.
484 if (database_->UpdateStarted()) {
485 database_->GetListsInfo(&lists);
486 database_error = false;
487 } else {
488 database_->UpdateFinished(false);
[email protected]90a3b242009-11-13 00:28:54489 }
[email protected]613a03b2008-10-24 23:02:00490
[email protected]d83d03aa2009-11-02 21:44:37491 ChromeThread::PostTask(
492 ChromeThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54493 NewRunnableMethod(
494 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
495 database_error));
initial.commit09911bf2008-07-26 23:55:29496}
497
[email protected]90a3b242009-11-13 00:28:54498void SafeBrowsingService::OnGetAllChunksFromDatabase(
499 const std::vector<SBListChunkRanges>& lists, bool database_error) {
500 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
501 if (enabled_)
502 protocol_manager_->OnGetChunksComplete(lists, database_error);
503}
504
505void SafeBrowsingService::ChunkInserted() {
506 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
507 ChromeThread::PostTask(
508 ChromeThread::IO, FROM_HERE,
509 NewRunnableMethod(this, &SafeBrowsingService::OnChunkInserted));
510}
511
512void SafeBrowsingService::OnChunkInserted() {
513 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
514 if (enabled_)
515 protocol_manager_->OnChunkInserted();
516}
517
518void SafeBrowsingService::DatabaseLoadComplete() {
519 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
520 if (!enabled_)
initial.commit09911bf2008-07-26 23:55:29521 return;
522
[email protected]03addd92009-11-17 21:38:10523 HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
524 if (queued_checks_.empty())
525 return;
526
527 // If the database isn't already available, calling CheckUrl() in the loop
528 // below will add the check back to the queue, and we'll infinite-loop.
529 DCHECK(database_available());
530 while (!queued_checks_.empty()) {
531 QueuedCheck check = queued_checks_.front();
532 HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
533 // If CheckUrl() determines the URL is safe immediately, it doesn't call the
534 // client's handler function (because normally it's being directly called by
535 // the client). Since we're not the client, we have to convey this result.
536 if (check.client && CheckUrl(check.url, check.client))
537 check.client->OnUrlCheckResult(check.url, URL_SAFE);
538 queued_checks_.pop_front();
539 }
[email protected]90a3b242009-11-13 00:28:54540}
initial.commit09911bf2008-07-26 23:55:29541
[email protected]90a3b242009-11-13 00:28:54542void SafeBrowsingService::HandleChunkForDatabase(
543 const std::string& list_name,
544 std::deque<SBChunk>* chunks) {
545 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54546 GetDatabase()->InsertChunks(list_name, chunks);
547}
548
549void SafeBrowsingService::DeleteChunks(
550 std::vector<SBChunkDelete>* chunk_deletes) {
551 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54552 GetDatabase()->DeleteChunks(chunk_deletes);
553}
554
555SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
556 const std::string& list_name) {
557 if (safe_browsing_util::IsPhishingList(list_name)) {
558 return URL_PHISHING;
[email protected]613a03b2008-10-24 23:02:00559 }
[email protected]90a3b242009-11-13 00:28:54560
561 if (safe_browsing_util::IsMalwareList(list_name)) {
562 return URL_MALWARE;
563 }
564
565 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
566 return URL_SAFE;
567}
568
569void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
570 bool proceed) {
571 client->OnBlockingPageComplete(proceed);
572}
573
574void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
575 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]cb2a67e2009-11-17 00:48:53576 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]90a3b242009-11-13 00:28:54577}
578
579void SafeBrowsingService::Start() {
580 DCHECK(!safe_browsing_thread_.get());
581 safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
582 if (!safe_browsing_thread_->Start())
583 return;
584
585 // Retrieve client MAC keys.
586 PrefService* local_state = g_browser_process->local_state();
587 std::string client_key, wrapped_key;
588 if (local_state) {
589 client_key =
590 WideToASCII(local_state->GetString(prefs::kSafeBrowsingClientKey));
591 wrapped_key =
592 WideToASCII(local_state->GetString(prefs::kSafeBrowsingWrappedKey));
593 }
594
595 // We will issue network fetches using the default profile's request context.
596 URLRequestContextGetter* request_context_getter =
597 GetDefaultProfile()->GetRequestContext();
598 request_context_getter->AddRef(); // Balanced in OnIOInitialize.
599
600 ChromeThread::PostTask(
601 ChromeThread::IO, FROM_HERE,
602 NewRunnableMethod(
603 this, &SafeBrowsingService::OnIOInitialize, client_key, wrapped_key,
604 request_context_getter));
[email protected]cb2a67e2009-11-17 00:48:53605}
[email protected]90a3b242009-11-13 00:28:54606
[email protected]cb2a67e2009-11-17 00:48:53607void SafeBrowsingService::OnCloseDatabase() {
608 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]03addd92009-11-17 21:38:10609 DCHECK(database_available());
[email protected]cb2a67e2009-11-17 00:48:53610
611 // Because |closing_database_| is true, nothing on the IO thread will be
612 // accessing the database, so it's safe to delete and then NULL the pointer.
613 delete database_;
614 database_ = NULL;
615 closing_database_ = false;
[email protected]90a3b242009-11-13 00:28:54616}
617
618void SafeBrowsingService::OnResetDatabase() {
619 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
620 GetDatabase()->ResetDatabase();
[email protected]90a3b242009-11-13 00:28:54621}
622
623void SafeBrowsingService::CacheHashResults(
624 const std::vector<SBPrefix>& prefixes,
625 const std::vector<SBFullHashResult>& full_hashes) {
626 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
627 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29628}
629
630void SafeBrowsingService::OnHandleGetHashResults(
631 SafeBrowsingCheck* check,
632 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]cb2a67e2009-11-17 00:48:53633 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29634 SBPrefix prefix = check->prefix_hits[0];
635 GetHashRequests::iterator it = gethash_requests_.find(prefix);
636 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
637 HandleOneCheck(check, full_hashes);
638 return;
639 }
640
641 // Call back all interested parties.
642 GetHashRequestors& requestors = it->second;
643 for (GetHashRequestors::iterator r = requestors.begin();
644 r != requestors.end(); ++r) {
645 HandleOneCheck(*r, full_hashes);
646 }
647
648 gethash_requests_.erase(it);
649}
650
651void SafeBrowsingService::HandleOneCheck(
652 SafeBrowsingCheck* check,
653 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]cb2a67e2009-11-17 00:48:53654 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29655 if (check->client) {
656 UrlCheckResult result = URL_SAFE;
657 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
[email protected]09d985f2009-05-11 21:59:58658 if (index != -1) {
initial.commit09911bf2008-07-26 23:55:29659 result = GetResultFromListname(full_hashes[index].list_name);
[email protected]09d985f2009-05-11 21:59:58660 } else {
661 // Log the case where the SafeBrowsing servers return full hashes in the
662 // GetHash response that match the prefix we're looking up, but don't
663 // match the full hash of the URL.
664 if (!full_hashes.empty())
665 UMA_HISTOGRAM_COUNTS("SB2.GetHashServerMiss", 1);
666 }
initial.commit09911bf2008-07-26 23:55:29667
668 // Let the client continue handling the original request.
669 check->client->OnUrlCheckResult(check->url, result);
670 }
671
672 checks_.erase(check);
673 delete check;
674}
675
[email protected]90a3b242009-11-13 00:28:54676void SafeBrowsingService::DoDisplayBlockingPage(
677 const UnsafeResource& resource) {
678 // The tab might have been closed.
679 TabContents* wc =
680 tab_util::GetTabContentsByID(resource.render_process_host_id,
681 resource.render_view_id);
initial.commit09911bf2008-07-26 23:55:29682
[email protected]90a3b242009-11-13 00:28:54683 if (!wc) {
684 // The tab is gone and we did not have a chance at showing the interstitial.
685 // Just act as "Don't Proceed" was chosen.
686 std::vector<UnsafeResource> resources;
687 resources.push_back(resource);
688 ChromeThread::PostTask(
[email protected]d83d03aa2009-11-02 21:44:37689 ChromeThread::IO, FROM_HERE,
690 NewRunnableMethod(
[email protected]90a3b242009-11-13 00:28:54691 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
692 return;
initial.commit09911bf2008-07-26 23:55:29693 }
694
[email protected]90a3b242009-11-13 00:28:54695 // Report the malware sub-resource to the SafeBrowsing servers if we have a
696 // malware sub-resource on a safe page and only if the user has opted in to
697 // reporting statistics.
698 PrefService* prefs = g_browser_process->local_state();
699 DCHECK(prefs);
700 if (prefs && prefs->GetBoolean(prefs::kMetricsReportingEnabled) &&
701 resource.resource_type != ResourceType::MAIN_FRAME &&
702 resource.threat_type == SafeBrowsingService::URL_MALWARE) {
703 GURL page_url = wc->GetURL();
704 GURL referrer_url;
705 NavigationEntry* entry = wc->controller().GetActiveEntry();
706 if (entry)
707 referrer_url = entry->referrer();
708 ChromeThread::PostTask(
709 ChromeThread::IO, FROM_HERE,
710 NewRunnableMethod(this,
711 &SafeBrowsingService::ReportMalware,
712 resource.url,
713 page_url,
714 referrer_url));
initial.commit09911bf2008-07-26 23:55:29715 }
716
[email protected]90a3b242009-11-13 00:28:54717 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
[email protected]613a03b2008-10-24 23:02:00718}
719
[email protected]dfdb0de72009-02-19 21:58:14720void SafeBrowsingService::ReportMalware(const GURL& malware_url,
721 const GURL& page_url,
722 const GURL& referrer_url) {
[email protected]d83d03aa2009-11-02 21:44:37723 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]dfdb0de72009-02-19 21:58:14724
[email protected]fd220e602009-11-14 01:02:37725 if (!enabled_)
[email protected]dfdb0de72009-02-19 21:58:14726 return;
727
[email protected]fd220e602009-11-14 01:02:37728 if (database_) {
729 // Check if 'page_url' is already blacklisted (exists in our cache). Only
730 // report if it's not there.
731 std::string list;
732 std::vector<SBPrefix> prefix_hits;
733 std::vector<SBFullHashResult> full_hits;
734 database_->ContainsUrl(page_url, &list, &prefix_hits, &full_hits,
735 protocol_manager_->last_update());
736 if (!full_hits.empty())
737 return;
738 }
[email protected]dfdb0de72009-02-19 21:58:14739
[email protected]fd220e602009-11-14 01:02:37740 protocol_manager_->ReportMalware(malware_url, page_url, referrer_url);
[email protected]dfdb0de72009-02-19 21:58:14741}