blob: 04fb70256759f85622345df636afdf571028218d [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"
[email protected]dcf7d352009-02-26 01:56:0228#include "chrome/common/url_constants.h"
initial.commit09911bf2008-07-26 23:55:2929#include "net/base/registry_controlled_domain.h"
30
[email protected]e1acf6f2008-10-27 20:43:3331using base::Time;
32using base::TimeDelta;
33
initial.commit09911bf2008-07-26 23:55:2934SafeBrowsingService::SafeBrowsingService()
35 : io_loop_(NULL),
36 database_(NULL),
37 protocol_manager_(NULL),
38 enabled_(false),
[email protected]613a03b2008-10-24 23:02:0039 resetting_(false),
[email protected]57119c3f2008-12-04 00:33:0440 database_loaded_(false),
41 update_in_progress_(false) {
[email protected]bb975362009-01-21 01:00:2242 new_safe_browsing_ = !CommandLine::ForCurrentProcess()->HasSwitch(
43 switches::kUseOldSafeBrowsing);
[email protected]0a3076572008-12-13 20:48:3644 base::SystemMonitor* monitor = base::SystemMonitor::Get();
45 DCHECK(monitor);
46 if (monitor)
47 monitor->AddObserver(this);
initial.commit09911bf2008-07-26 23:55:2948}
49
50SafeBrowsingService::~SafeBrowsingService() {
[email protected]0a3076572008-12-13 20:48:3651 base::SystemMonitor* monitor = base::SystemMonitor::Get();
52 if (monitor)
53 monitor->RemoveObserver(this);
initial.commit09911bf2008-07-26 23:55:2954}
55
56// Only called on the UI thread.
57void SafeBrowsingService::Initialize(MessageLoop* io_loop) {
58 io_loop_ = io_loop;
59
60 // Get the profile's preference for SafeBrowsing.
[email protected]c870c762009-01-28 05:47:1561 FilePath user_data_dir;
initial.commit09911bf2008-07-26 23:55:2962 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
63 ProfileManager* profile_manager = g_browser_process->profile_manager();
[email protected]f7011fcb2009-01-28 21:54:3264 Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
initial.commit09911bf2008-07-26 23:55:2965 PrefService* pref_service = profile->GetPrefs();
66 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
67 Start();
68}
69
70// Start up SafeBrowsing objects. This can be called at browser start, or when
71// the user checks the "Enable SafeBrowsing" option in the Advanced options UI.
72void SafeBrowsingService::Start() {
73 DCHECK(!db_thread_.get());
[email protected]ab820df2008-08-26 05:55:1074 db_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
initial.commit09911bf2008-07-26 23:55:2975 if (!db_thread_->Start())
76 return;
77
initial.commit09911bf2008-07-26 23:55:2978 // Retrieve client MAC keys.
79 PrefService* local_state = g_browser_process->local_state();
80 std::string client_key, wrapped_key;
81 if (local_state) {
82 client_key =
83 WideToASCII(local_state->GetString(prefs::kSafeBrowsingClientKey));
84 wrapped_key =
85 WideToASCII(local_state->GetString(prefs::kSafeBrowsingWrappedKey));
86 }
87
88 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
89 this, &SafeBrowsingService::OnIOInitialize, MessageLoop::current(),
90 client_key, wrapped_key));
[email protected]2c2fb222008-12-17 02:35:4691
92 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
93 this, &SafeBrowsingService::OnDBInitialize));
initial.commit09911bf2008-07-26 23:55:2994}
95
96void SafeBrowsingService::ShutDown() {
97 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
98 this, &SafeBrowsingService::OnIOShutdown));
99}
100
101void SafeBrowsingService::OnIOInitialize(MessageLoop* notify_loop,
102 const std::string& client_key,
103 const std::string& wrapped_key) {
104 DCHECK(MessageLoop::current() == io_loop_);
105 enabled_ = true;
106 protocol_manager_ = new SafeBrowsingProtocolManager(this,
107 notify_loop,
108 client_key,
109 wrapped_key);
[email protected]613a03b2008-10-24 23:02:00110 // We want to initialize the protocol manager only after the database has
111 // loaded, which we'll receive asynchronously (DatabaseLoadComplete). If
112 // database_loaded_ isn't true, we'll wait for that notification to do the
113 // init.
114 if (database_loaded_)
115 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29116}
117
118void SafeBrowsingService::OnDBInitialize() {
119 DCHECK(MessageLoop::current() == db_thread_->message_loop());
120 GetDatabase();
121}
122
123void SafeBrowsingService::OnIOShutdown() {
124 DCHECK(MessageLoop::current() == io_loop_);
125 if (!enabled_)
126 return;
127
128 enabled_ = false;
[email protected]fbb2b7a2008-11-18 22:54:04129 resetting_ = false;
initial.commit09911bf2008-07-26 23:55:29130
131 // This cancels all in-flight GetHash requests.
132 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04133 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29134
135 if (db_thread_.get())
136 db_thread_->message_loop()->DeleteSoon(FROM_HERE, database_);
137
138 // Flush the database thread. Any in-progress database check results will be
139 // ignored and cleaned up below.
140 db_thread_.reset(NULL);
141
142 database_ = NULL;
[email protected]fbb2b7a2008-11-18 22:54:04143 database_loaded_ = false;
initial.commit09911bf2008-07-26 23:55:29144
[email protected]613a03b2008-10-24 23:02:00145 // Delete queued and pending checks once the database thread is done, calling
146 // back any clients with 'URL_SAFE'.
147 while (!queued_checks_.empty()) {
148 QueuedCheck check = queued_checks_.front();
149 if (check.client)
150 check.client->OnUrlCheckResult(check.url, URL_SAFE);
151 queued_checks_.pop_front();
152 }
153
initial.commit09911bf2008-07-26 23:55:29154 for (CurrentChecks::iterator it = checks_.begin();
155 it != checks_.end(); ++it) {
156 if ((*it)->client)
157 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
158 delete *it;
159 }
160 checks_.clear();
161
162 gethash_requests_.clear();
163}
164
165// Runs on the UI thread.
166void SafeBrowsingService::OnEnable(bool enabled) {
167 if (enabled)
168 Start();
169 else
170 ShutDown();
171}
172
173bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
[email protected]dcf7d352009-02-26 01:56:02174 return url.SchemeIs(chrome::kHttpScheme) ||
175 url.SchemeIs(chrome::kHttpsScheme);
initial.commit09911bf2008-07-26 23:55:29176}
177
178bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
179 DCHECK(MessageLoop::current() == io_loop_);
initial.commit09911bf2008-07-26 23:55:29180 if (!enabled_ || !database_)
181 return true;
182
[email protected]613a03b2008-10-24 23:02:00183 if (new_safe_browsing_)
184 return CheckUrlNew(url, client);
185
initial.commit09911bf2008-07-26 23:55:29186 if (!resetting_) {
187 Time start_time = Time::Now();
188 bool need_check = database_->NeedToCheckUrl(url);
[email protected]553dba62009-02-24 19:08:23189 UMA_HISTOGRAM_TIMES("SB.BloomFilter", Time::Now() - start_time);
initial.commit09911bf2008-07-26 23:55:29190 if (!need_check)
191 return true; // The url is definitely safe.
192 }
193
194 // The url may or may not be safe, need to go to the database to be sure.
195 SafeBrowsingCheck* check = new SafeBrowsingCheck();
196 check->url = url;
197 check->client = client;
198 check->result = URL_SAFE;
199 check->need_get_hash = false;
200 check->start = Time::Now();
201 checks_.insert(check);
202
[email protected]613a03b2008-10-24 23:02:00203 // Old school SafeBrowsing does an asynchronous database check.
initial.commit09911bf2008-07-26 23:55:29204 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
205 this, &SafeBrowsingService::CheckDatabase,
206 check, protocol_manager_->last_update()));
[email protected]613a03b2008-10-24 23:02:00207
208 return false;
209}
210
211bool SafeBrowsingService::CheckUrlNew(const GURL& url, Client* client) {
212 if (resetting_ || !database_loaded_) {
213 QueuedCheck check;
214 check.client = client;
215 check.url = url;
216 queued_checks_.push_back(check);
217 return false;
218 }
219
220 std::string list;
221 std::vector<SBPrefix> prefix_hits;
222 std::vector<SBFullHashResult> full_hits;
[email protected]22573822008-11-14 00:40:47223 base::Time check_start = base::Time::Now();
[email protected]c3ff89492008-11-11 02:17:51224 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
225 &full_hits,
[email protected]613a03b2008-10-24 23:02:00226 protocol_manager_->last_update());
[email protected]22573822008-11-14 00:40:47227
[email protected]553dba62009-02-24 19:08:23228 UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::Time::Now() - check_start);
[email protected]22573822008-11-14 00:40:47229
[email protected]613a03b2008-10-24 23:02:00230 if (!prefix_match)
231 return true; // URL is okay.
232
233 // Needs to be asynchronous, since we could be in the constructor of a
234 // ResourceDispatcherHost event handler which can't pause there.
235 SafeBrowsingCheck* check = new SafeBrowsingCheck();
236 check->url = url;
237 check->client = client;
238 check->result = URL_SAFE;
239 check->start = Time::Now();
240 check->need_get_hash = full_hits.empty();
241 check->prefix_hits.swap(prefix_hits);
242 check->full_hits.swap(full_hits);
243 checks_.insert(check);
244
245 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
246 this, &SafeBrowsingService::OnCheckDone, check));
247
initial.commit09911bf2008-07-26 23:55:29248 return false;
249}
250
251void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
252 ResourceType::Type resource_type,
253 UrlCheckResult result,
254 Client* client,
255 MessageLoop* ui_loop,
256 int render_process_host_id,
257 int render_view_id) {
258 // Check if the user has already ignored our warning for this render_view
259 // and domain.
260 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
261 const WhiteListedEntry& entry = white_listed_entries_[i];
262 if (entry.render_process_host_id == render_process_host_id &&
263 entry.render_view_id == render_view_id &&
264 entry.result == result &&
265 entry.domain ==
[email protected]8ac1a752008-07-31 19:40:37266 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
initial.commit09911bf2008-07-26 23:55:29267 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
268 this, &SafeBrowsingService::NotifyClientBlockingComplete,
269 client, true));
270 return;
271 }
272 }
273
[email protected]1b277e72009-01-23 21:05:34274 UnsafeResource resource;
275 resource.url = url;
276 resource.resource_type = resource_type;
277 resource.threat_type= result;
278 resource.client = client;
279 resource.render_process_host_id = render_process_host_id;
280 resource.render_view_id = render_view_id;
[email protected]cbab76d2008-10-13 22:42:47281 // The blocking page must be created from the UI thread.
282 ui_loop->PostTask(FROM_HERE, NewRunnableMethod(this,
283 &SafeBrowsingService::DoDisplayBlockingPage,
[email protected]1b277e72009-01-23 21:05:34284 resource));
[email protected]cbab76d2008-10-13 22:42:47285}
286
287// Invoked on the UI thread.
288void SafeBrowsingService::DoDisplayBlockingPage(
[email protected]1b277e72009-01-23 21:05:34289 const UnsafeResource& resource) {
[email protected]a3a1d142008-12-19 00:42:30290 // The tab might have been closed.
[email protected]dfdb0de72009-02-19 21:58:14291 WebContents* wc =
292 tab_util::GetWebContentsByID(resource.render_process_host_id,
293 resource.render_view_id);
294
295 if (!wc) {
[email protected]a3a1d142008-12-19 00:42:30296 // The tab is gone and we did not have a chance at showing the interstitial.
297 // Just act as "Don't Proceed" was chosen.
298 base::Thread* io_thread = g_browser_process->io_thread();
299 if (!io_thread)
300 return;
[email protected]1b277e72009-01-23 21:05:34301 std::vector<UnsafeResource> resources;
302 resources.push_back(resource);
[email protected]a3a1d142008-12-19 00:42:30303 io_thread->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]1b277e72009-01-23 21:05:34304 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
[email protected]a3a1d142008-12-19 00:42:30305 return;
306 }
[email protected]dfdb0de72009-02-19 21:58:14307
308 // Report the malware sub-resource to the SafeBrowsing servers if we have a
309 // malware sub-resource on a safe page and only if the user has opted in to
310 // reporting statistics.
311 PrefService* prefs = g_browser_process->local_state();
312 DCHECK(prefs);
313 if (prefs && prefs->GetBoolean(prefs::kMetricsReportingEnabled) &&
314 resource.resource_type != ResourceType::MAIN_FRAME &&
315 resource.threat_type == SafeBrowsingService::URL_MALWARE) {
316 GURL page_url = wc->GetURL();
317 GURL referrer_url;
318 if (wc->controller()) {
319 NavigationEntry* entry = wc->controller()->GetActiveEntry();
320 if (entry)
321 referrer_url = entry->referrer();
322 }
323 io_loop_->PostTask(FROM_HERE,
324 NewRunnableMethod(this,
325 &SafeBrowsingService::ReportMalware,
326 resource.url,
327 page_url,
328 referrer_url));
329 }
330
[email protected]1b277e72009-01-23 21:05:34331 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
initial.commit09911bf2008-07-26 23:55:29332}
333
334void SafeBrowsingService::CancelCheck(Client* client) {
335 DCHECK(MessageLoop::current() == io_loop_);
336
337 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
338 if ((*i)->client == client)
339 (*i)->client = NULL;
340 }
[email protected]613a03b2008-10-24 23:02:00341
342 // Scan the queued clients store. Clients may be here if they requested a URL
343 // check before the database has finished loading or resetting.
344 if (!database_loaded_ || resetting_) {
345 std::deque<QueuedCheck>::iterator it = queued_checks_.begin();
346 for (; it != queued_checks_.end(); ++it) {
347 if (it->client == client)
348 it->client = NULL;
349 }
350 }
initial.commit09911bf2008-07-26 23:55:29351}
352
353void SafeBrowsingService::CheckDatabase(SafeBrowsingCheck* info,
354 Time last_update) {
355 DCHECK(MessageLoop::current() == db_thread_->message_loop());
356 // If client == NULL it means it was cancelled, no need for db lookup.
357 if (info->client && GetDatabase()) {
358 Time now = Time::Now();
359 std::string list;
360 if (GetDatabase()->ContainsUrl(info->url,
361 &list,
362 &info->prefix_hits,
363 &info->full_hits,
364 last_update)) {
365 if (info->prefix_hits.empty()) {
366 info->result = GetResultFromListname(list);
367 } else {
368 if (info->full_hits.empty())
369 info->need_get_hash = true;
370 }
371 }
372 info->db_time = Time::Now() - now;
373 }
374
375 if (io_loop_)
376 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
377 this, &SafeBrowsingService::OnCheckDone, info));
378}
379
[email protected]613a03b2008-10-24 23:02:00380void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
initial.commit09911bf2008-07-26 23:55:29381 DCHECK(MessageLoop::current() == io_loop_);
382
383 // If we've been shutdown during the database lookup, this check will already
384 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00385 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29386 return;
387
[email protected]553dba62009-02-24 19:08:23388 UMA_HISTOGRAM_TIMES("SB.Database", Time::Now() - check->start);
[email protected]613a03b2008-10-24 23:02:00389 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29390 // We have a partial match so we need to query Google for the full hash.
391 // Clean up will happen in HandleGetHashResults.
392
393 // See if we have a GetHash request already in progress for this particular
394 // prefix. If so, we just append ourselves to the list of interested parties
395 // when the results arrive. We only do this for checks involving one prefix,
396 // since that is the common case (multiple prefixes will issue the request
397 // as normal).
[email protected]613a03b2008-10-24 23:02:00398 if (check->prefix_hits.size() == 1) {
399 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29400 GetHashRequests::iterator it = gethash_requests_.find(prefix);
401 if (it != gethash_requests_.end()) {
402 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00403 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29404 return;
405 }
406
407 // No request in progress, so we're the first for this prefix.
408 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00409 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29410 gethash_requests_[prefix] = requestors;
411 }
412
413 // Reset the start time so that we can measure the network time without the
414 // database time.
[email protected]613a03b2008-10-24 23:02:00415 check->start = Time::Now();
416 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29417 } else {
418 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00419 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29420 }
421}
422
423SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
424 DCHECK(MessageLoop::current() == db_thread_->message_loop());
425 if (database_)
426 return database_;
427
[email protected]c870c762009-01-28 05:47:15428 FilePath path;
initial.commit09911bf2008-07-26 23:55:29429 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
430 DCHECK(result);
[email protected]c870c762009-01-28 05:47:15431 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29432
433 Time before = Time::Now();
[email protected]54d80bb02008-09-20 02:03:08434 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
[email protected]613a03b2008-10-24 23:02:00435 Callback0::Type* chunk_callback =
initial.commit09911bf2008-07-26 23:55:29436 NewCallback(this, &SafeBrowsingService::ChunkInserted);
[email protected]613a03b2008-10-24 23:02:00437 bool init_success = database->Init(path, chunk_callback);
438
439 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
440 this, &SafeBrowsingService::DatabaseLoadComplete, !init_success));
441
442 if (!init_success) {
initial.commit09911bf2008-07-26 23:55:29443 NOTREACHED();
444 return NULL;
445 }
446
447 database_ = database;
448
449 TimeDelta open_time = Time::Now() - before;
450 SB_DLOG(INFO) << "SafeBrowsing database open took " <<
451 open_time.InMilliseconds() << " ms.";
452
453 return database_;
454}
455
456// Public API called only on the IO thread.
457// The SafeBrowsingProtocolManager has received the full hash results for
458// prefix hits detected in the database.
459void SafeBrowsingService::HandleGetHashResults(
460 SafeBrowsingCheck* check,
[email protected]200abc32008-09-05 01:44:33461 const std::vector<SBFullHashResult>& full_hashes,
462 bool can_cache) {
initial.commit09911bf2008-07-26 23:55:29463 if (checks_.find(check) == checks_.end())
464 return;
465
466 DCHECK(enabled_);
467
[email protected]22573822008-11-14 00:40:47468 if (new_safe_browsing_)
[email protected]553dba62009-02-24 19:08:23469 UMA_HISTOGRAM_LONG_TIMES("SB2.Network", Time::Now() - check->start);
[email protected]22573822008-11-14 00:40:47470 else
[email protected]553dba62009-02-24 19:08:23471 UMA_HISTOGRAM_LONG_TIMES("SB.Network", Time::Now() - check->start);
[email protected]22573822008-11-14 00:40:47472
[email protected]200abc32008-09-05 01:44:33473 std::vector<SBPrefix> prefixes = check->prefix_hits;
initial.commit09911bf2008-07-26 23:55:29474 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
475
[email protected]613a03b2008-10-24 23:02:00476 if (can_cache) {
477 if (!new_safe_browsing_) {
478 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
479 this, &SafeBrowsingService::CacheHashResults, prefixes, full_hashes));
480 } else if (database_) {
[email protected]c3ff89492008-11-11 02:17:51481 // Cache the GetHash results in memory:
[email protected]613a03b2008-10-24 23:02:00482 database_->CacheHashResults(prefixes, full_hashes);
483 }
484 }
initial.commit09911bf2008-07-26 23:55:29485}
486
487void SafeBrowsingService::OnHandleGetHashResults(
488 SafeBrowsingCheck* check,
489 const std::vector<SBFullHashResult>& full_hashes) {
490 SBPrefix prefix = check->prefix_hits[0];
491 GetHashRequests::iterator it = gethash_requests_.find(prefix);
492 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
493 HandleOneCheck(check, full_hashes);
494 return;
495 }
496
497 // Call back all interested parties.
498 GetHashRequestors& requestors = it->second;
499 for (GetHashRequestors::iterator r = requestors.begin();
500 r != requestors.end(); ++r) {
501 HandleOneCheck(*r, full_hashes);
502 }
503
504 gethash_requests_.erase(it);
505}
506
507void SafeBrowsingService::HandleOneCheck(
508 SafeBrowsingCheck* check,
509 const std::vector<SBFullHashResult>& full_hashes) {
510 if (check->client) {
511 UrlCheckResult result = URL_SAFE;
512 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
513 if (index != -1)
514 result = GetResultFromListname(full_hashes[index].list_name);
515
516 // Let the client continue handling the original request.
517 check->client->OnUrlCheckResult(check->url, result);
518 }
519
520 checks_.erase(check);
521 delete check;
522}
523
[email protected]57119c3f2008-12-04 00:33:04524void SafeBrowsingService::UpdateStarted() {
initial.commit09911bf2008-07-26 23:55:29525 DCHECK(MessageLoop::current() == io_loop_);
526 DCHECK(enabled_);
[email protected]57119c3f2008-12-04 00:33:04527 DCHECK(!update_in_progress_);
528 update_in_progress_ = true;
initial.commit09911bf2008-07-26 23:55:29529 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
530 this, &SafeBrowsingService::GetAllChunksFromDatabase));
531}
532
[email protected]613a03b2008-10-24 23:02:00533void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
[email protected]aad08752008-10-02 22:13:41534 DCHECK(MessageLoop::current() == io_loop_);
535 DCHECK(enabled_);
[email protected]57119c3f2008-12-04 00:33:04536 if (update_in_progress_) {
537 update_in_progress_ = false;
538 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
539 this, &SafeBrowsingService::DatabaseUpdateFinished, update_succeeded));
540 }
[email protected]aad08752008-10-02 22:13:41541}
542
[email protected]613a03b2008-10-24 23:02:00543void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
[email protected]aad08752008-10-02 22:13:41544 DCHECK(MessageLoop::current() == db_thread_->message_loop());
545 if (GetDatabase())
[email protected]613a03b2008-10-24 23:02:00546 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]aad08752008-10-02 22:13:41547}
548
[email protected]1b277e72009-01-23 21:05:34549void SafeBrowsingService::OnBlockingPageDone(
550 const std::vector<UnsafeResource>& resources,
551 bool proceed) {
552 for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
553 iter != resources.end(); ++iter) {
554 const UnsafeResource& resource = *iter;
555 NotifyClientBlockingComplete(resource.client, proceed);
initial.commit09911bf2008-07-26 23:55:29556
[email protected]1b277e72009-01-23 21:05:34557 if (proceed) {
558 // Whitelist this domain and warning type for the given tab.
559 WhiteListedEntry entry;
560 entry.render_process_host_id = resource.render_process_host_id;
561 entry.render_view_id = resource.render_view_id;
562 entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
563 resource.url);
564 entry.result = resource.threat_type;
565 white_listed_entries_.push_back(entry);
566 }
initial.commit09911bf2008-07-26 23:55:29567 }
initial.commit09911bf2008-07-26 23:55:29568}
569
570void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
571 bool proceed) {
572 client->OnBlockingPageComplete(proceed);
573}
574
575// This method runs on the UI loop to access the prefs.
576void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
577 const std::string& wrapped_key) {
578 PrefService* prefs = g_browser_process->local_state();
579 if (prefs) {
580 prefs->SetString(prefs::kSafeBrowsingClientKey, ASCIIToWide(client_key));
581 prefs->SetString(prefs::kSafeBrowsingWrappedKey, ASCIIToWide(wrapped_key));
582 }
583}
584
585void SafeBrowsingService::ChunkInserted() {
[email protected]fbb2b7a2008-11-18 22:54:04586 DCHECK(MessageLoop::current() == db_thread_->message_loop());
initial.commit09911bf2008-07-26 23:55:29587 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
588 this, &SafeBrowsingService::OnChunkInserted));
589}
590
591void SafeBrowsingService::OnChunkInserted() {
592 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04593 if (enabled_)
594 protocol_manager_->OnChunkInserted();
initial.commit09911bf2008-07-26 23:55:29595}
596
[email protected]613a03b2008-10-24 23:02:00597void SafeBrowsingService::DatabaseLoadComplete(bool database_error) {
598 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04599 if (!enabled_)
600 return;
[email protected]613a03b2008-10-24 23:02:00601
602 database_loaded_ = true;
603
604 // TODO(paulg): More robust database initialization error handling.
605 if (protocol_manager_ && !database_error)
606 protocol_manager_->Initialize();
607
608 // If we have any queued requests, we can now check them.
609 if (!resetting_)
610 RunQueuedClients();
611}
612
initial.commit09911bf2008-07-26 23:55:29613// static
[email protected]919d77f02009-01-06 19:48:35614void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
initial.commit09911bf2008-07-26 23:55:29615 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, L"");
616 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, L"");
617}
618
619void SafeBrowsingService::ResetDatabase() {
620 DCHECK(MessageLoop::current() == io_loop_);
621 resetting_ = true;
622 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
623 this, &SafeBrowsingService::OnResetDatabase));
624}
625
626void SafeBrowsingService::OnResetDatabase() {
627 DCHECK(MessageLoop::current() == db_thread_->message_loop());
628 GetDatabase()->ResetDatabase();
629 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
630 this, &SafeBrowsingService::OnResetComplete));
631}
632
633void SafeBrowsingService::OnResetComplete() {
634 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04635 if (enabled_) {
636 resetting_ = false;
637 database_loaded_ = true;
638 RunQueuedClients();
639 }
initial.commit09911bf2008-07-26 23:55:29640}
641
642void SafeBrowsingService::HandleChunk(const std::string& list,
643 std::deque<SBChunk>* chunks) {
644 DCHECK(MessageLoop::current() == io_loop_);
645 DCHECK(enabled_);
646 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
647 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
648}
649
650void SafeBrowsingService::HandleChunkForDatabase(
651 const std::string& list_name,
652 std::deque<SBChunk>* chunks) {
653 DCHECK(MessageLoop::current() == db_thread_->message_loop());
654
655 GetDatabase()->InsertChunks(list_name, chunks);
656}
657
658void SafeBrowsingService::HandleChunkDelete(
659 std::vector<SBChunkDelete>* chunk_deletes) {
660 DCHECK(MessageLoop::current() == io_loop_);
661 DCHECK(enabled_);
662 db_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
663 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
664}
665
666void SafeBrowsingService::DeleteChunks(
667 std::vector<SBChunkDelete>* chunk_deletes) {
668 DCHECK(MessageLoop::current() == db_thread_->message_loop());
669
670 GetDatabase()->DeleteChunks(chunk_deletes);
671}
672
673// Database worker function.
674void SafeBrowsingService::GetAllChunksFromDatabase() {
675 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]a5a37522008-11-11 21:49:56676 bool database_error = true;
initial.commit09911bf2008-07-26 23:55:29677 std::vector<SBListChunkRanges> lists;
[email protected]53ad8572008-11-13 21:50:34678 if (GetDatabase()) {
679 if (GetDatabase()->UpdateStarted()) {
680 GetDatabase()->GetListsInfo(&lists);
681 database_error = false;
682 } else {
683 GetDatabase()->UpdateFinished(false);
684 }
initial.commit09911bf2008-07-26 23:55:29685 }
686
687 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
688 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
689 database_error));
690}
691
692// Called on the io thread with the results of all chunks.
693void SafeBrowsingService::OnGetAllChunksFromDatabase(
694 const std::vector<SBListChunkRanges>& lists, bool database_error) {
695 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04696 if (enabled_)
697 protocol_manager_->OnGetChunksComplete(lists, database_error);
initial.commit09911bf2008-07-26 23:55:29698}
699
700SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
701 const std::string& list_name) {
702 if (safe_browsing_util::IsPhishingList(list_name)) {
703 return URL_PHISHING;
704 }
705
706 if (safe_browsing_util::IsMalwareList(list_name)) {
707 return URL_MALWARE;
708 }
709
710 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
711 return URL_SAFE;
712}
713
initial.commit09911bf2008-07-26 23:55:29714void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
[email protected]fc76de12008-11-14 01:51:18715 if (new_safe_browsing_)
[email protected]553dba62009-02-24 19:08:23716 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
[email protected]fc76de12008-11-14 01:51:18717 else
[email protected]553dba62009-02-24 19:08:23718 UMA_HISTOGRAM_LONG_TIMES("SB.Delay", time);
initial.commit09911bf2008-07-26 23:55:29719}
720
721void SafeBrowsingService::CacheHashResults(
[email protected]200abc32008-09-05 01:44:33722 const std::vector<SBPrefix>& prefixes,
initial.commit09911bf2008-07-26 23:55:29723 const std::vector<SBFullHashResult>& full_hashes) {
724 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]200abc32008-09-05 01:44:33725 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29726}
727
[email protected]0a3076572008-12-13 20:48:36728void SafeBrowsingService::OnSuspend(base::SystemMonitor*) {
initial.commit09911bf2008-07-26 23:55:29729}
730
731// Tell the SafeBrowsing database not to do expensive disk operations for a few
732// minutes after waking up. It's quite likely that the act of resuming from a
733// low power state will involve much disk activity, which we don't want to
734// exacerbate.
[email protected]0a3076572008-12-13 20:48:36735void SafeBrowsingService::OnResume(base::SystemMonitor*) {
initial.commit09911bf2008-07-26 23:55:29736 if (enabled_) {
[email protected]0a3076572008-12-13 20:48:36737 ChromeThread::GetMessageLoop(ChromeThread::DB)->PostTask(FROM_HERE,
738 NewRunnableMethod(this, &SafeBrowsingService::HandleResume));
initial.commit09911bf2008-07-26 23:55:29739 }
740}
741
742void SafeBrowsingService::HandleResume() {
743 DCHECK(MessageLoop::current() == db_thread_->message_loop());
[email protected]53ad8572008-11-13 21:50:34744 // We don't call GetDatabase() here, since we want to avoid unnecessary calls
745 // to Open, Reset, etc, or reload the bloom filter while we're coming out of
746 // a suspended state.
747 if (database_)
748 database_->HandleResume();
license.botbf09a502008-08-24 00:55:55749}
[email protected]613a03b2008-10-24 23:02:00750
751void SafeBrowsingService::RunQueuedClients() {
752 DCHECK(MessageLoop::current() == io_loop_);
[email protected]553dba62009-02-24 19:08:23753 HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
[email protected]613a03b2008-10-24 23:02:00754 while (!queued_checks_.empty()) {
755 QueuedCheck check = queued_checks_.front();
[email protected]553dba62009-02-24 19:08:23756 HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
[email protected]613a03b2008-10-24 23:02:00757 CheckUrl(check.url, check.client);
758 queued_checks_.pop_front();
759 }
760}
761
[email protected]dfdb0de72009-02-19 21:58:14762void SafeBrowsingService::ReportMalware(const GURL& malware_url,
763 const GURL& page_url,
764 const GURL& referrer_url) {
765 DCHECK(MessageLoop::current() == io_loop_);
766
767 // We need to access the database cache on the io_loop_ which is only allowed
768 // in the new SafeBrowsing database system.
769 if (!new_safe_browsing_ || !enabled_ || !database_)
770 return;
771
772 // Check if 'page_url' is already blacklisted (exists in our cache). Only
773 // report if it's not there.
774 std::string list;
775 std::vector<SBPrefix> prefix_hits;
776 std::vector<SBFullHashResult> full_hits;
777 database_->ContainsUrl(page_url, &list, &prefix_hits, &full_hits,
778 protocol_manager_->last_update());
779
780 if (full_hits.empty())
781 protocol_manager_->ReportMalware(malware_url, page_url, referrer_url);
782}