blob: 1c45b84befcb1e2732bf26af7203150df5fd74f8 [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]975bead2009-11-30 21:59:5311#include "chrome/browser/metrics/metrics_service.h"
[email protected]d11f5662009-11-12 20:52:5612#include "chrome/browser/net/url_request_context_getter.h"
initial.commit09911bf2008-07-26 23:55:2913#include "chrome/browser/profile_manager.h"
[email protected]51065cb2009-02-19 00:25:2314#include "chrome/browser/safe_browsing/protocol_manager.h"
[email protected]5b7c7d3c2009-02-19 00:06:2215#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
initial.commit09911bf2008-07-26 23:55:2916#include "chrome/browser/safe_browsing/safe_browsing_database.h"
[email protected]f3ec7742009-01-15 00:59:1617#include "chrome/browser/tab_contents/tab_util.h"
[email protected]57c6a652009-05-04 07:58:3418#include "chrome/browser/tab_contents/tab_contents.h"
initial.commit09911bf2008-07-26 23:55:2919#include "chrome/common/chrome_constants.h"
20#include "chrome/common/chrome_paths.h"
initial.commit09911bf2008-07-26 23:55:2921#include "chrome/common/pref_names.h"
[email protected]b1748b1d82009-11-30 20:32:5622#include "chrome/common/pref_service.h"
[email protected]dcf7d352009-02-26 01:56:0223#include "chrome/common/url_constants.h"
[email protected]90a3b242009-11-13 00:28:5424#include "net/base/registry_controlled_domain.h"
25
[email protected]1a871512009-11-06 06:11:1826#if defined(OS_WIN)
27#include "chrome/installer/util/browser_distribution.h"
28#endif
initial.commit09911bf2008-07-26 23:55:2929
[email protected]e1acf6f2008-10-27 20:43:3330using base::Time;
31using base::TimeDelta;
32
[email protected]d11f5662009-11-12 20:52:5633static Profile* GetDefaultProfile() {
34 FilePath user_data_dir;
35 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
36 ProfileManager* profile_manager = g_browser_process->profile_manager();
37 return profile_manager->GetDefaultProfile(user_data_dir);
38}
39
initial.commit09911bf2008-07-26 23:55:2940SafeBrowsingService::SafeBrowsingService()
[email protected]d83d03aa2009-11-02 21:44:3741 : database_(NULL),
initial.commit09911bf2008-07-26 23:55:2942 protocol_manager_(NULL),
43 enabled_(false),
[email protected]cb2a67e2009-11-17 00:48:5344 update_in_progress_(false),
45 closing_database_(false) {
initial.commit09911bf2008-07-26 23:55:2946}
47
[email protected]d83d03aa2009-11-02 21:44:3748void SafeBrowsingService::Initialize() {
initial.commit09911bf2008-07-26 23:55:2949 // Get the profile's preference for SafeBrowsing.
[email protected]d11f5662009-11-12 20:52:5650 PrefService* pref_service = GetDefaultProfile()->GetPrefs();
initial.commit09911bf2008-07-26 23:55:2951 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
52 Start();
53}
54
initial.commit09911bf2008-07-26 23:55:2955void SafeBrowsingService::ShutDown() {
[email protected]d83d03aa2009-11-02 21:44:3756 ChromeThread::PostTask(
57 ChromeThread::IO, FROM_HERE,
58 NewRunnableMethod(this, &SafeBrowsingService::OnIOShutdown));
initial.commit09911bf2008-07-26 23:55:2959}
60
[email protected]90a3b242009-11-13 00:28:5461bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
62 return url.SchemeIs(chrome::kHttpScheme) ||
63 url.SchemeIs(chrome::kHttpsScheme);
64}
65
66bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
67 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]dec173b2009-11-13 21:53:2668 if (!enabled_)
[email protected]90a3b242009-11-13 00:28:5469 return true;
70
[email protected]cb2a67e2009-11-17 00:48:5371 if (!MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:5472 QueuedCheck check;
73 check.client = client;
74 check.url = url;
75 queued_checks_.push_back(check);
76 return false;
77 }
78
79 std::string list;
80 std::vector<SBPrefix> prefix_hits;
81 std::vector<SBFullHashResult> full_hits;
82 base::Time check_start = base::Time::Now();
83 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
84 &full_hits,
85 protocol_manager_->last_update());
86
87 UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::Time::Now() - check_start);
88
89 if (!prefix_match)
90 return true; // URL is okay.
91
92 // Needs to be asynchronous, since we could be in the constructor of a
93 // ResourceDispatcherHost event handler which can't pause there.
94 SafeBrowsingCheck* check = new SafeBrowsingCheck();
95 check->url = url;
96 check->client = client;
97 check->result = URL_SAFE;
98 check->need_get_hash = full_hits.empty();
99 check->prefix_hits.swap(prefix_hits);
100 check->full_hits.swap(full_hits);
101 checks_.insert(check);
102
103 ChromeThread::PostTask(
104 ChromeThread::IO, FROM_HERE,
105 NewRunnableMethod(this, &SafeBrowsingService::OnCheckDone, check));
106
107 return false;
108}
109
110void SafeBrowsingService::CancelCheck(Client* client) {
111 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]90a3b242009-11-13 00:28:54112 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
[email protected]03addd92009-11-17 21:38:10113 // We can't delete matching checks here because the db thread has a copy of
114 // the pointer. Instead, we simply NULL out the client, and when the db
115 // thread calls us back, we'll clean up the check.
[email protected]90a3b242009-11-13 00:28:54116 if ((*i)->client == client)
117 (*i)->client = NULL;
118 }
119
120 // Scan the queued clients store. Clients may be here if they requested a URL
[email protected]fd220e602009-11-14 01:02:37121 // check before the database has finished loading.
[email protected]dec173b2009-11-13 21:53:26122 for (std::deque<QueuedCheck>::iterator it(queued_checks_.begin());
[email protected]03addd92009-11-17 21:38:10123 it != queued_checks_.end(); ) {
124 // In this case it's safe to delete matches entirely since nothing has a
125 // pointer to them.
[email protected]dec173b2009-11-13 21:53:26126 if (it->client == client)
[email protected]03addd92009-11-17 21:38:10127 it = queued_checks_.erase(it);
128 else
129 ++it;
[email protected]90a3b242009-11-13 00:28:54130 }
131}
132
133void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
134 ResourceType::Type resource_type,
135 UrlCheckResult result,
136 Client* client,
137 int render_process_host_id,
138 int render_view_id) {
[email protected]cb2a67e2009-11-17 00:48:53139 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
140
[email protected]90a3b242009-11-13 00:28:54141 // Check if the user has already ignored our warning for this render_view
142 // and domain.
143 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
144 const WhiteListedEntry& entry = white_listed_entries_[i];
145 if (entry.render_process_host_id == render_process_host_id &&
146 entry.render_view_id == render_view_id &&
147 entry.result == result &&
148 entry.domain ==
149 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
150 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
151 this, &SafeBrowsingService::NotifyClientBlockingComplete,
152 client, true));
153 return;
154 }
155 }
156
157 UnsafeResource resource;
158 resource.url = url;
159 resource.resource_type = resource_type;
160 resource.threat_type= result;
161 resource.client = client;
162 resource.render_process_host_id = render_process_host_id;
163 resource.render_view_id = render_view_id;
164
165 // The blocking page must be created from the UI thread.
166 ChromeThread::PostTask(
167 ChromeThread::UI, FROM_HERE,
168 NewRunnableMethod(
169 this, &SafeBrowsingService::DoDisplayBlockingPage, resource));
170}
171
172void SafeBrowsingService::HandleGetHashResults(
173 SafeBrowsingCheck* check,
174 const std::vector<SBFullHashResult>& full_hashes,
175 bool can_cache) {
[email protected]cb2a67e2009-11-17 00:48:53176 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]90a3b242009-11-13 00:28:54177 if (checks_.find(check) == checks_.end())
178 return;
179
180 DCHECK(enabled_);
181
182 UMA_HISTOGRAM_LONG_TIMES("SB2.Network", Time::Now() - check->start);
183
184 std::vector<SBPrefix> prefixes = check->prefix_hits;
185 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
186
[email protected]cb2a67e2009-11-17 00:48:53187 if (can_cache && MakeDatabaseAvailable()) {
[email protected]90a3b242009-11-13 00:28:54188 // Cache the GetHash results in memory:
189 database_->CacheHashResults(prefixes, full_hashes);
190 }
191}
192
193void SafeBrowsingService::HandleChunk(const std::string& list,
194 std::deque<SBChunk>* chunks) {
195 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
196 DCHECK(enabled_);
197 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
198 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
199}
200
201void SafeBrowsingService::HandleChunkDelete(
202 std::vector<SBChunkDelete>* chunk_deletes) {
203 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
204 DCHECK(enabled_);
205 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
206 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
207}
208
209void SafeBrowsingService::UpdateStarted() {
210 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
211 DCHECK(enabled_);
212 DCHECK(!update_in_progress_);
213 update_in_progress_ = true;
214 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
215 this, &SafeBrowsingService::GetAllChunksFromDatabase));
216}
217
218void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
219 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
220 DCHECK(enabled_);
221 if (update_in_progress_) {
222 update_in_progress_ = false;
223 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
224 NewRunnableMethod(this,
225 &SafeBrowsingService::DatabaseUpdateFinished,
226 update_succeeded));
227 }
228}
229
230void SafeBrowsingService::OnBlockingPageDone(
231 const std::vector<UnsafeResource>& resources,
232 bool proceed) {
233 for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
234 iter != resources.end(); ++iter) {
235 const UnsafeResource& resource = *iter;
236 NotifyClientBlockingComplete(resource.client, proceed);
237
238 if (proceed) {
239 // Whitelist this domain and warning type for the given tab.
240 WhiteListedEntry entry;
241 entry.render_process_host_id = resource.render_process_host_id;
242 entry.render_view_id = resource.render_view_id;
243 entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
244 resource.url);
245 entry.result = resource.threat_type;
246 white_listed_entries_.push_back(entry);
247 }
248 }
249}
250
251void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
252 const std::string& wrapped_key) {
253 PrefService* prefs = g_browser_process->local_state();
254 if (prefs) {
255 prefs->SetString(prefs::kSafeBrowsingClientKey, ASCIIToWide(client_key));
256 prefs->SetString(prefs::kSafeBrowsingWrappedKey, ASCIIToWide(wrapped_key));
257 }
258}
259
260void SafeBrowsingService::OnEnable(bool enabled) {
261 if (enabled)
262 Start();
263 else
264 ShutDown();
265}
266
267// static
268void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
269 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, L"");
270 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, L"");
271}
272
[email protected]cb2a67e2009-11-17 00:48:53273void SafeBrowsingService::CloseDatabase() {
274 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
275
[email protected]03addd92009-11-17 21:38:10276 // Cases to avoid:
277 // * If |closing_database_| is true, continuing will queue up a second
278 // request, |closing_database_| will be reset after handling the first
279 // request, and if any functions on the db thread recreate the database, we
280 // could start using it on the IO thread and then have the second request
281 // handler delete it out from under us.
282 // * If |database_| is NULL, then either no creation request is in flight, in
283 // which case we don't need to do anything, or one is in flight, in which
284 // case the database will be recreated before our deletion request is
285 // handled, and could be used on the IO thread in that time period, leading
286 // to the same problem as above.
287 // * If |queued_checks_| is non-empty and |database_| is non-NULL, we're
288 // about to be called back (in DatabaseLoadComplete()). This will call
289 // CheckUrl(), which will want the database. Closing the database here
290 // would lead to an infinite loop in DatabaseLoadComplete(), and even if it
291 // didn't, it would be pointless since we'd just want to recreate.
292 //
[email protected]34094312009-12-03 21:40:26293 // The first two cases above are handled by checking DatabaseAvailable().
294 if (!DatabaseAvailable() || !queued_checks_.empty())
[email protected]cb2a67e2009-11-17 00:48:53295 return;
296
297 closing_database_ = true;
298 if (safe_browsing_thread_.get()) {
299 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
300 NewRunnableMethod(this, &SafeBrowsingService::OnCloseDatabase));
301 }
302}
303
[email protected]90a3b242009-11-13 00:28:54304void SafeBrowsingService::ResetDatabase() {
305 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]cb2a67e2009-11-17 00:48:53306 DCHECK(enabled_);
[email protected]90a3b242009-11-13 00:28:54307 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
308 this, &SafeBrowsingService::OnResetDatabase));
309}
310
311void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
312 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
313}
314
315SafeBrowsingService::~SafeBrowsingService() {
[email protected]cb2a67e2009-11-17 00:48:53316 // We should have already been shut down. If we're still enabled, then the
317 // database isn't going to be closed properly, which could lead to corruption.
318 DCHECK(!enabled_);
[email protected]90a3b242009-11-13 00:28:54319}
320
[email protected]d11f5662009-11-12 20:52:56321void SafeBrowsingService::OnIOInitialize(
322 const std::string& client_key,
323 const std::string& wrapped_key,
324 URLRequestContextGetter* request_context_getter) {
[email protected]d83d03aa2009-11-02 21:44:37325 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29326 enabled_ = true;
[email protected]cb2a67e2009-11-17 00:48:53327 MakeDatabaseAvailable();
[email protected]1a871512009-11-06 06:11:18328
329 // On Windows, get the safe browsing client name from the browser
330 // distribution classes in installer util. These classes don't yet have
331 // an analog on non-Windows builds so just keep the name specified here.
332#if defined(OS_WIN)
333 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
334 std::string client_name(dist->GetSafeBrowsingName());
335#else
336#if defined(GOOGLE_CHROME_BUILD)
337 std::string client_name("googlechrome");
338#else
339 std::string client_name("chromium");
340#endif
341#endif
342
initial.commit09911bf2008-07-26 23:55:29343 protocol_manager_ = new SafeBrowsingProtocolManager(this,
[email protected]1a871512009-11-06 06:11:18344 client_name,
initial.commit09911bf2008-07-26 23:55:29345 client_key,
[email protected]d11f5662009-11-12 20:52:56346 wrapped_key,
347 request_context_getter);
348
349 // Balance the reference added by Start().
350 request_context_getter->Release();
351
[email protected]dec173b2009-11-13 21:53:26352 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29353}
354
initial.commit09911bf2008-07-26 23:55:29355void SafeBrowsingService::OnIOShutdown() {
[email protected]d83d03aa2009-11-02 21:44:37356 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29357 if (!enabled_)
358 return;
359
360 enabled_ = false;
initial.commit09911bf2008-07-26 23:55:29361
362 // This cancels all in-flight GetHash requests.
363 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04364 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29365
[email protected]03addd92009-11-17 21:38:10366 // Delete queued checks, calling back any clients with 'URL_SAFE'.
367 // If we don't do this here we may fail to close the database below.
368 while (!queued_checks_.empty()) {
369 QueuedCheck check = queued_checks_.front();
370 if (check.client)
371 check.client->OnUrlCheckResult(check.url, URL_SAFE);
372 queued_checks_.pop_front();
373 }
374
[email protected]cb2a67e2009-11-17 00:48:53375 // Close the database. We don't simply DeleteSoon() because if a close is
376 // already pending, we'll double-free, and we don't set |database_| to NULL
377 // because if there is still anything running on the db thread, it could
378 // create a new database object (via GetDatabase()) that would then leak.
379 CloseDatabase();
initial.commit09911bf2008-07-26 23:55:29380
381 // Flush the database thread. Any in-progress database check results will be
382 // ignored and cleaned up below.
[email protected]cb2a67e2009-11-17 00:48:53383 //
384 // Note that to avoid leaking the database, we rely on the fact that no new
385 // tasks will be added to the db thread between the call above and this one.
386 // See comments on the declaration of |safe_browsing_thread_|.
387 safe_browsing_thread_.reset();
initial.commit09911bf2008-07-26 23:55:29388
[email protected]03addd92009-11-17 21:38:10389 // Delete pending checks, calling back any clients with 'URL_SAFE'. We have
390 // to do this after the db thread returns because methods on it can have
391 // copies of these pointers, so deleting them might lead to accessing garbage.
initial.commit09911bf2008-07-26 23:55:29392 for (CurrentChecks::iterator it = checks_.begin();
393 it != checks_.end(); ++it) {
394 if ((*it)->client)
395 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
396 delete *it;
397 }
398 checks_.clear();
399
400 gethash_requests_.clear();
401}
402
[email protected]34094312009-12-03 21:40:26403bool SafeBrowsingService::DatabaseAvailable() const {
404 AutoLock lock(database_lock_);
405 return !closing_database_ && (database_ != NULL);
406}
407
[email protected]cb2a67e2009-11-17 00:48:53408bool SafeBrowsingService::MakeDatabaseAvailable() {
409 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
410 DCHECK(enabled_);
[email protected]34094312009-12-03 21:40:26411 if (DatabaseAvailable())
412 return true;
413 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
414 NewRunnableMethod(this, &SafeBrowsingService::GetDatabase));
415 return false;
[email protected]cb2a67e2009-11-17 00:48:53416}
417
[email protected]90a3b242009-11-13 00:28:54418SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
419 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
420 if (database_)
421 return database_;
initial.commit09911bf2008-07-26 23:55:29422
[email protected]90a3b242009-11-13 00:28:54423 FilePath path;
424 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
425 DCHECK(result);
426 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29427
[email protected]90a3b242009-11-13 00:28:54428 Time before = Time::Now();
429 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
430 Callback0::Type* chunk_callback =
431 NewCallback(this, &SafeBrowsingService::ChunkInserted);
432 database->Init(path, chunk_callback);
[email protected]34094312009-12-03 21:40:26433 {
434 // Acquiring the lock here guarantees correct ordering between the writes to
435 // the new database object above, and the setting of |databse_| below.
436 AutoLock lock(database_lock_);
437 database_ = database;
438 }
[email protected]613a03b2008-10-24 23:02:00439
[email protected]d83d03aa2009-11-02 21:44:37440 ChromeThread::PostTask(
441 ChromeThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54442 NewRunnableMethod(this, &SafeBrowsingService::DatabaseLoadComplete));
[email protected]613a03b2008-10-24 23:02:00443
[email protected]cb2a67e2009-11-17 00:48:53444 UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", Time::Now() - before);
[email protected]90a3b242009-11-13 00:28:54445 return database_;
initial.commit09911bf2008-07-26 23:55:29446}
447
[email protected]613a03b2008-10-24 23:02:00448void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
[email protected]d83d03aa2009-11-02 21:44:37449 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29450
451 // If we've been shutdown during the database lookup, this check will already
452 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00453 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29454 return;
455
[email protected]613a03b2008-10-24 23:02:00456 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29457 // We have a partial match so we need to query Google for the full hash.
458 // Clean up will happen in HandleGetHashResults.
459
460 // See if we have a GetHash request already in progress for this particular
461 // prefix. If so, we just append ourselves to the list of interested parties
462 // when the results arrive. We only do this for checks involving one prefix,
463 // since that is the common case (multiple prefixes will issue the request
464 // as normal).
[email protected]613a03b2008-10-24 23:02:00465 if (check->prefix_hits.size() == 1) {
466 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29467 GetHashRequests::iterator it = gethash_requests_.find(prefix);
468 if (it != gethash_requests_.end()) {
469 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00470 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29471 return;
472 }
473
474 // No request in progress, so we're the first for this prefix.
475 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00476 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29477 gethash_requests_[prefix] = requestors;
478 }
479
480 // Reset the start time so that we can measure the network time without the
481 // database time.
[email protected]613a03b2008-10-24 23:02:00482 check->start = Time::Now();
483 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29484 } else {
485 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00486 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29487 }
488}
489
[email protected]90a3b242009-11-13 00:28:54490void SafeBrowsingService::GetAllChunksFromDatabase() {
[email protected]f3724ea2009-05-08 22:09:56491 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54492 bool database_error = true;
493 std::vector<SBListChunkRanges> lists;
[email protected]cb2a67e2009-11-17 00:48:53494 GetDatabase(); // This guarantees that |database_| is non-NULL.
495 if (database_->UpdateStarted()) {
496 database_->GetListsInfo(&lists);
497 database_error = false;
498 } else {
499 database_->UpdateFinished(false);
[email protected]90a3b242009-11-13 00:28:54500 }
[email protected]613a03b2008-10-24 23:02:00501
[email protected]d83d03aa2009-11-02 21:44:37502 ChromeThread::PostTask(
503 ChromeThread::IO, FROM_HERE,
[email protected]90a3b242009-11-13 00:28:54504 NewRunnableMethod(
505 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
506 database_error));
initial.commit09911bf2008-07-26 23:55:29507}
508
[email protected]90a3b242009-11-13 00:28:54509void SafeBrowsingService::OnGetAllChunksFromDatabase(
510 const std::vector<SBListChunkRanges>& lists, bool database_error) {
511 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
512 if (enabled_)
513 protocol_manager_->OnGetChunksComplete(lists, database_error);
514}
515
516void SafeBrowsingService::ChunkInserted() {
517 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
518 ChromeThread::PostTask(
519 ChromeThread::IO, FROM_HERE,
520 NewRunnableMethod(this, &SafeBrowsingService::OnChunkInserted));
521}
522
523void SafeBrowsingService::OnChunkInserted() {
524 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
525 if (enabled_)
526 protocol_manager_->OnChunkInserted();
527}
528
529void SafeBrowsingService::DatabaseLoadComplete() {
530 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
531 if (!enabled_)
initial.commit09911bf2008-07-26 23:55:29532 return;
533
[email protected]03addd92009-11-17 21:38:10534 HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
535 if (queued_checks_.empty())
536 return;
537
538 // If the database isn't already available, calling CheckUrl() in the loop
539 // below will add the check back to the queue, and we'll infinite-loop.
[email protected]34094312009-12-03 21:40:26540 DCHECK(DatabaseAvailable());
[email protected]03addd92009-11-17 21:38:10541 while (!queued_checks_.empty()) {
542 QueuedCheck check = queued_checks_.front();
543 HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
544 // If CheckUrl() determines the URL is safe immediately, it doesn't call the
545 // client's handler function (because normally it's being directly called by
546 // the client). Since we're not the client, we have to convey this result.
547 if (check.client && CheckUrl(check.url, check.client))
548 check.client->OnUrlCheckResult(check.url, URL_SAFE);
549 queued_checks_.pop_front();
550 }
[email protected]90a3b242009-11-13 00:28:54551}
initial.commit09911bf2008-07-26 23:55:29552
[email protected]90a3b242009-11-13 00:28:54553void SafeBrowsingService::HandleChunkForDatabase(
554 const std::string& list_name,
555 std::deque<SBChunk>* chunks) {
556 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54557 GetDatabase()->InsertChunks(list_name, chunks);
558}
559
560void SafeBrowsingService::DeleteChunks(
561 std::vector<SBChunkDelete>* chunk_deletes) {
562 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]90a3b242009-11-13 00:28:54563 GetDatabase()->DeleteChunks(chunk_deletes);
564}
565
566SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
567 const std::string& list_name) {
568 if (safe_browsing_util::IsPhishingList(list_name)) {
569 return URL_PHISHING;
[email protected]613a03b2008-10-24 23:02:00570 }
[email protected]90a3b242009-11-13 00:28:54571
572 if (safe_browsing_util::IsMalwareList(list_name)) {
573 return URL_MALWARE;
574 }
575
576 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
577 return URL_SAFE;
578}
579
580void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
581 bool proceed) {
582 client->OnBlockingPageComplete(proceed);
583}
584
585void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
586 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]cb2a67e2009-11-17 00:48:53587 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]90a3b242009-11-13 00:28:54588}
589
590void SafeBrowsingService::Start() {
591 DCHECK(!safe_browsing_thread_.get());
592 safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
593 if (!safe_browsing_thread_->Start())
594 return;
595
596 // Retrieve client MAC keys.
597 PrefService* local_state = g_browser_process->local_state();
598 std::string client_key, wrapped_key;
599 if (local_state) {
600 client_key =
601 WideToASCII(local_state->GetString(prefs::kSafeBrowsingClientKey));
602 wrapped_key =
603 WideToASCII(local_state->GetString(prefs::kSafeBrowsingWrappedKey));
604 }
605
606 // We will issue network fetches using the default profile's request context.
607 URLRequestContextGetter* request_context_getter =
608 GetDefaultProfile()->GetRequestContext();
609 request_context_getter->AddRef(); // Balanced in OnIOInitialize.
610
611 ChromeThread::PostTask(
612 ChromeThread::IO, FROM_HERE,
613 NewRunnableMethod(
614 this, &SafeBrowsingService::OnIOInitialize, client_key, wrapped_key,
615 request_context_getter));
[email protected]cb2a67e2009-11-17 00:48:53616}
[email protected]90a3b242009-11-13 00:28:54617
[email protected]cb2a67e2009-11-17 00:48:53618void SafeBrowsingService::OnCloseDatabase() {
619 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]a3aaf3d2009-11-17 22:12:39620 DCHECK(closing_database_);
[email protected]cb2a67e2009-11-17 00:48:53621
622 // Because |closing_database_| is true, nothing on the IO thread will be
623 // accessing the database, so it's safe to delete and then NULL the pointer.
624 delete database_;
625 database_ = NULL;
[email protected]34094312009-12-03 21:40:26626
627 // Acquiring the lock here guarantees correct ordering between the resetting
628 // of |database_| above and of |closing_database_| below, which ensures there
629 // won't be a window during which the IO thread falsely believes the database
630 // is available.
631 AutoLock lock(database_lock_);
[email protected]cb2a67e2009-11-17 00:48:53632 closing_database_ = false;
[email protected]90a3b242009-11-13 00:28:54633}
634
635void SafeBrowsingService::OnResetDatabase() {
636 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
637 GetDatabase()->ResetDatabase();
[email protected]90a3b242009-11-13 00:28:54638}
639
640void SafeBrowsingService::CacheHashResults(
641 const std::vector<SBPrefix>& prefixes,
642 const std::vector<SBFullHashResult>& full_hashes) {
643 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
644 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29645}
646
647void SafeBrowsingService::OnHandleGetHashResults(
648 SafeBrowsingCheck* check,
649 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]cb2a67e2009-11-17 00:48:53650 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29651 SBPrefix prefix = check->prefix_hits[0];
652 GetHashRequests::iterator it = gethash_requests_.find(prefix);
653 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
654 HandleOneCheck(check, full_hashes);
655 return;
656 }
657
658 // Call back all interested parties.
659 GetHashRequestors& requestors = it->second;
660 for (GetHashRequestors::iterator r = requestors.begin();
661 r != requestors.end(); ++r) {
662 HandleOneCheck(*r, full_hashes);
663 }
664
665 gethash_requests_.erase(it);
666}
667
668void SafeBrowsingService::HandleOneCheck(
669 SafeBrowsingCheck* check,
670 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]cb2a67e2009-11-17 00:48:53671 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
initial.commit09911bf2008-07-26 23:55:29672 if (check->client) {
673 UrlCheckResult result = URL_SAFE;
674 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
[email protected]09d985f2009-05-11 21:59:58675 if (index != -1) {
initial.commit09911bf2008-07-26 23:55:29676 result = GetResultFromListname(full_hashes[index].list_name);
[email protected]09d985f2009-05-11 21:59:58677 } else {
678 // Log the case where the SafeBrowsing servers return full hashes in the
679 // GetHash response that match the prefix we're looking up, but don't
680 // match the full hash of the URL.
681 if (!full_hashes.empty())
682 UMA_HISTOGRAM_COUNTS("SB2.GetHashServerMiss", 1);
683 }
initial.commit09911bf2008-07-26 23:55:29684
685 // Let the client continue handling the original request.
686 check->client->OnUrlCheckResult(check->url, result);
687 }
688
689 checks_.erase(check);
690 delete check;
691}
692
[email protected]90a3b242009-11-13 00:28:54693void SafeBrowsingService::DoDisplayBlockingPage(
694 const UnsafeResource& resource) {
695 // The tab might have been closed.
696 TabContents* wc =
697 tab_util::GetTabContentsByID(resource.render_process_host_id,
698 resource.render_view_id);
initial.commit09911bf2008-07-26 23:55:29699
[email protected]90a3b242009-11-13 00:28:54700 if (!wc) {
701 // The tab is gone and we did not have a chance at showing the interstitial.
702 // Just act as "Don't Proceed" was chosen.
703 std::vector<UnsafeResource> resources;
704 resources.push_back(resource);
705 ChromeThread::PostTask(
[email protected]d83d03aa2009-11-02 21:44:37706 ChromeThread::IO, FROM_HERE,
707 NewRunnableMethod(
[email protected]90a3b242009-11-13 00:28:54708 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
709 return;
initial.commit09911bf2008-07-26 23:55:29710 }
711
[email protected]90a3b242009-11-13 00:28:54712 // Report the malware sub-resource to the SafeBrowsing servers if we have a
713 // malware sub-resource on a safe page and only if the user has opted in to
714 // reporting statistics.
[email protected]975bead2009-11-30 21:59:53715 const MetricsService* metrics = g_browser_process->metrics_service();
716 DCHECK(metrics);
717 if (metrics && metrics->reporting_active() &&
[email protected]90a3b242009-11-13 00:28:54718 resource.resource_type != ResourceType::MAIN_FRAME &&
719 resource.threat_type == SafeBrowsingService::URL_MALWARE) {
720 GURL page_url = wc->GetURL();
721 GURL referrer_url;
722 NavigationEntry* entry = wc->controller().GetActiveEntry();
723 if (entry)
724 referrer_url = entry->referrer();
725 ChromeThread::PostTask(
726 ChromeThread::IO, FROM_HERE,
727 NewRunnableMethod(this,
728 &SafeBrowsingService::ReportMalware,
729 resource.url,
730 page_url,
731 referrer_url));
initial.commit09911bf2008-07-26 23:55:29732 }
733
[email protected]90a3b242009-11-13 00:28:54734 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
[email protected]613a03b2008-10-24 23:02:00735}
736
[email protected]dfdb0de72009-02-19 21:58:14737void SafeBrowsingService::ReportMalware(const GURL& malware_url,
738 const GURL& page_url,
739 const GURL& referrer_url) {
[email protected]d83d03aa2009-11-02 21:44:37740 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
[email protected]dfdb0de72009-02-19 21:58:14741
[email protected]fd220e602009-11-14 01:02:37742 if (!enabled_)
[email protected]dfdb0de72009-02-19 21:58:14743 return;
744
[email protected]34094312009-12-03 21:40:26745 if (DatabaseAvailable()) {
[email protected]fd220e602009-11-14 01:02:37746 // Check if 'page_url' is already blacklisted (exists in our cache). Only
747 // report if it's not there.
748 std::string list;
749 std::vector<SBPrefix> prefix_hits;
750 std::vector<SBFullHashResult> full_hits;
751 database_->ContainsUrl(page_url, &list, &prefix_hits, &full_hits,
752 protocol_manager_->last_update());
753 if (!full_hits.empty())
754 return;
755 }
[email protected]dfdb0de72009-02-19 21:58:14756
[email protected]fd220e602009-11-14 01:02:37757 protocol_manager_->ReportMalware(malware_url, page_url, referrer_url);
[email protected]dfdb0de72009-02-19 21:58:14758}