blob: ede15874dba01097e381f75c546ef9cfba335c27 [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
[email protected]2041cf342010-02-19 03:15:598#include "base/callback.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]975bead2009-11-30 21:59:5312#include "chrome/browser/metrics/metrics_service.h"
[email protected]052313b2010-02-19 09:43:0813#include "chrome/browser/pref_service.h"
initial.commit09911bf2008-07-26 23:55:2914#include "chrome/browser/profile_manager.h"
[email protected]51065cb2009-02-19 00:25:2315#include "chrome/browser/safe_browsing/protocol_manager.h"
[email protected]5b7c7d3c2009-02-19 00:06:2216#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
initial.commit09911bf2008-07-26 23:55:2917#include "chrome/browser/safe_browsing/safe_browsing_database.h"
[email protected]f3ec7742009-01-15 00:59:1618#include "chrome/browser/tab_contents/tab_util.h"
[email protected]57c6a652009-05-04 07:58:3419#include "chrome/browser/tab_contents/tab_contents.h"
initial.commit09911bf2008-07-26 23:55:2920#include "chrome/common/chrome_constants.h"
21#include "chrome/common/chrome_paths.h"
[email protected]68d2a05f2010-05-07 21:39:5522#include "chrome/common/net/url_request_context_getter.h"
initial.commit09911bf2008-07-26 23:55:2923#include "chrome/common/pref_names.h"
[email protected]dcf7d352009-02-26 01:56:0224#include "chrome/common/url_constants.h"
[email protected]90a3b242009-11-13 00:28:5425#include "net/base/registry_controlled_domain.h"
26
[email protected]1a871512009-11-06 06:11:1827#if defined(OS_WIN)
28#include "chrome/installer/util/browser_distribution.h"
29#endif
initial.commit09911bf2008-07-26 23:55:2930
[email protected]e1acf6f2008-10-27 20:43:3331using base::Time;
32using base::TimeDelta;
33
[email protected]d11f5662009-11-12 20:52:5634static Profile* GetDefaultProfile() {
35 FilePath user_data_dir;
36 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
37 ProfileManager* profile_manager = g_browser_process->profile_manager();
38 return profile_manager->GetDefaultProfile(user_data_dir);
39}
40
initial.commit09911bf2008-07-26 23:55:2941SafeBrowsingService::SafeBrowsingService()
[email protected]d83d03aa2009-11-02 21:44:3742 : database_(NULL),
initial.commit09911bf2008-07-26 23:55:2943 protocol_manager_(NULL),
44 enabled_(false),
[email protected]cb2a67e2009-11-17 00:48:5345 update_in_progress_(false),
46 closing_database_(false) {
initial.commit09911bf2008-07-26 23:55:2947}
48
[email protected]d83d03aa2009-11-02 21:44:3749void SafeBrowsingService::Initialize() {
initial.commit09911bf2008-07-26 23:55:2950 // Get the profile's preference for SafeBrowsing.
[email protected]d11f5662009-11-12 20:52:5651 PrefService* pref_service = GetDefaultProfile()->GetPrefs();
initial.commit09911bf2008-07-26 23:55:2952 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
53 Start();
54}
55
initial.commit09911bf2008-07-26 23:55:2956void SafeBrowsingService::ShutDown() {
[email protected]d83d03aa2009-11-02 21:44:3757 ChromeThread::PostTask(
58 ChromeThread::IO, FROM_HERE,
59 NewRunnableMethod(this, &SafeBrowsingService::OnIOShutdown));
initial.commit09911bf2008-07-26 23:55:2960}
61
[email protected]90a3b242009-11-13 00:28:5462bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
63 return url.SchemeIs(chrome::kHttpScheme) ||
64 url.SchemeIs(chrome::kHttpsScheme);
65}
66
67bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
68 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]dec173b2009-11-13 21:53:2669 if (!enabled_)
[email protected]90a3b242009-11-13 00:28:5470 return true;
71
[email protected]cb2a67e2009-11-17 00:48:5372 if (!MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:5473 QueuedCheck check;
74 check.client = client;
75 check.url = url;
76 queued_checks_.push_back(check);
77 return false;
78 }
79
80 std::string list;
81 std::vector<SBPrefix> prefix_hits;
82 std::vector<SBFullHashResult> full_hits;
83 base::Time check_start = base::Time::Now();
84 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
85 &full_hits,
86 protocol_manager_->last_update());
87
88 UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::Time::Now() - check_start);
89
90 if (!prefix_match)
91 return true; // URL is okay.
92
93 // Needs to be asynchronous, since we could be in the constructor of a
94 // ResourceDispatcherHost event handler which can't pause there.
95 SafeBrowsingCheck* check = new SafeBrowsingCheck();
96 check->url = url;
97 check->client = client;
98 check->result = URL_SAFE;
99 check->need_get_hash = full_hits.empty();
100 check->prefix_hits.swap(prefix_hits);
101 check->full_hits.swap(full_hits);
102 checks_.insert(check);
103
104 ChromeThread::PostTask(
105 ChromeThread::IO, FROM_HERE,
106 NewRunnableMethod(this, &SafeBrowsingService::OnCheckDone, check));
107
108 return false;
109}
110
111void SafeBrowsingService::CancelCheck(Client* client) {
112 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]90a3b242009-11-13 00:28:54113 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
[email protected]03addd92009-11-17 21:38:10114 // We can't delete matching checks here because the db thread has a copy of
115 // the pointer. Instead, we simply NULL out the client, and when the db
116 // thread calls us back, we'll clean up the check.
[email protected]90a3b242009-11-13 00:28:54117 if ((*i)->client == client)
118 (*i)->client = NULL;
119 }
120
121 // Scan the queued clients store. Clients may be here if they requested a URL
[email protected]fd220e602009-11-14 01:02:37122 // check before the database has finished loading.
[email protected]dec173b2009-11-13 21:53:26123 for (std::deque<QueuedCheck>::iterator it(queued_checks_.begin());
[email protected]03addd92009-11-17 21:38:10124 it != queued_checks_.end(); ) {
125 // In this case it's safe to delete matches entirely since nothing has a
126 // pointer to them.
[email protected]dec173b2009-11-13 21:53:26127 if (it->client == client)
[email protected]03addd92009-11-17 21:38:10128 it = queued_checks_.erase(it);
129 else
130 ++it;
[email protected]90a3b242009-11-13 00:28:54131 }
132}
133
134void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
135 ResourceType::Type resource_type,
136 UrlCheckResult result,
137 Client* client,
138 int render_process_host_id,
139 int render_view_id) {
[email protected]cb2a67e2009-11-17 00:48:53140 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
141
[email protected]90a3b242009-11-13 00:28:54142 // Check if the user has already ignored our warning for this render_view
143 // and domain.
144 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
145 const WhiteListedEntry& entry = white_listed_entries_[i];
146 if (entry.render_process_host_id == render_process_host_id &&
147 entry.render_view_id == render_view_id &&
148 entry.result == result &&
149 entry.domain ==
150 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
151 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
152 this, &SafeBrowsingService::NotifyClientBlockingComplete,
153 client, true));
154 return;
155 }
156 }
157
158 UnsafeResource resource;
159 resource.url = url;
160 resource.resource_type = resource_type;
161 resource.threat_type= result;
162 resource.client = client;
163 resource.render_process_host_id = render_process_host_id;
164 resource.render_view_id = render_view_id;
165
166 // The blocking page must be created from the UI thread.
167 ChromeThread::PostTask(
168 ChromeThread::UI, FROM_HERE,
169 NewRunnableMethod(
170 this, &SafeBrowsingService::DoDisplayBlockingPage, resource));
171}
172
173void SafeBrowsingService::HandleGetHashResults(
174 SafeBrowsingCheck* check,
175 const std::vector<SBFullHashResult>& full_hashes,
176 bool can_cache) {
[email protected]cb2a67e2009-11-17 00:48:53177 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]90a3b242009-11-13 00:28:54178 if (checks_.find(check) == checks_.end())
179 return;
180
181 DCHECK(enabled_);
182
183 UMA_HISTOGRAM_LONG_TIMES("SB2.Network", Time::Now() - check->start);
184
185 std::vector<SBPrefix> prefixes = check->prefix_hits;
186 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
187
[email protected]cb2a67e2009-11-17 00:48:53188 if (can_cache && MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:54189 // Cache the GetHash results in memory:
190 database_->CacheHashResults(prefixes, full_hashes);
191 }
192}
193
194void SafeBrowsingService::HandleChunk(const std::string& list,
[email protected]7b1e37102010-03-08 21:43:16195 SBChunkList* chunks) {
[email protected]90a3b242009-11-13 00:28:54196 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
197 DCHECK(enabled_);
198 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
199 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
200}
201
202void SafeBrowsingService::HandleChunkDelete(
203 std::vector<SBChunkDelete>* chunk_deletes) {
204 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
205 DCHECK(enabled_);
206 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
207 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
208}
209
210void SafeBrowsingService::UpdateStarted() {
211 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
212 DCHECK(enabled_);
213 DCHECK(!update_in_progress_);
214 update_in_progress_ = true;
215 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
216 this, &SafeBrowsingService::GetAllChunksFromDatabase));
217}
218
219void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
220 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
221 DCHECK(enabled_);
222 if (update_in_progress_) {
223 update_in_progress_ = false;
224 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
225 NewRunnableMethod(this,
226 &SafeBrowsingService::DatabaseUpdateFinished,
227 update_succeeded));
228 }
229}
230
231void SafeBrowsingService::OnBlockingPageDone(
232 const std::vector<UnsafeResource>& resources,
233 bool proceed) {
234 for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
235 iter != resources.end(); ++iter) {
236 const UnsafeResource& resource = *iter;
237 NotifyClientBlockingComplete(resource.client, proceed);
238
239 if (proceed) {
240 // Whitelist this domain and warning type for the given tab.
241 WhiteListedEntry entry;
242 entry.render_process_host_id = resource.render_process_host_id;
243 entry.render_view_id = resource.render_view_id;
244 entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
245 resource.url);
246 entry.result = resource.threat_type;
247 white_listed_entries_.push_back(entry);
248 }
249 }
250}
251
252void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
253 const std::string& wrapped_key) {
254 PrefService* prefs = g_browser_process->local_state();
255 if (prefs) {
256 prefs->SetString(prefs::kSafeBrowsingClientKey, ASCIIToWide(client_key));
257 prefs->SetString(prefs::kSafeBrowsingWrappedKey, ASCIIToWide(wrapped_key));
258 }
259}
260
261void SafeBrowsingService::OnEnable(bool enabled) {
262 if (enabled)
263 Start();
264 else
265 ShutDown();
266}
267
268// static
269void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
270 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, L"");
271 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, L"");
272}
273
[email protected]cb2a67e2009-11-17 00:48:53274void SafeBrowsingService::CloseDatabase() {
275 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
276
[email protected]03addd92009-11-17 21:38:10277 // Cases to avoid:
278 // * If |closing_database_| is true, continuing will queue up a second
279 // request, |closing_database_| will be reset after handling the first
280 // request, and if any functions on the db thread recreate the database, we
281 // could start using it on the IO thread and then have the second request
282 // handler delete it out from under us.
283 // * If |database_| is NULL, then either no creation request is in flight, in
284 // which case we don't need to do anything, or one is in flight, in which
285 // case the database will be recreated before our deletion request is
286 // handled, and could be used on the IO thread in that time period, leading
287 // to the same problem as above.
288 // * If |queued_checks_| is non-empty and |database_| is non-NULL, we're
289 // about to be called back (in DatabaseLoadComplete()). This will call
290 // CheckUrl(), which will want the database. Closing the database here
291 // would lead to an infinite loop in DatabaseLoadComplete(), and even if it
292 // didn't, it would be pointless since we'd just want to recreate.
293 //
[email protected]34094312009-12-03 21:40:26294 // The first two cases above are handled by checking DatabaseAvailable().
295 if (!DatabaseAvailable() || !queued_checks_.empty())
[email protected]cb2a67e2009-11-17 00:48:53296 return;
297
298 closing_database_ = true;
299 if (safe_browsing_thread_.get()) {
300 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
301 NewRunnableMethod(this, &SafeBrowsingService::OnCloseDatabase));
302 }
303}
304
[email protected]90a3b242009-11-13 00:28:54305void SafeBrowsingService::ResetDatabase() {
306 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53307 DCHECK(enabled_);
[email protected]90a3b242009-11-13 00:28:54308 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
309 this, &SafeBrowsingService::OnResetDatabase));
310}
311
312void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
313 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
314}
315
316SafeBrowsingService::~SafeBrowsingService() {
[email protected]cb2a67e2009-11-17 00:48:53317 // We should have already been shut down. If we're still enabled, then the
318 // database isn't going to be closed properly, which could lead to corruption.
319 DCHECK(!enabled_);
[email protected]90a3b242009-11-13 00:28:54320}
321
[email protected]d11f5662009-11-12 20:52:56322void SafeBrowsingService::OnIOInitialize(
323 const std::string& client_key,
324 const std::string& wrapped_key,
325 URLRequestContextGetter* request_context_getter) {
[email protected]d83d03aa2009-11-02 21:44:37326 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29327 enabled_ = true;
[email protected]cb2a67e2009-11-17 00:48:53328 MakeDatabaseAvailable();
[email protected]1a871512009-11-06 06:11:18329
330 // On Windows, get the safe browsing client name from the browser
331 // distribution classes in installer util. These classes don't yet have
332 // an analog on non-Windows builds so just keep the name specified here.
333#if defined(OS_WIN)
334 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
335 std::string client_name(dist->GetSafeBrowsingName());
336#else
337#if defined(GOOGLE_CHROME_BUILD)
338 std::string client_name("googlechrome");
339#else
340 std::string client_name("chromium");
341#endif
342#endif
343
initial.commit09911bf2008-07-26 23:55:29344 protocol_manager_ = new SafeBrowsingProtocolManager(this,
[email protected]1a871512009-11-06 06:11:18345 client_name,
initial.commit09911bf2008-07-26 23:55:29346 client_key,
[email protected]d11f5662009-11-12 20:52:56347 wrapped_key,
348 request_context_getter);
349
350 // Balance the reference added by Start().
351 request_context_getter->Release();
352
[email protected]dec173b2009-11-13 21:53:26353 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29354}
355
initial.commit09911bf2008-07-26 23:55:29356void SafeBrowsingService::OnIOShutdown() {
[email protected]d83d03aa2009-11-02 21:44:37357 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29358 if (!enabled_)
359 return;
360
361 enabled_ = false;
initial.commit09911bf2008-07-26 23:55:29362
363 // This cancels all in-flight GetHash requests.
364 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04365 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29366
[email protected]03addd92009-11-17 21:38:10367 // Delete queued checks, calling back any clients with 'URL_SAFE'.
368 // If we don't do this here we may fail to close the database below.
369 while (!queued_checks_.empty()) {
370 QueuedCheck check = queued_checks_.front();
371 if (check.client)
372 check.client->OnUrlCheckResult(check.url, URL_SAFE);
373 queued_checks_.pop_front();
374 }
375
[email protected]cb2a67e2009-11-17 00:48:53376 // Close the database. We don't simply DeleteSoon() because if a close is
377 // already pending, we'll double-free, and we don't set |database_| to NULL
378 // because if there is still anything running on the db thread, it could
379 // create a new database object (via GetDatabase()) that would then leak.
380 CloseDatabase();
initial.commit09911bf2008-07-26 23:55:29381
382 // Flush the database thread. Any in-progress database check results will be
383 // ignored and cleaned up below.
[email protected]cb2a67e2009-11-17 00:48:53384 //
385 // Note that to avoid leaking the database, we rely on the fact that no new
386 // tasks will be added to the db thread between the call above and this one.
387 // See comments on the declaration of |safe_browsing_thread_|.
388 safe_browsing_thread_.reset();
initial.commit09911bf2008-07-26 23:55:29389
[email protected]03addd92009-11-17 21:38:10390 // Delete pending checks, calling back any clients with 'URL_SAFE'. We have
391 // to do this after the db thread returns because methods on it can have
392 // copies of these pointers, so deleting them might lead to accessing garbage.
initial.commit09911bf2008-07-26 23:55:29393 for (CurrentChecks::iterator it = checks_.begin();
394 it != checks_.end(); ++it) {
395 if ((*it)->client)
396 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
397 delete *it;
398 }
399 checks_.clear();
400
401 gethash_requests_.clear();
402}
403
[email protected]34094312009-12-03 21:40:26404bool SafeBrowsingService::DatabaseAvailable() const {
405 AutoLock lock(database_lock_);
406 return !closing_database_ && (database_ != NULL);
407}
408
[email protected]cb2a67e2009-11-17 00:48:53409bool SafeBrowsingService::MakeDatabaseAvailable() {
410 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
411 DCHECK(enabled_);
[email protected]34094312009-12-03 21:40:26412 if (DatabaseAvailable())
413 return true;
414 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
415 NewRunnableMethod(this, &SafeBrowsingService::GetDatabase));
416 return false;
[email protected]cb2a67e2009-11-17 00:48:53417}
418
[email protected]90a3b242009-11-13 00:28:54419SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
420 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
421 if (database_)
422 return database_;
initial.commit09911bf2008-07-26 23:55:29423
[email protected]90a3b242009-11-13 00:28:54424 FilePath path;
425 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
426 DCHECK(result);
427 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29428
[email protected]90a3b242009-11-13 00:28:54429 Time before = Time::Now();
430 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
[email protected]7b1e37102010-03-08 21:43:16431 database->Init(path);
[email protected]34094312009-12-03 21:40:26432 {
433 // Acquiring the lock here guarantees correct ordering between the writes to
434 // the new database object above, and the setting of |databse_| below.
435 AutoLock lock(database_lock_);
436 database_ = database;
437 }
[email protected]613a03b2008-10-24 23:02:00438
[email protected]d83d03aa2009-11-02 21:44:37439 ChromeThread::PostTask(
440 ChromeThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54441 NewRunnableMethod(this, &SafeBrowsingService::DatabaseLoadComplete));
[email protected]613a03b2008-10-24 23:02:00442
[email protected]cb2a67e2009-11-17 00:48:53443 UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", Time::Now() - before);
[email protected]90a3b242009-11-13 00:28:54444 return database_;
initial.commit09911bf2008-07-26 23:55:29445}
446
[email protected]613a03b2008-10-24 23:02:00447void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
[email protected]d83d03aa2009-11-02 21:44:37448 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29449
450 // If we've been shutdown during the database lookup, this check will already
451 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00452 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29453 return;
454
[email protected]613a03b2008-10-24 23:02:00455 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29456 // We have a partial match so we need to query Google for the full hash.
457 // Clean up will happen in HandleGetHashResults.
458
459 // See if we have a GetHash request already in progress for this particular
460 // prefix. If so, we just append ourselves to the list of interested parties
461 // when the results arrive. We only do this for checks involving one prefix,
462 // since that is the common case (multiple prefixes will issue the request
463 // as normal).
[email protected]613a03b2008-10-24 23:02:00464 if (check->prefix_hits.size() == 1) {
465 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29466 GetHashRequests::iterator it = gethash_requests_.find(prefix);
467 if (it != gethash_requests_.end()) {
468 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00469 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29470 return;
471 }
472
473 // No request in progress, so we're the first for this prefix.
474 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00475 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29476 gethash_requests_[prefix] = requestors;
477 }
478
479 // Reset the start time so that we can measure the network time without the
480 // database time.
[email protected]613a03b2008-10-24 23:02:00481 check->start = Time::Now();
482 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29483 } else {
484 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00485 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29486 }
487}
488
[email protected]90a3b242009-11-13 00:28:54489void SafeBrowsingService::GetAllChunksFromDatabase() {
[email protected]f3724ea2009-05-08 22:09:56490 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54491 bool database_error = true;
492 std::vector<SBListChunkRanges> lists;
[email protected]cb2a67e2009-11-17 00:48:53493 GetDatabase(); // This guarantees that |database_| is non-NULL.
494 if (database_->UpdateStarted()) {
495 database_->GetListsInfo(&lists);
496 database_error = false;
497 } else {
498 database_->UpdateFinished(false);
[email protected]90a3b242009-11-13 00:28:54499 }
[email protected]613a03b2008-10-24 23:02:00500
[email protected]d83d03aa2009-11-02 21:44:37501 ChromeThread::PostTask(
502 ChromeThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54503 NewRunnableMethod(
504 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
505 database_error));
initial.commit09911bf2008-07-26 23:55:29506}
507
[email protected]90a3b242009-11-13 00:28:54508void SafeBrowsingService::OnGetAllChunksFromDatabase(
509 const std::vector<SBListChunkRanges>& lists, bool database_error) {
510 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
511 if (enabled_)
512 protocol_manager_->OnGetChunksComplete(lists, database_error);
513}
514
[email protected]90a3b242009-11-13 00:28:54515void SafeBrowsingService::OnChunkInserted() {
516 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
517 if (enabled_)
518 protocol_manager_->OnChunkInserted();
519}
520
521void SafeBrowsingService::DatabaseLoadComplete() {
522 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
523 if (!enabled_)
initial.commit09911bf2008-07-26 23:55:29524 return;
525
[email protected]03addd92009-11-17 21:38:10526 HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
527 if (queued_checks_.empty())
528 return;
529
530 // If the database isn't already available, calling CheckUrl() in the loop
531 // below will add the check back to the queue, and we'll infinite-loop.
[email protected]34094312009-12-03 21:40:26532 DCHECK(DatabaseAvailable());
[email protected]03addd92009-11-17 21:38:10533 while (!queued_checks_.empty()) {
534 QueuedCheck check = queued_checks_.front();
535 HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
536 // If CheckUrl() determines the URL is safe immediately, it doesn't call the
537 // client's handler function (because normally it's being directly called by
538 // the client). Since we're not the client, we have to convey this result.
539 if (check.client && CheckUrl(check.url, check.client))
540 check.client->OnUrlCheckResult(check.url, URL_SAFE);
541 queued_checks_.pop_front();
542 }
[email protected]90a3b242009-11-13 00:28:54543}
initial.commit09911bf2008-07-26 23:55:29544
[email protected]90a3b242009-11-13 00:28:54545void SafeBrowsingService::HandleChunkForDatabase(
546 const std::string& list_name,
[email protected]7b1e37102010-03-08 21:43:16547 SBChunkList* chunks) {
[email protected]90a3b242009-11-13 00:28:54548 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16549 if (chunks) {
550 GetDatabase()->InsertChunks(list_name, *chunks);
551 delete chunks;
552 }
553 ChromeThread::PostTask(
554 ChromeThread::IO, FROM_HERE,
555 NewRunnableMethod(this, &SafeBrowsingService::OnChunkInserted));
[email protected]90a3b242009-11-13 00:28:54556}
557
558void SafeBrowsingService::DeleteChunks(
559 std::vector<SBChunkDelete>* chunk_deletes) {
560 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]7b1e37102010-03-08 21:43:16561 if (chunk_deletes) {
562 GetDatabase()->DeleteChunks(*chunk_deletes);
563 delete chunk_deletes;
564 }
[email protected]90a3b242009-11-13 00:28:54565}
566
567SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
568 const std::string& list_name) {
569 if (safe_browsing_util::IsPhishingList(list_name)) {
570 return URL_PHISHING;
[email protected]613a03b2008-10-24 23:02:00571 }
[email protected]90a3b242009-11-13 00:28:54572
573 if (safe_browsing_util::IsMalwareList(list_name)) {
574 return URL_MALWARE;
575 }
576
577 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
578 return URL_SAFE;
579}
580
581void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
582 bool proceed) {
583 client->OnBlockingPageComplete(proceed);
584}
585
586void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
587 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]cb2a67e2009-11-17 00:48:53588 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]90a3b242009-11-13 00:28:54589}
590
591void SafeBrowsingService::Start() {
592 DCHECK(!safe_browsing_thread_.get());
593 safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
594 if (!safe_browsing_thread_->Start())
595 return;
596
597 // Retrieve client MAC keys.
598 PrefService* local_state = g_browser_process->local_state();
599 std::string client_key, wrapped_key;
600 if (local_state) {
601 client_key =
602 WideToASCII(local_state->GetString(prefs::kSafeBrowsingClientKey));
603 wrapped_key =
604 WideToASCII(local_state->GetString(prefs::kSafeBrowsingWrappedKey));
605 }
606
607 // We will issue network fetches using the default profile's request context.
608 URLRequestContextGetter* request_context_getter =
609 GetDefaultProfile()->GetRequestContext();
610 request_context_getter->AddRef(); // Balanced in OnIOInitialize.
611
612 ChromeThread::PostTask(
613 ChromeThread::IO, FROM_HERE,
614 NewRunnableMethod(
615 this, &SafeBrowsingService::OnIOInitialize, client_key, wrapped_key,
616 request_context_getter));
[email protected]cb2a67e2009-11-17 00:48:53617}
[email protected]90a3b242009-11-13 00:28:54618
[email protected]cb2a67e2009-11-17 00:48:53619void SafeBrowsingService::OnCloseDatabase() {
620 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]a3aaf3d2009-11-17 22:12:39621 DCHECK(closing_database_);
[email protected]cb2a67e2009-11-17 00:48:53622
623 // Because |closing_database_| is true, nothing on the IO thread will be
624 // accessing the database, so it's safe to delete and then NULL the pointer.
625 delete database_;
626 database_ = NULL;
[email protected]34094312009-12-03 21:40:26627
628 // Acquiring the lock here guarantees correct ordering between the resetting
629 // of |database_| above and of |closing_database_| below, which ensures there
630 // won't be a window during which the IO thread falsely believes the database
631 // is available.
632 AutoLock lock(database_lock_);
[email protected]cb2a67e2009-11-17 00:48:53633 closing_database_ = false;
[email protected]90a3b242009-11-13 00:28:54634}
635
636void SafeBrowsingService::OnResetDatabase() {
637 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
638 GetDatabase()->ResetDatabase();
[email protected]90a3b242009-11-13 00:28:54639}
640
641void SafeBrowsingService::CacheHashResults(
642 const std::vector<SBPrefix>& prefixes,
643 const std::vector<SBFullHashResult>& full_hashes) {
644 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
645 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29646}
647
648void SafeBrowsingService::OnHandleGetHashResults(
649 SafeBrowsingCheck* check,
650 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]cb2a67e2009-11-17 00:48:53651 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29652 SBPrefix prefix = check->prefix_hits[0];
653 GetHashRequests::iterator it = gethash_requests_.find(prefix);
654 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
655 HandleOneCheck(check, full_hashes);
656 return;
657 }
658
659 // Call back all interested parties.
660 GetHashRequestors& requestors = it->second;
661 for (GetHashRequestors::iterator r = requestors.begin();
662 r != requestors.end(); ++r) {
663 HandleOneCheck(*r, full_hashes);
664 }
665
666 gethash_requests_.erase(it);
667}
668
669void SafeBrowsingService::HandleOneCheck(
670 SafeBrowsingCheck* check,
671 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]cb2a67e2009-11-17 00:48:53672 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29673 if (check->client) {
674 UrlCheckResult result = URL_SAFE;
675 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
[email protected]09d985f2009-05-11 21:59:58676 if (index != -1) {
initial.commit09911bf2008-07-26 23:55:29677 result = GetResultFromListname(full_hashes[index].list_name);
[email protected]09d985f2009-05-11 21:59:58678 } else {
679 // Log the case where the SafeBrowsing servers return full hashes in the
680 // GetHash response that match the prefix we're looking up, but don't
681 // match the full hash of the URL.
682 if (!full_hashes.empty())
683 UMA_HISTOGRAM_COUNTS("SB2.GetHashServerMiss", 1);
684 }
initial.commit09911bf2008-07-26 23:55:29685
686 // Let the client continue handling the original request.
687 check->client->OnUrlCheckResult(check->url, result);
688 }
689
690 checks_.erase(check);
691 delete check;
692}
693
[email protected]90a3b242009-11-13 00:28:54694void SafeBrowsingService::DoDisplayBlockingPage(
695 const UnsafeResource& resource) {
696 // The tab might have been closed.
697 TabContents* wc =
698 tab_util::GetTabContentsByID(resource.render_process_host_id,
699 resource.render_view_id);
initial.commit09911bf2008-07-26 23:55:29700
[email protected]90a3b242009-11-13 00:28:54701 if (!wc) {
702 // The tab is gone and we did not have a chance at showing the interstitial.
703 // Just act as "Don't Proceed" was chosen.
704 std::vector<UnsafeResource> resources;
705 resources.push_back(resource);
706 ChromeThread::PostTask(
[email protected]d83d03aa2009-11-02 21:44:37707 ChromeThread::IO, FROM_HERE,
708 NewRunnableMethod(
[email protected]90a3b242009-11-13 00:28:54709 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
710 return;
initial.commit09911bf2008-07-26 23:55:29711 }
712
[email protected]90a3b242009-11-13 00:28:54713 // Report the malware sub-resource to the SafeBrowsing servers if we have a
714 // malware sub-resource on a safe page and only if the user has opted in to
715 // reporting statistics.
[email protected]975bead2009-11-30 21:59:53716 const MetricsService* metrics = g_browser_process->metrics_service();
717 DCHECK(metrics);
718 if (metrics && metrics->reporting_active() &&
[email protected]90a3b242009-11-13 00:28:54719 resource.resource_type != ResourceType::MAIN_FRAME &&
720 resource.threat_type == SafeBrowsingService::URL_MALWARE) {
721 GURL page_url = wc->GetURL();
722 GURL referrer_url;
723 NavigationEntry* entry = wc->controller().GetActiveEntry();
724 if (entry)
725 referrer_url = entry->referrer();
726 ChromeThread::PostTask(
727 ChromeThread::IO, FROM_HERE,
728 NewRunnableMethod(this,
729 &SafeBrowsingService::ReportMalware,
730 resource.url,
731 page_url,
732 referrer_url));
initial.commit09911bf2008-07-26 23:55:29733 }
734
[email protected]90a3b242009-11-13 00:28:54735 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
[email protected]613a03b2008-10-24 23:02:00736}
737
[email protected]dfdb0de72009-02-19 21:58:14738void SafeBrowsingService::ReportMalware(const GURL& malware_url,
739 const GURL& page_url,
740 const GURL& referrer_url) {
[email protected]d83d03aa2009-11-02 21:44:37741 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]dfdb0de72009-02-19 21:58:14742
[email protected]fd220e602009-11-14 01:02:37743 if (!enabled_)
[email protected]dfdb0de72009-02-19 21:58:14744 return;
745
[email protected]34094312009-12-03 21:40:26746 if (DatabaseAvailable()) {
[email protected]fd220e602009-11-14 01:02:37747 // Check if 'page_url' is already blacklisted (exists in our cache). Only
748 // report if it's not there.
749 std::string list;
750 std::vector<SBPrefix> prefix_hits;
751 std::vector<SBFullHashResult> full_hits;
752 database_->ContainsUrl(page_url, &list, &prefix_hits, &full_hits,
753 protocol_manager_->last_update());
754 if (!full_hits.empty())
755 return;
756 }
[email protected]dfdb0de72009-02-19 21:58:14757
[email protected]fd220e602009-11-14 01:02:37758 protocol_manager_->ReportMalware(malware_url, page_url, referrer_url);
[email protected]dfdb0de72009-02-19 21:58:14759}