blob: 04e55a76e3412da7ea80a3eb9828b2227e5d68db [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// 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]613a03b2008-10-24 23:02:008#include "base/command_line.h"
initial.commit09911bf2008-07-26 23:55:299#include "base/histogram.h"
10#include "base/logging.h"
11#include "base/message_loop.h"
12#include "base/path_service.h"
13#include "base/string_util.h"
14#include "chrome/browser/browser_process.h"
[email protected]0a3076572008-12-13 20:48:3615#include "chrome/browser/chrome_thread.h"
initial.commit09911bf2008-07-26 23:55:2916#include "chrome/browser/profile_manager.h"
[email protected]51065cb2009-02-19 00:25:2317#include "chrome/browser/safe_browsing/protocol_manager.h"
[email protected]5b7c7d3c2009-02-19 00:06:2218#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
initial.commit09911bf2008-07-26 23:55:2919#include "chrome/browser/safe_browsing/safe_browsing_database.h"
[email protected]dfdb0de72009-02-19 21:58:1420#include "chrome/browser/tab_contents/navigation_entry.h"
[email protected]f3ec7742009-01-15 00:59:1621#include "chrome/browser/tab_contents/tab_util.h"
[email protected]dfdb0de72009-02-19 21:58:1422#include "chrome/browser/tab_contents/web_contents.h"
initial.commit09911bf2008-07-26 23:55:2923#include "chrome/common/chrome_constants.h"
24#include "chrome/common/chrome_paths.h"
[email protected]613a03b2008-10-24 23:02:0025#include "chrome/common/chrome_switches.h"
initial.commit09911bf2008-07-26 23:55:2926#include "chrome/common/pref_names.h"
27#include "chrome/common/pref_service.h"
28#include "net/base/registry_controlled_domain.h"
29
[email protected]e1acf6f2008-10-27 20:43:3330using base::Time;
31using base::TimeDelta;
32
initial.commit09911bf2008-07-26 23:55:2933SafeBrowsingService::SafeBrowsingService()
34 : io_loop_(NULL),
35 database_(NULL),
36 protocol_manager_(NULL),
37 enabled_(false),
[email protected]613a03b2008-10-24 23:02:0038 resetting_(false),
[email protected]57119c3f2008-12-04 00:33:0439 database_loaded_(false),
40 update_in_progress_(false) {
[email protected]bb975362009-01-21 01:00:2241 new_safe_browsing_ = !CommandLine::ForCurrentProcess()->HasSwitch(
42 switches::kUseOldSafeBrowsing);
[email protected]0a3076572008-12-13 20:48:3643 base::SystemMonitor* monitor = base::SystemMonitor::Get();
44 DCHECK(monitor);
45 if (monitor)
46 monitor->AddObserver(this);
initial.commit09911bf2008-07-26 23:55:2947}
48
49SafeBrowsingService::~SafeBrowsingService() {
[email protected]0a3076572008-12-13 20:48:3650 base::SystemMonitor* monitor = base::SystemMonitor::Get();
51 if (monitor)
52 monitor->RemoveObserver(this);
initial.commit09911bf2008-07-26 23:55:2953}
54
55// Only called on the UI thread.
56void SafeBrowsingService::Initialize(MessageLoop* io_loop) {
57 io_loop_ = io_loop;
58
59 // Get the profile's preference for SafeBrowsing.
[email protected]c870c762009-01-28 05:47:1560 FilePath user_data_dir;
initial.commit09911bf2008-07-26 23:55:2961 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
62 ProfileManager* profile_manager = g_browser_process->profile_manager();
[email protected]f7011fcb2009-01-28 21:54:3263 Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
initial.commit09911bf2008-07-26 23:55:2964 PrefService* pref_service = profile->GetPrefs();
65 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
66 Start();
67}
68
69// Start up SafeBrowsing objects. This can be called at browser start, or when
70// the user checks the "Enable SafeBrowsing" option in the Advanced options UI.
71void SafeBrowsingService::Start() {
72 DCHECK(!db_thread_.get());
[email protected]ab820df2008-08-26 05:55:1073 db_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
initial.commit09911bf2008-07-26 23:55:2974 if (!db_thread_->Start())
75 return;
76
initial.commit09911bf2008-07-26 23:55:2977 // Retrieve client MAC keys.
78 PrefService* local_state = g_browser_process->local_state();
79 std::string client_key, wrapped_key;
80 if (local_state) {
81 client_key =
82 WideToASCII(local_state->GetString(prefs::kSafeBrowsingClientKey));
83 wrapped_key =
84 WideToASCII(local_state->GetString(prefs::kSafeBrowsingWrappedKey));
85 }
86
87 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
88 this, &SafeBrowsingService::OnIOInitialize, MessageLoop::current(),
89 client_key, wrapped_key));
[email protected]2c2fb222008-12-17 02:35:4690
91 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
92 this, &SafeBrowsingService::OnDBInitialize));
initial.commit09911bf2008-07-26 23:55:2993}
94
95void SafeBrowsingService::ShutDown() {
96 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
97 this, &SafeBrowsingService::OnIOShutdown));
98}
99
100void SafeBrowsingService::OnIOInitialize(MessageLoop* notify_loop,
101 const std::string& client_key,
102 const std::string& wrapped_key) {
103 DCHECK(MessageLoop::current() == io_loop_);
104 enabled_ = true;
105 protocol_manager_ = new SafeBrowsingProtocolManager(this,
106 notify_loop,
107 client_key,
108 wrapped_key);
[email protected]613a03b2008-10-24 23:02:00109 // We want to initialize the protocol manager only after the database has
110 // loaded, which we'll receive asynchronously (DatabaseLoadComplete). If
111 // database_loaded_ isn't true, we'll wait for that notification to do the
112 // init.
113 if (database_loaded_)
114 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29115}
116
117void SafeBrowsingService::OnDBInitialize() {
118 DCHECK(MessageLoop::current() == db_thread_->message_loop());
119 GetDatabase();
120}
121
122void SafeBrowsingService::OnIOShutdown() {
123 DCHECK(MessageLoop::current() == io_loop_);
124 if (!enabled_)
125 return;
126
127 enabled_ = false;
[email protected]fbb2b7a2008-11-18 22:54:04128 resetting_ = false;
initial.commit09911bf2008-07-26 23:55:29129
130 // This cancels all in-flight GetHash requests.
131 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04132 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29133
134 if (db_thread_.get())
135 db_thread_->message_loop()->DeleteSoon(FROM_HERE, database_);
136
137 // Flush the database thread. Any in-progress database check results will be
138 // ignored and cleaned up below.
139 db_thread_.reset(NULL);
140
141 database_ = NULL;
[email protected]fbb2b7a2008-11-18 22:54:04142 database_loaded_ = false;
initial.commit09911bf2008-07-26 23:55:29143
[email protected]613a03b2008-10-24 23:02:00144 // Delete queued and pending checks once the database thread is done, calling
145 // back any clients with 'URL_SAFE'.
146 while (!queued_checks_.empty()) {
147 QueuedCheck check = queued_checks_.front();
148 if (check.client)
149 check.client->OnUrlCheckResult(check.url, URL_SAFE);
150 queued_checks_.pop_front();
151 }
152
initial.commit09911bf2008-07-26 23:55:29153 for (CurrentChecks::iterator it = checks_.begin();
154 it != checks_.end(); ++it) {
155 if ((*it)->client)
156 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
157 delete *it;
158 }
159 checks_.clear();
160
161 gethash_requests_.clear();
162}
163
164// Runs on the UI thread.
165void SafeBrowsingService::OnEnable(bool enabled) {
166 if (enabled)
167 Start();
168 else
169 ShutDown();
170}
171
172bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
173 return url.SchemeIs("http") || url.SchemeIs("https");
174}
175
176bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
177 DCHECK(MessageLoop::current() == io_loop_);
initial.commit09911bf2008-07-26 23:55:29178 if (!enabled_ || !database_)
179 return true;
180
[email protected]613a03b2008-10-24 23:02:00181 if (new_safe_browsing_)
182 return CheckUrlNew(url, client);
183
initial.commit09911bf2008-07-26 23:55:29184 if (!resetting_) {
185 Time start_time = Time::Now();
186 bool need_check = database_->NeedToCheckUrl(url);
[email protected]553dba62009-02-24 19:08:23187 UMA_HISTOGRAM_TIMES("SB.BloomFilter", Time::Now() - start_time);
initial.commit09911bf2008-07-26 23:55:29188 if (!need_check)
189 return true; // The url is definitely safe.
190 }
191
192 // The url may or may not be safe, need to go to the database to be sure.
193 SafeBrowsingCheck* check = new SafeBrowsingCheck();
194 check->url = url;
195 check->client = client;
196 check->result = URL_SAFE;
197 check->need_get_hash = false;
198 check->start = Time::Now();
199 checks_.insert(check);
200
[email protected]613a03b2008-10-24 23:02:00201 // Old school SafeBrowsing does an asynchronous database check.
initial.commit09911bf2008-07-26 23:55:29202 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
203 this, &SafeBrowsingService::CheckDatabase,
204 check, protocol_manager_->last_update()));
[email protected]613a03b2008-10-24 23:02:00205
206 return false;
207}
208
209bool SafeBrowsingService::CheckUrlNew(const GURL& url, Client* client) {
210 if (resetting_ || !database_loaded_) {
211 QueuedCheck check;
212 check.client = client;
213 check.url = url;
214 queued_checks_.push_back(check);
215 return false;
216 }
217
218 std::string list;
219 std::vector<SBPrefix> prefix_hits;
220 std::vector<SBFullHashResult> full_hits;
[email protected]22573822008-11-14 00:40:47221 base::Time check_start = base::Time::Now();
[email protected]c3ff89492008-11-11 02:17:51222 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
223 &full_hits,
[email protected]613a03b2008-10-24 23:02:00224 protocol_manager_->last_update());
[email protected]22573822008-11-14 00:40:47225
[email protected]553dba62009-02-24 19:08:23226 UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::Time::Now() - check_start);
[email protected]22573822008-11-14 00:40:47227
[email protected]613a03b2008-10-24 23:02:00228 if (!prefix_match)
229 return true; // URL is okay.
230
231 // Needs to be asynchronous, since we could be in the constructor of a
232 // ResourceDispatcherHost event handler which can't pause there.
233 SafeBrowsingCheck* check = new SafeBrowsingCheck();
234 check->url = url;
235 check->client = client;
236 check->result = URL_SAFE;
237 check->start = Time::Now();
238 check->need_get_hash = full_hits.empty();
239 check->prefix_hits.swap(prefix_hits);
240 check->full_hits.swap(full_hits);
241 checks_.insert(check);
242
243 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
244 this, &SafeBrowsingService::OnCheckDone, check));
245
initial.commit09911bf2008-07-26 23:55:29246 return false;
247}
248
249void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
250 ResourceType::Type resource_type,
251 UrlCheckResult result,
252 Client* client,
253 MessageLoop* ui_loop,
254 int render_process_host_id,
255 int render_view_id) {
256 // Check if the user has already ignored our warning for this render_view
257 // and domain.
258 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
259 const WhiteListedEntry& entry = white_listed_entries_[i];
260 if (entry.render_process_host_id == render_process_host_id &&
261 entry.render_view_id == render_view_id &&
262 entry.result == result &&
263 entry.domain ==
[email protected]8ac1a752008-07-31 19:40:37264 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
initial.commit09911bf2008-07-26 23:55:29265 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
266 this, &SafeBrowsingService::NotifyClientBlockingComplete,
267 client, true));
268 return;
269 }
270 }
271
[email protected]1b277e72009-01-23 21:05:34272 UnsafeResource resource;
273 resource.url = url;
274 resource.resource_type = resource_type;
275 resource.threat_type= result;
276 resource.client = client;
277 resource.render_process_host_id = render_process_host_id;
278 resource.render_view_id = render_view_id;
[email protected]cbab76d2008-10-13 22:42:47279 // The blocking page must be created from the UI thread.
280 ui_loop->PostTask(FROM_HERE, NewRunnableMethod(this,
281 &SafeBrowsingService::DoDisplayBlockingPage,
[email protected]1b277e72009-01-23 21:05:34282 resource));
[email protected]cbab76d2008-10-13 22:42:47283}
284
285// Invoked on the UI thread.
286void SafeBrowsingService::DoDisplayBlockingPage(
[email protected]1b277e72009-01-23 21:05:34287 const UnsafeResource& resource) {
[email protected]a3a1d142008-12-19 00:42:30288 // The tab might have been closed.
[email protected]dfdb0de72009-02-19 21:58:14289 WebContents* wc =
290 tab_util::GetWebContentsByID(resource.render_process_host_id,
291 resource.render_view_id);
292
293 if (!wc) {
[email protected]a3a1d142008-12-19 00:42:30294 // The tab is gone and we did not have a chance at showing the interstitial.
295 // Just act as "Don't Proceed" was chosen.
296 base::Thread* io_thread = g_browser_process->io_thread();
297 if (!io_thread)
298 return;
[email protected]1b277e72009-01-23 21:05:34299 std::vector<UnsafeResource> resources;
300 resources.push_back(resource);
[email protected]a3a1d142008-12-19 00:42:30301 io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]1b277e72009-01-23 21:05:34302 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
[email protected]a3a1d142008-12-19 00:42:30303 return;
304 }
[email protected]dfdb0de72009-02-19 21:58:14305
306 // Report the malware sub-resource to the SafeBrowsing servers if we have a
307 // malware sub-resource on a safe page and only if the user has opted in to
308 // reporting statistics.
309 PrefService* prefs = g_browser_process->local_state();
310 DCHECK(prefs);
311 if (prefs && prefs->GetBoolean(prefs::kMetricsReportingEnabled) &&
312 resource.resource_type != ResourceType::MAIN_FRAME &&
313 resource.threat_type == SafeBrowsingService::URL_MALWARE) {
314 GURL page_url = wc->GetURL();
315 GURL referrer_url;
316 if (wc->controller()) {
317 NavigationEntry* entry = wc->controller()->GetActiveEntry();
318 if (entry)
319 referrer_url = entry->referrer();
320 }
321 io_loop_->PostTask(FROM_HERE,
322 NewRunnableMethod(this,
323 &SafeBrowsingService::ReportMalware,
324 resource.url,
325 page_url,
326 referrer_url));
327 }
328
[email protected]1b277e72009-01-23 21:05:34329 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
initial.commit09911bf2008-07-26 23:55:29330}
331
332void SafeBrowsingService::CancelCheck(Client* client) {
333 DCHECK(MessageLoop::current() == io_loop_);
334
335 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
336 if ((*i)->client == client)
337 (*i)->client = NULL;
338 }
[email protected]613a03b2008-10-24 23:02:00339
340 // Scan the queued clients store. Clients may be here if they requested a URL
341 // check before the database has finished loading or resetting.
342 if (!database_loaded_ || resetting_) {
343 std::deque<QueuedCheck>::iterator it = queued_checks_.begin();
344 for (; it != queued_checks_.end(); ++it) {
345 if (it->client == client)
346 it->client = NULL;
347 }
348 }
initial.commit09911bf2008-07-26 23:55:29349}
350
351void SafeBrowsingService::CheckDatabase(SafeBrowsingCheck* info,
352 Time last_update) {
353 DCHECK(MessageLoop::current() == db_thread_->message_loop());
354 // If client == NULL it means it was cancelled, no need for db lookup.
355 if (info->client && GetDatabase()) {
356 Time now = Time::Now();
357 std::string list;
358 if (GetDatabase()->ContainsUrl(info->url,
359 &list,
360 &info->prefix_hits,
361 &info->full_hits,
362 last_update)) {
363 if (info->prefix_hits.empty()) {
364 info->result = GetResultFromListname(list);
365 } else {
366 if (info->full_hits.empty())
367 info->need_get_hash = true;
368 }
369 }
370 info->db_time = Time::Now() - now;
371 }
372
373 if (io_loop_)
374 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
375 this, &SafeBrowsingService::OnCheckDone, info));
376}
377
[email protected]613a03b2008-10-24 23:02:00378void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
initial.commit09911bf2008-07-26 23:55:29379 DCHECK(MessageLoop::current() == io_loop_);
380
381 // If we've been shutdown during the database lookup, this check will already
382 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00383 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29384 return;
385
[email protected]553dba62009-02-24 19:08:23386 UMA_HISTOGRAM_TIMES("SB.Database", Time::Now() - check->start);
[email protected]613a03b2008-10-24 23:02:00387 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29388 // We have a partial match so we need to query Google for the full hash.
389 // Clean up will happen in HandleGetHashResults.
390
391 // See if we have a GetHash request already in progress for this particular
392 // prefix. If so, we just append ourselves to the list of interested parties
393 // when the results arrive. We only do this for checks involving one prefix,
394 // since that is the common case (multiple prefixes will issue the request
395 // as normal).
[email protected]613a03b2008-10-24 23:02:00396 if (check->prefix_hits.size() == 1) {
397 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29398 GetHashRequests::iterator it = gethash_requests_.find(prefix);
399 if (it != gethash_requests_.end()) {
400 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00401 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29402 return;
403 }
404
405 // No request in progress, so we're the first for this prefix.
406 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00407 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29408 gethash_requests_[prefix] = requestors;
409 }
410
411 // Reset the start time so that we can measure the network time without the
412 // database time.
[email protected]613a03b2008-10-24 23:02:00413 check->start = Time::Now();
414 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29415 } else {
416 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00417 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29418 }
419}
420
421SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
422 DCHECK(MessageLoop::current() == db_thread_->message_loop());
423 if (database_)
424 return database_;
425
[email protected]c870c762009-01-28 05:47:15426 FilePath path;
initial.commit09911bf2008-07-26 23:55:29427 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
428 DCHECK(result);
[email protected]c870c762009-01-28 05:47:15429 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29430
431 Time before = Time::Now();
[email protected]54d80bb02008-09-20 02:03:08432 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
[email protected]613a03b2008-10-24 23:02:00433 Callback0::Type* chunk_callback =
initial.commit09911bf2008-07-26 23:55:29434 NewCallback(this, &SafeBrowsingService::ChunkInserted);
[email protected]613a03b2008-10-24 23:02:00435 bool init_success = database->Init(path, chunk_callback);
436
437 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
438 this, &SafeBrowsingService::DatabaseLoadComplete, !init_success));
439
440 if (!init_success) {
initial.commit09911bf2008-07-26 23:55:29441 NOTREACHED();
442 return NULL;
443 }
444
445 database_ = database;
446
447 TimeDelta open_time = Time::Now() - before;
448 SB_DLOG(INFO) << "SafeBrowsing database open took " <<
449 open_time.InMilliseconds() << " ms.";
450
451 return database_;
452}
453
454// Public API called only on the IO thread.
455// The SafeBrowsingProtocolManager has received the full hash results for
456// prefix hits detected in the database.
457void SafeBrowsingService::HandleGetHashResults(
458 SafeBrowsingCheck* check,
[email protected]200abc32008-09-05 01:44:33459 const std::vector<SBFullHashResult>& full_hashes,
460 bool can_cache) {
initial.commit09911bf2008-07-26 23:55:29461 if (checks_.find(check) == checks_.end())
462 return;
463
464 DCHECK(enabled_);
465
[email protected]22573822008-11-14 00:40:47466 if (new_safe_browsing_)
[email protected]553dba62009-02-24 19:08:23467 UMA_HISTOGRAM_LONG_TIMES("SB2.Network", Time::Now() - check->start);
[email protected]22573822008-11-14 00:40:47468 else
[email protected]553dba62009-02-24 19:08:23469 UMA_HISTOGRAM_LONG_TIMES("SB.Network", Time::Now() - check->start);
[email protected]22573822008-11-14 00:40:47470
[email protected]200abc32008-09-05 01:44:33471 std::vector<SBPrefix> prefixes = check->prefix_hits;
initial.commit09911bf2008-07-26 23:55:29472 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
473
[email protected]613a03b2008-10-24 23:02:00474 if (can_cache) {
475 if (!new_safe_browsing_) {
476 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
477 this, &SafeBrowsingService::CacheHashResults, prefixes, full_hashes));
478 } else if (database_) {
[email protected]c3ff89492008-11-11 02:17:51479 // Cache the GetHash results in memory:
[email protected]613a03b2008-10-24 23:02:00480 database_->CacheHashResults(prefixes, full_hashes);
481 }
482 }
initial.commit09911bf2008-07-26 23:55:29483}
484
485void SafeBrowsingService::OnHandleGetHashResults(
486 SafeBrowsingCheck* check,
487 const std::vector<SBFullHashResult>& full_hashes) {
488 SBPrefix prefix = check->prefix_hits[0];
489 GetHashRequests::iterator it = gethash_requests_.find(prefix);
490 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
491 HandleOneCheck(check, full_hashes);
492 return;
493 }
494
495 // Call back all interested parties.
496 GetHashRequestors& requestors = it->second;
497 for (GetHashRequestors::iterator r = requestors.begin();
498 r != requestors.end(); ++r) {
499 HandleOneCheck(*r, full_hashes);
500 }
501
502 gethash_requests_.erase(it);
503}
504
505void SafeBrowsingService::HandleOneCheck(
506 SafeBrowsingCheck* check,
507 const std::vector<SBFullHashResult>& full_hashes) {
508 if (check->client) {
509 UrlCheckResult result = URL_SAFE;
510 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
511 if (index != -1)
512 result = GetResultFromListname(full_hashes[index].list_name);
513
514 // Let the client continue handling the original request.
515 check->client->OnUrlCheckResult(check->url, result);
516 }
517
518 checks_.erase(check);
519 delete check;
520}
521
[email protected]57119c3f2008-12-04 00:33:04522void SafeBrowsingService::UpdateStarted() {
initial.commit09911bf2008-07-26 23:55:29523 DCHECK(MessageLoop::current() == io_loop_);
524 DCHECK(enabled_);
[email protected]57119c3f2008-12-04 00:33:04525 DCHECK(!update_in_progress_);
526 update_in_progress_ = true;
initial.commit09911bf2008-07-26 23:55:29527 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
528 this, &SafeBrowsingService::GetAllChunksFromDatabase));
529}
530
[email protected]613a03b2008-10-24 23:02:00531void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
[email protected]aad08752008-10-02 22:13:41532 DCHECK(MessageLoop::current() == io_loop_);
533 DCHECK(enabled_);
[email protected]57119c3f2008-12-04 00:33:04534 if (update_in_progress_) {
535 update_in_progress_ = false;
536 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
537 this, &SafeBrowsingService::DatabaseUpdateFinished, update_succeeded));
538 }
[email protected]aad08752008-10-02 22:13:41539}
540
[email protected]613a03b2008-10-24 23:02:00541void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
[email protected]aad08752008-10-02 22:13:41542 DCHECK(MessageLoop::current() == db_thread_->message_loop());
543 if (GetDatabase())
[email protected]613a03b2008-10-24 23:02:00544 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]aad08752008-10-02 22:13:41545}
546
[email protected]1b277e72009-01-23 21:05:34547void SafeBrowsingService::OnBlockingPageDone(
548 const std::vector<UnsafeResource>& resources,
549 bool proceed) {
550 for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
551 iter != resources.end(); ++iter) {
552 const UnsafeResource& resource = *iter;
553 NotifyClientBlockingComplete(resource.client, proceed);
initial.commit09911bf2008-07-26 23:55:29554
[email protected]1b277e72009-01-23 21:05:34555 if (proceed) {
556 // Whitelist this domain and warning type for the given tab.
557 WhiteListedEntry entry;
558 entry.render_process_host_id = resource.render_process_host_id;
559 entry.render_view_id = resource.render_view_id;
560 entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
561 resource.url);
562 entry.result = resource.threat_type;
563 white_listed_entries_.push_back(entry);
564 }
initial.commit09911bf2008-07-26 23:55:29565 }
initial.commit09911bf2008-07-26 23:55:29566}
567
568void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
569 bool proceed) {
570 client->OnBlockingPageComplete(proceed);
571}
572
573// This method runs on the UI loop to access the prefs.
574void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
575 const std::string& wrapped_key) {
576 PrefService* prefs = g_browser_process->local_state();
577 if (prefs) {
578 prefs->SetString(prefs::kSafeBrowsingClientKey, ASCIIToWide(client_key));
579 prefs->SetString(prefs::kSafeBrowsingWrappedKey, ASCIIToWide(wrapped_key));
580 }
581}
582
583void SafeBrowsingService::ChunkInserted() {
[email protected]fbb2b7a2008-11-18 22:54:04584 DCHECK(MessageLoop::current() == db_thread_->message_loop());
initial.commit09911bf2008-07-26 23:55:29585 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
586 this, &SafeBrowsingService::OnChunkInserted));
587}
588
589void SafeBrowsingService::OnChunkInserted() {
590 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04591 if (enabled_)
592 protocol_manager_->OnChunkInserted();
initial.commit09911bf2008-07-26 23:55:29593}
594
[email protected]613a03b2008-10-24 23:02:00595void SafeBrowsingService::DatabaseLoadComplete(bool database_error) {
596 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04597 if (!enabled_)
598 return;
[email protected]613a03b2008-10-24 23:02:00599
600 database_loaded_ = true;
601
602 // TODO(paulg): More robust database initialization error handling.
603 if (protocol_manager_ && !database_error)
604 protocol_manager_->Initialize();
605
606 // If we have any queued requests, we can now check them.
607 if (!resetting_)
608 RunQueuedClients();
609}
610
initial.commit09911bf2008-07-26 23:55:29611// static
[email protected]919d77f02009-01-06 19:48:35612void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
initial.commit09911bf2008-07-26 23:55:29613 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, L"");
614 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, L"");
615}
616
617void SafeBrowsingService::ResetDatabase() {
618 DCHECK(MessageLoop::current() == io_loop_);
619 resetting_ = true;
620 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
621 this, &SafeBrowsingService::OnResetDatabase));
622}
623
624void SafeBrowsingService::OnResetDatabase() {
625 DCHECK(MessageLoop::current() == db_thread_->message_loop());
626 GetDatabase()->ResetDatabase();
627 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
628 this, &SafeBrowsingService::OnResetComplete));
629}
630
631void SafeBrowsingService::OnResetComplete() {
632 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04633 if (enabled_) {
634 resetting_ = false;
635 database_loaded_ = true;
636 RunQueuedClients();
637 }
initial.commit09911bf2008-07-26 23:55:29638}
639
640void SafeBrowsingService::HandleChunk(const std::string& list,
641 std::deque<SBChunk>* chunks) {
642 DCHECK(MessageLoop::current() == io_loop_);
643 DCHECK(enabled_);
644 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
645 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
646}
647
648void SafeBrowsingService::HandleChunkForDatabase(
649 const std::string& list_name,
650 std::deque<SBChunk>* chunks) {
651 DCHECK(MessageLoop::current() == db_thread_->message_loop());
652
653 GetDatabase()->InsertChunks(list_name, chunks);
654}
655
656void SafeBrowsingService::HandleChunkDelete(
657 std::vector<SBChunkDelete>* chunk_deletes) {
658 DCHECK(MessageLoop::current() == io_loop_);
659 DCHECK(enabled_);
660 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
661 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
662}
663
664void SafeBrowsingService::DeleteChunks(
665 std::vector<SBChunkDelete>* chunk_deletes) {
666 DCHECK(MessageLoop::current() == db_thread_->message_loop());
667
668 GetDatabase()->DeleteChunks(chunk_deletes);
669}
670
671// Database worker function.
672void SafeBrowsingService::GetAllChunksFromDatabase() {
673 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]a5a37522008-11-11 21:49:56674 bool database_error = true;
initial.commit09911bf2008-07-26 23:55:29675 std::vector<SBListChunkRanges> lists;
[email protected]53ad8572008-11-13 21:50:34676 if (GetDatabase()) {
677 if (GetDatabase()->UpdateStarted()) {
678 GetDatabase()->GetListsInfo(&lists);
679 database_error = false;
680 } else {
681 GetDatabase()->UpdateFinished(false);
682 }
initial.commit09911bf2008-07-26 23:55:29683 }
684
685 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
686 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
687 database_error));
688}
689
690// Called on the io thread with the results of all chunks.
691void SafeBrowsingService::OnGetAllChunksFromDatabase(
692 const std::vector<SBListChunkRanges>& lists, bool database_error) {
693 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04694 if (enabled_)
695 protocol_manager_->OnGetChunksComplete(lists, database_error);
initial.commit09911bf2008-07-26 23:55:29696}
697
698SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
699 const std::string& list_name) {
700 if (safe_browsing_util::IsPhishingList(list_name)) {
701 return URL_PHISHING;
702 }
703
704 if (safe_browsing_util::IsMalwareList(list_name)) {
705 return URL_MALWARE;
706 }
707
708 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
709 return URL_SAFE;
710}
711
initial.commit09911bf2008-07-26 23:55:29712void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
[email protected]fc76de12008-11-14 01:51:18713 if (new_safe_browsing_)
[email protected]553dba62009-02-24 19:08:23714 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
[email protected]fc76de12008-11-14 01:51:18715 else
[email protected]553dba62009-02-24 19:08:23716 UMA_HISTOGRAM_LONG_TIMES("SB.Delay", time);
initial.commit09911bf2008-07-26 23:55:29717}
718
719void SafeBrowsingService::CacheHashResults(
[email protected]200abc32008-09-05 01:44:33720 const std::vector<SBPrefix>& prefixes,
initial.commit09911bf2008-07-26 23:55:29721 const std::vector<SBFullHashResult>& full_hashes) {
722 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]200abc32008-09-05 01:44:33723 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29724}
725
[email protected]0a3076572008-12-13 20:48:36726void SafeBrowsingService::OnSuspend(base::SystemMonitor*) {
initial.commit09911bf2008-07-26 23:55:29727}
728
729// Tell the SafeBrowsing database not to do expensive disk operations for a few
730// minutes after waking up. It's quite likely that the act of resuming from a
731// low power state will involve much disk activity, which we don't want to
732// exacerbate.
[email protected]0a3076572008-12-13 20:48:36733void SafeBrowsingService::OnResume(base::SystemMonitor*) {
initial.commit09911bf2008-07-26 23:55:29734 if (enabled_) {
[email protected]0a3076572008-12-13 20:48:36735 ChromeThread::GetMessageLoop(ChromeThread::DB)->PostTask(FROM_HERE,
736 NewRunnableMethod(this, &SafeBrowsingService::HandleResume));
initial.commit09911bf2008-07-26 23:55:29737 }
738}
739
740void SafeBrowsingService::HandleResume() {
741 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]53ad8572008-11-13 21:50:34742 // We don't call GetDatabase() here, since we want to avoid unnecessary calls
743 // to Open, Reset, etc, or reload the bloom filter while we're coming out of
744 // a suspended state.
745 if (database_)
746 database_->HandleResume();
license.botbf09a502008-08-24 00:55:55747}
[email protected]613a03b2008-10-24 23:02:00748
749void SafeBrowsingService::RunQueuedClients() {
750 DCHECK(MessageLoop::current() == io_loop_);
[email protected]553dba62009-02-24 19:08:23751 HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
[email protected]613a03b2008-10-24 23:02:00752 while (!queued_checks_.empty()) {
753 QueuedCheck check = queued_checks_.front();
[email protected]553dba62009-02-24 19:08:23754 HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
[email protected]613a03b2008-10-24 23:02:00755 CheckUrl(check.url, check.client);
756 queued_checks_.pop_front();
757 }
758}
759
[email protected]dfdb0de72009-02-19 21:58:14760void SafeBrowsingService::ReportMalware(const GURL& malware_url,
761 const GURL& page_url,
762 const GURL& referrer_url) {
763 DCHECK(MessageLoop::current() == io_loop_);
764
765 // We need to access the database cache on the io_loop_ which is only allowed
766 // in the new SafeBrowsing database system.
767 if (!new_safe_browsing_ || !enabled_ || !database_)
768 return;
769
770 // Check if 'page_url' is already blacklisted (exists in our cache). Only
771 // report if it's not there.
772 std::string list;
773 std::vector<SBPrefix> prefix_hits;
774 std::vector<SBFullHashResult> full_hits;
775 database_->ContainsUrl(page_url, &list, &prefix_hits, &full_hits,
776 protocol_manager_->last_update());
777
778 if (full_hits.empty())
779 protocol_manager_->ReportMalware(malware_url, page_url, referrer_url);
780}