blob: d3d0920a9094b7a3318323f298c96411850e0fbd [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]57c6a652009-05-04 07:58:3422#include "chrome/browser/tab_contents/tab_contents.h"
initial.commit09911bf2008-07-26 23:55:2923#include "chrome/common/chrome_constants.h"
24#include "chrome/common/chrome_paths.h"
initial.commit09911bf2008-07-26 23:55:2925#include "chrome/common/pref_names.h"
26#include "chrome/common/pref_service.h"
[email protected]dcf7d352009-02-26 01:56:0227#include "chrome/common/url_constants.h"
initial.commit09911bf2008-07-26 23:55:2928#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]0a3076572008-12-13 20:48:3641 base::SystemMonitor* monitor = base::SystemMonitor::Get();
42 DCHECK(monitor);
43 if (monitor)
44 monitor->AddObserver(this);
initial.commit09911bf2008-07-26 23:55:2945}
46
47SafeBrowsingService::~SafeBrowsingService() {
[email protected]0a3076572008-12-13 20:48:3648 base::SystemMonitor* monitor = base::SystemMonitor::Get();
49 if (monitor)
50 monitor->RemoveObserver(this);
initial.commit09911bf2008-07-26 23:55:2951}
52
53// Only called on the UI thread.
54void SafeBrowsingService::Initialize(MessageLoop* io_loop) {
55 io_loop_ = io_loop;
56
57 // Get the profile's preference for SafeBrowsing.
[email protected]c870c762009-01-28 05:47:1558 FilePath user_data_dir;
initial.commit09911bf2008-07-26 23:55:2959 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
60 ProfileManager* profile_manager = g_browser_process->profile_manager();
[email protected]f7011fcb2009-01-28 21:54:3261 Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
initial.commit09911bf2008-07-26 23:55:2962 PrefService* pref_service = profile->GetPrefs();
63 if (pref_service->GetBoolean(prefs::kSafeBrowsingEnabled))
64 Start();
65}
66
67// Start up SafeBrowsing objects. This can be called at browser start, or when
68// the user checks the "Enable SafeBrowsing" option in the Advanced options UI.
69void SafeBrowsingService::Start() {
[email protected]f3724ea2009-05-08 22:09:5670 DCHECK(!safe_browsing_thread_.get());
71 safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
72 if (!safe_browsing_thread_->Start())
initial.commit09911bf2008-07-26 23:55:2973 return;
74
initial.commit09911bf2008-07-26 23:55:2975 // Retrieve client MAC keys.
76 PrefService* local_state = g_browser_process->local_state();
77 std::string client_key, wrapped_key;
78 if (local_state) {
79 client_key =
80 WideToASCII(local_state->GetString(prefs::kSafeBrowsingClientKey));
81 wrapped_key =
82 WideToASCII(local_state->GetString(prefs::kSafeBrowsingWrappedKey));
83 }
84
85 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
86 this, &SafeBrowsingService::OnIOInitialize, MessageLoop::current(),
87 client_key, wrapped_key));
[email protected]2c2fb222008-12-17 02:35:4688
[email protected]f3724ea2009-05-08 22:09:5689 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]2c2fb222008-12-17 02:35:4690 this, &SafeBrowsingService::OnDBInitialize));
initial.commit09911bf2008-07-26 23:55:2991}
92
93void SafeBrowsingService::ShutDown() {
94 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
95 this, &SafeBrowsingService::OnIOShutdown));
96}
97
98void SafeBrowsingService::OnIOInitialize(MessageLoop* notify_loop,
99 const std::string& client_key,
100 const std::string& wrapped_key) {
101 DCHECK(MessageLoop::current() == io_loop_);
102 enabled_ = true;
103 protocol_manager_ = new SafeBrowsingProtocolManager(this,
104 notify_loop,
105 client_key,
106 wrapped_key);
[email protected]613a03b2008-10-24 23:02:00107 // We want to initialize the protocol manager only after the database has
108 // loaded, which we'll receive asynchronously (DatabaseLoadComplete). If
109 // database_loaded_ isn't true, we'll wait for that notification to do the
110 // init.
111 if (database_loaded_)
112 protocol_manager_->Initialize();
initial.commit09911bf2008-07-26 23:55:29113}
114
115void SafeBrowsingService::OnDBInitialize() {
[email protected]f3724ea2009-05-08 22:09:56116 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
initial.commit09911bf2008-07-26 23:55:29117 GetDatabase();
118}
119
120void SafeBrowsingService::OnIOShutdown() {
121 DCHECK(MessageLoop::current() == io_loop_);
122 if (!enabled_)
123 return;
124
125 enabled_ = false;
[email protected]fbb2b7a2008-11-18 22:54:04126 resetting_ = false;
initial.commit09911bf2008-07-26 23:55:29127
128 // This cancels all in-flight GetHash requests.
129 delete protocol_manager_;
[email protected]fbb2b7a2008-11-18 22:54:04130 protocol_manager_ = NULL;
initial.commit09911bf2008-07-26 23:55:29131
[email protected]f3724ea2009-05-08 22:09:56132 if (safe_browsing_thread_.get())
133 safe_browsing_thread_->message_loop()->DeleteSoon(FROM_HERE, database_);
initial.commit09911bf2008-07-26 23:55:29134
135 // Flush the database thread. Any in-progress database check results will be
136 // ignored and cleaned up below.
[email protected]f3724ea2009-05-08 22:09:56137 safe_browsing_thread_.reset(NULL);
initial.commit09911bf2008-07-26 23:55:29138
139 database_ = NULL;
[email protected]fbb2b7a2008-11-18 22:54:04140 database_loaded_ = false;
initial.commit09911bf2008-07-26 23:55:29141
[email protected]613a03b2008-10-24 23:02:00142 // Delete queued and pending checks once the database thread is done, calling
143 // back any clients with 'URL_SAFE'.
144 while (!queued_checks_.empty()) {
145 QueuedCheck check = queued_checks_.front();
146 if (check.client)
147 check.client->OnUrlCheckResult(check.url, URL_SAFE);
148 queued_checks_.pop_front();
149 }
150
initial.commit09911bf2008-07-26 23:55:29151 for (CurrentChecks::iterator it = checks_.begin();
152 it != checks_.end(); ++it) {
153 if ((*it)->client)
154 (*it)->client->OnUrlCheckResult((*it)->url, URL_SAFE);
155 delete *it;
156 }
157 checks_.clear();
158
159 gethash_requests_.clear();
160}
161
162// Runs on the UI thread.
163void SafeBrowsingService::OnEnable(bool enabled) {
164 if (enabled)
165 Start();
166 else
167 ShutDown();
168}
169
170bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
[email protected]dcf7d352009-02-26 01:56:02171 return url.SchemeIs(chrome::kHttpScheme) ||
172 url.SchemeIs(chrome::kHttpsScheme);
initial.commit09911bf2008-07-26 23:55:29173}
174
175bool SafeBrowsingService::CheckUrl(const GURL& url, Client* client) {
176 DCHECK(MessageLoop::current() == io_loop_);
initial.commit09911bf2008-07-26 23:55:29177 if (!enabled_ || !database_)
178 return true;
179
[email protected]613a03b2008-10-24 23:02:00180 if (resetting_ || !database_loaded_) {
181 QueuedCheck check;
182 check.client = client;
183 check.url = url;
184 queued_checks_.push_back(check);
185 return false;
186 }
187
188 std::string list;
189 std::vector<SBPrefix> prefix_hits;
190 std::vector<SBFullHashResult> full_hits;
[email protected]22573822008-11-14 00:40:47191 base::Time check_start = base::Time::Now();
[email protected]c3ff89492008-11-11 02:17:51192 bool prefix_match = database_->ContainsUrl(url, &list, &prefix_hits,
193 &full_hits,
[email protected]613a03b2008-10-24 23:02:00194 protocol_manager_->last_update());
[email protected]f0a51fb52009-03-05 12:46:38195
[email protected]553dba62009-02-24 19:08:23196 UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::Time::Now() - check_start);
[email protected]22573822008-11-14 00:40:47197
[email protected]613a03b2008-10-24 23:02:00198 if (!prefix_match)
199 return true; // URL is okay.
200
201 // Needs to be asynchronous, since we could be in the constructor of a
202 // ResourceDispatcherHost event handler which can't pause there.
203 SafeBrowsingCheck* check = new SafeBrowsingCheck();
204 check->url = url;
205 check->client = client;
206 check->result = URL_SAFE;
[email protected]613a03b2008-10-24 23:02:00207 check->need_get_hash = full_hits.empty();
208 check->prefix_hits.swap(prefix_hits);
209 check->full_hits.swap(full_hits);
210 checks_.insert(check);
211
212 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
213 this, &SafeBrowsingService::OnCheckDone, check));
214
initial.commit09911bf2008-07-26 23:55:29215 return false;
216}
217
218void SafeBrowsingService::DisplayBlockingPage(const GURL& url,
219 ResourceType::Type resource_type,
220 UrlCheckResult result,
221 Client* client,
222 MessageLoop* ui_loop,
223 int render_process_host_id,
224 int render_view_id) {
225 // Check if the user has already ignored our warning for this render_view
226 // and domain.
227 for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
228 const WhiteListedEntry& entry = white_listed_entries_[i];
229 if (entry.render_process_host_id == render_process_host_id &&
230 entry.render_view_id == render_view_id &&
231 entry.result == result &&
232 entry.domain ==
[email protected]8ac1a752008-07-31 19:40:37233 net::RegistryControlledDomainService::GetDomainAndRegistry(url)) {
initial.commit09911bf2008-07-26 23:55:29234 MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod(
235 this, &SafeBrowsingService::NotifyClientBlockingComplete,
236 client, true));
237 return;
238 }
239 }
240
[email protected]1b277e72009-01-23 21:05:34241 UnsafeResource resource;
242 resource.url = url;
243 resource.resource_type = resource_type;
244 resource.threat_type= result;
245 resource.client = client;
246 resource.render_process_host_id = render_process_host_id;
247 resource.render_view_id = render_view_id;
[email protected]484c57a2009-03-21 01:24:01248
[email protected]cbab76d2008-10-13 22:42:47249 // The blocking page must be created from the UI thread.
250 ui_loop->PostTask(FROM_HERE, NewRunnableMethod(this,
251 &SafeBrowsingService::DoDisplayBlockingPage,
[email protected]1b277e72009-01-23 21:05:34252 resource));
[email protected]cbab76d2008-10-13 22:42:47253}
254
255// Invoked on the UI thread.
256void SafeBrowsingService::DoDisplayBlockingPage(
[email protected]1b277e72009-01-23 21:05:34257 const UnsafeResource& resource) {
[email protected]a3a1d142008-12-19 00:42:30258 // The tab might have been closed.
[email protected]57c6a652009-05-04 07:58:34259 TabContents* wc =
260 tab_util::GetTabContentsByID(resource.render_process_host_id,
[email protected]dfdb0de72009-02-19 21:58:14261 resource.render_view_id);
262
263 if (!wc) {
[email protected]a3a1d142008-12-19 00:42:30264 // The tab is gone and we did not have a chance at showing the interstitial.
265 // Just act as "Don't Proceed" was chosen.
[email protected]1b277e72009-01-23 21:05:34266 std::vector<UnsafeResource> resources;
267 resources.push_back(resource);
[email protected]bc0caef2009-04-03 17:18:04268 MessageLoop* message_loop;
269 if (g_browser_process->io_thread())
270 message_loop = g_browser_process->io_thread()->message_loop();
271 else // For unit-tests, just post on the current thread.
272 message_loop = MessageLoop::current();
273 message_loop->PostTask(FROM_HERE, NewRunnableMethod(
[email protected]1b277e72009-01-23 21:05:34274 this, &SafeBrowsingService::OnBlockingPageDone, resources, false));
[email protected]a3a1d142008-12-19 00:42:30275 return;
276 }
[email protected]dfdb0de72009-02-19 21:58:14277
278 // Report the malware sub-resource to the SafeBrowsing servers if we have a
279 // malware sub-resource on a safe page and only if the user has opted in to
280 // reporting statistics.
281 PrefService* prefs = g_browser_process->local_state();
282 DCHECK(prefs);
283 if (prefs && prefs->GetBoolean(prefs::kMetricsReportingEnabled) &&
284 resource.resource_type != ResourceType::MAIN_FRAME &&
285 resource.threat_type == SafeBrowsingService::URL_MALWARE) {
286 GURL page_url = wc->GetURL();
287 GURL referrer_url;
[email protected]ce3fa3c2009-04-20 19:55:57288 NavigationEntry* entry = wc->controller().GetActiveEntry();
289 if (entry)
290 referrer_url = entry->referrer();
[email protected]dfdb0de72009-02-19 21:58:14291 io_loop_->PostTask(FROM_HERE,
292 NewRunnableMethod(this,
293 &SafeBrowsingService::ReportMalware,
294 resource.url,
295 page_url,
296 referrer_url));
297 }
298
[email protected]1b277e72009-01-23 21:05:34299 SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
initial.commit09911bf2008-07-26 23:55:29300}
301
302void SafeBrowsingService::CancelCheck(Client* client) {
303 DCHECK(MessageLoop::current() == io_loop_);
304
305 for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
306 if ((*i)->client == client)
307 (*i)->client = NULL;
308 }
[email protected]613a03b2008-10-24 23:02:00309
310 // Scan the queued clients store. Clients may be here if they requested a URL
311 // check before the database has finished loading or resetting.
312 if (!database_loaded_ || resetting_) {
313 std::deque<QueuedCheck>::iterator it = queued_checks_.begin();
314 for (; it != queued_checks_.end(); ++it) {
315 if (it->client == client)
316 it->client = NULL;
317 }
318 }
initial.commit09911bf2008-07-26 23:55:29319}
320
[email protected]613a03b2008-10-24 23:02:00321void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
initial.commit09911bf2008-07-26 23:55:29322 DCHECK(MessageLoop::current() == io_loop_);
323
324 // If we've been shutdown during the database lookup, this check will already
325 // have been deleted (in OnIOShutdown).
[email protected]613a03b2008-10-24 23:02:00326 if (!enabled_ || checks_.find(check) == checks_.end())
initial.commit09911bf2008-07-26 23:55:29327 return;
328
[email protected]613a03b2008-10-24 23:02:00329 if (check->client && check->need_get_hash) {
initial.commit09911bf2008-07-26 23:55:29330 // We have a partial match so we need to query Google for the full hash.
331 // Clean up will happen in HandleGetHashResults.
332
333 // See if we have a GetHash request already in progress for this particular
334 // prefix. If so, we just append ourselves to the list of interested parties
335 // when the results arrive. We only do this for checks involving one prefix,
336 // since that is the common case (multiple prefixes will issue the request
337 // as normal).
[email protected]613a03b2008-10-24 23:02:00338 if (check->prefix_hits.size() == 1) {
339 SBPrefix prefix = check->prefix_hits[0];
initial.commit09911bf2008-07-26 23:55:29340 GetHashRequests::iterator it = gethash_requests_.find(prefix);
341 if (it != gethash_requests_.end()) {
342 // There's already a request in progress.
[email protected]613a03b2008-10-24 23:02:00343 it->second.push_back(check);
initial.commit09911bf2008-07-26 23:55:29344 return;
345 }
346
347 // No request in progress, so we're the first for this prefix.
348 GetHashRequestors requestors;
[email protected]613a03b2008-10-24 23:02:00349 requestors.push_back(check);
initial.commit09911bf2008-07-26 23:55:29350 gethash_requests_[prefix] = requestors;
351 }
352
353 // Reset the start time so that we can measure the network time without the
354 // database time.
[email protected]613a03b2008-10-24 23:02:00355 check->start = Time::Now();
356 protocol_manager_->GetFullHash(check, check->prefix_hits);
initial.commit09911bf2008-07-26 23:55:29357 } else {
358 // We may have cached results for previous GetHash queries.
[email protected]613a03b2008-10-24 23:02:00359 HandleOneCheck(check, check->full_hits);
initial.commit09911bf2008-07-26 23:55:29360 }
361}
362
363SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
[email protected]f3724ea2009-05-08 22:09:56364 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
initial.commit09911bf2008-07-26 23:55:29365 if (database_)
366 return database_;
367
[email protected]c870c762009-01-28 05:47:15368 FilePath path;
initial.commit09911bf2008-07-26 23:55:29369 bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
370 DCHECK(result);
[email protected]c870c762009-01-28 05:47:15371 path = path.Append(chrome::kSafeBrowsingFilename);
initial.commit09911bf2008-07-26 23:55:29372
373 Time before = Time::Now();
[email protected]54d80bb02008-09-20 02:03:08374 SafeBrowsingDatabase* database = SafeBrowsingDatabase::Create();
[email protected]613a03b2008-10-24 23:02:00375 Callback0::Type* chunk_callback =
initial.commit09911bf2008-07-26 23:55:29376 NewCallback(this, &SafeBrowsingService::ChunkInserted);
[email protected]613a03b2008-10-24 23:02:00377 bool init_success = database->Init(path, chunk_callback);
378
379 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
380 this, &SafeBrowsingService::DatabaseLoadComplete, !init_success));
381
382 if (!init_success) {
initial.commit09911bf2008-07-26 23:55:29383 NOTREACHED();
384 return NULL;
385 }
386
387 database_ = database;
388
389 TimeDelta open_time = Time::Now() - before;
390 SB_DLOG(INFO) << "SafeBrowsing database open took " <<
391 open_time.InMilliseconds() << " ms.";
392
393 return database_;
394}
395
396// Public API called only on the IO thread.
397// The SafeBrowsingProtocolManager has received the full hash results for
398// prefix hits detected in the database.
399void SafeBrowsingService::HandleGetHashResults(
400 SafeBrowsingCheck* check,
[email protected]200abc32008-09-05 01:44:33401 const std::vector<SBFullHashResult>& full_hashes,
402 bool can_cache) {
initial.commit09911bf2008-07-26 23:55:29403 if (checks_.find(check) == checks_.end())
404 return;
405
406 DCHECK(enabled_);
407
[email protected]484c57a2009-03-21 01:24:01408 UMA_HISTOGRAM_LONG_TIMES("SB2.Network", Time::Now() - check->start);
[email protected]22573822008-11-14 00:40:47409
[email protected]200abc32008-09-05 01:44:33410 std::vector<SBPrefix> prefixes = check->prefix_hits;
initial.commit09911bf2008-07-26 23:55:29411 OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
412
[email protected]484c57a2009-03-21 01:24:01413 if (can_cache && database_) {
414 // Cache the GetHash results in memory:
415 database_->CacheHashResults(prefixes, full_hashes);
[email protected]613a03b2008-10-24 23:02:00416 }
initial.commit09911bf2008-07-26 23:55:29417}
418
419void SafeBrowsingService::OnHandleGetHashResults(
420 SafeBrowsingCheck* check,
421 const std::vector<SBFullHashResult>& full_hashes) {
422 SBPrefix prefix = check->prefix_hits[0];
423 GetHashRequests::iterator it = gethash_requests_.find(prefix);
424 if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
425 HandleOneCheck(check, full_hashes);
426 return;
427 }
428
429 // Call back all interested parties.
430 GetHashRequestors& requestors = it->second;
431 for (GetHashRequestors::iterator r = requestors.begin();
432 r != requestors.end(); ++r) {
433 HandleOneCheck(*r, full_hashes);
434 }
435
436 gethash_requests_.erase(it);
437}
438
439void SafeBrowsingService::HandleOneCheck(
440 SafeBrowsingCheck* check,
441 const std::vector<SBFullHashResult>& full_hashes) {
442 if (check->client) {
443 UrlCheckResult result = URL_SAFE;
444 int index = safe_browsing_util::CompareFullHashes(check->url, full_hashes);
[email protected]09d985f2009-05-11 21:59:58445 if (index != -1) {
initial.commit09911bf2008-07-26 23:55:29446 result = GetResultFromListname(full_hashes[index].list_name);
[email protected]09d985f2009-05-11 21:59:58447 } else {
448 // Log the case where the SafeBrowsing servers return full hashes in the
449 // GetHash response that match the prefix we're looking up, but don't
450 // match the full hash of the URL.
451 if (!full_hashes.empty())
452 UMA_HISTOGRAM_COUNTS("SB2.GetHashServerMiss", 1);
453 }
initial.commit09911bf2008-07-26 23:55:29454
455 // Let the client continue handling the original request.
456 check->client->OnUrlCheckResult(check->url, result);
457 }
458
459 checks_.erase(check);
460 delete check;
461}
462
[email protected]57119c3f2008-12-04 00:33:04463void SafeBrowsingService::UpdateStarted() {
initial.commit09911bf2008-07-26 23:55:29464 DCHECK(MessageLoop::current() == io_loop_);
465 DCHECK(enabled_);
[email protected]57119c3f2008-12-04 00:33:04466 DCHECK(!update_in_progress_);
467 update_in_progress_ = true;
[email protected]f3724ea2009-05-08 22:09:56468 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
initial.commit09911bf2008-07-26 23:55:29469 this, &SafeBrowsingService::GetAllChunksFromDatabase));
470}
471
[email protected]613a03b2008-10-24 23:02:00472void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
[email protected]aad08752008-10-02 22:13:41473 DCHECK(MessageLoop::current() == io_loop_);
474 DCHECK(enabled_);
[email protected]57119c3f2008-12-04 00:33:04475 if (update_in_progress_) {
476 update_in_progress_ = false;
[email protected]f3724ea2009-05-08 22:09:56477 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
478 NewRunnableMethod(this,
479 &SafeBrowsingService::DatabaseUpdateFinished,
480 update_succeeded));
[email protected]57119c3f2008-12-04 00:33:04481 }
[email protected]aad08752008-10-02 22:13:41482}
483
[email protected]613a03b2008-10-24 23:02:00484void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
[email protected]f3724ea2009-05-08 22:09:56485 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]aad08752008-10-02 22:13:41486 if (GetDatabase())
[email protected]613a03b2008-10-24 23:02:00487 GetDatabase()->UpdateFinished(update_succeeded);
[email protected]aad08752008-10-02 22:13:41488}
489
[email protected]1b277e72009-01-23 21:05:34490void SafeBrowsingService::OnBlockingPageDone(
491 const std::vector<UnsafeResource>& resources,
492 bool proceed) {
493 for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
494 iter != resources.end(); ++iter) {
495 const UnsafeResource& resource = *iter;
496 NotifyClientBlockingComplete(resource.client, proceed);
initial.commit09911bf2008-07-26 23:55:29497
[email protected]1b277e72009-01-23 21:05:34498 if (proceed) {
499 // Whitelist this domain and warning type for the given tab.
500 WhiteListedEntry entry;
501 entry.render_process_host_id = resource.render_process_host_id;
502 entry.render_view_id = resource.render_view_id;
503 entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
504 resource.url);
505 entry.result = resource.threat_type;
506 white_listed_entries_.push_back(entry);
507 }
initial.commit09911bf2008-07-26 23:55:29508 }
initial.commit09911bf2008-07-26 23:55:29509}
510
511void SafeBrowsingService::NotifyClientBlockingComplete(Client* client,
512 bool proceed) {
513 client->OnBlockingPageComplete(proceed);
514}
515
516// This method runs on the UI loop to access the prefs.
517void SafeBrowsingService::OnNewMacKeys(const std::string& client_key,
518 const std::string& wrapped_key) {
519 PrefService* prefs = g_browser_process->local_state();
520 if (prefs) {
521 prefs->SetString(prefs::kSafeBrowsingClientKey, ASCIIToWide(client_key));
522 prefs->SetString(prefs::kSafeBrowsingWrappedKey, ASCIIToWide(wrapped_key));
523 }
524}
525
526void SafeBrowsingService::ChunkInserted() {
[email protected]f3724ea2009-05-08 22:09:56527 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
initial.commit09911bf2008-07-26 23:55:29528 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
529 this, &SafeBrowsingService::OnChunkInserted));
530}
531
532void SafeBrowsingService::OnChunkInserted() {
533 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04534 if (enabled_)
535 protocol_manager_->OnChunkInserted();
initial.commit09911bf2008-07-26 23:55:29536}
537
[email protected]613a03b2008-10-24 23:02:00538void SafeBrowsingService::DatabaseLoadComplete(bool database_error) {
539 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04540 if (!enabled_)
541 return;
[email protected]613a03b2008-10-24 23:02:00542
543 database_loaded_ = true;
544
545 // TODO(paulg): More robust database initialization error handling.
546 if (protocol_manager_ && !database_error)
547 protocol_manager_->Initialize();
548
549 // If we have any queued requests, we can now check them.
550 if (!resetting_)
551 RunQueuedClients();
552}
553
initial.commit09911bf2008-07-26 23:55:29554// static
[email protected]919d77f02009-01-06 19:48:35555void SafeBrowsingService::RegisterPrefs(PrefService* prefs) {
initial.commit09911bf2008-07-26 23:55:29556 prefs->RegisterStringPref(prefs::kSafeBrowsingClientKey, L"");
557 prefs->RegisterStringPref(prefs::kSafeBrowsingWrappedKey, L"");
558}
559
560void SafeBrowsingService::ResetDatabase() {
561 DCHECK(MessageLoop::current() == io_loop_);
562 resetting_ = true;
[email protected]f3724ea2009-05-08 22:09:56563 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
initial.commit09911bf2008-07-26 23:55:29564 this, &SafeBrowsingService::OnResetDatabase));
565}
566
567void SafeBrowsingService::OnResetDatabase() {
[email protected]f3724ea2009-05-08 22:09:56568 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
initial.commit09911bf2008-07-26 23:55:29569 GetDatabase()->ResetDatabase();
570 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
571 this, &SafeBrowsingService::OnResetComplete));
572}
573
574void SafeBrowsingService::OnResetComplete() {
575 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04576 if (enabled_) {
577 resetting_ = false;
578 database_loaded_ = true;
579 RunQueuedClients();
580 }
initial.commit09911bf2008-07-26 23:55:29581}
582
583void SafeBrowsingService::HandleChunk(const std::string& list,
584 std::deque<SBChunk>* chunks) {
585 DCHECK(MessageLoop::current() == io_loop_);
586 DCHECK(enabled_);
[email protected]f3724ea2009-05-08 22:09:56587 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
initial.commit09911bf2008-07-26 23:55:29588 this, &SafeBrowsingService::HandleChunkForDatabase, list, chunks));
589}
590
591void SafeBrowsingService::HandleChunkForDatabase(
592 const std::string& list_name,
593 std::deque<SBChunk>* chunks) {
[email protected]f3724ea2009-05-08 22:09:56594 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
initial.commit09911bf2008-07-26 23:55:29595
596 GetDatabase()->InsertChunks(list_name, chunks);
597}
598
599void SafeBrowsingService::HandleChunkDelete(
600 std::vector<SBChunkDelete>* chunk_deletes) {
601 DCHECK(MessageLoop::current() == io_loop_);
602 DCHECK(enabled_);
[email protected]f3724ea2009-05-08 22:09:56603 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
initial.commit09911bf2008-07-26 23:55:29604 this, &SafeBrowsingService::DeleteChunks, chunk_deletes));
605}
606
607void SafeBrowsingService::DeleteChunks(
608 std::vector<SBChunkDelete>* chunk_deletes) {
[email protected]f3724ea2009-05-08 22:09:56609 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
initial.commit09911bf2008-07-26 23:55:29610
611 GetDatabase()->DeleteChunks(chunk_deletes);
612}
613
614// Database worker function.
615void SafeBrowsingService::GetAllChunksFromDatabase() {
[email protected]f3724ea2009-05-08 22:09:56616 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]a5a37522008-11-11 21:49:56617 bool database_error = true;
initial.commit09911bf2008-07-26 23:55:29618 std::vector<SBListChunkRanges> lists;
[email protected]53ad8572008-11-13 21:50:34619 if (GetDatabase()) {
620 if (GetDatabase()->UpdateStarted()) {
621 GetDatabase()->GetListsInfo(&lists);
622 database_error = false;
623 } else {
624 GetDatabase()->UpdateFinished(false);
625 }
initial.commit09911bf2008-07-26 23:55:29626 }
627
628 io_loop_->PostTask(FROM_HERE, NewRunnableMethod(
629 this, &SafeBrowsingService::OnGetAllChunksFromDatabase, lists,
630 database_error));
631}
632
633// Called on the io thread with the results of all chunks.
634void SafeBrowsingService::OnGetAllChunksFromDatabase(
635 const std::vector<SBListChunkRanges>& lists, bool database_error) {
636 DCHECK(MessageLoop::current() == io_loop_);
[email protected]fbb2b7a2008-11-18 22:54:04637 if (enabled_)
638 protocol_manager_->OnGetChunksComplete(lists, database_error);
initial.commit09911bf2008-07-26 23:55:29639}
640
641SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
642 const std::string& list_name) {
643 if (safe_browsing_util::IsPhishingList(list_name)) {
644 return URL_PHISHING;
645 }
646
647 if (safe_browsing_util::IsMalwareList(list_name)) {
648 return URL_MALWARE;
649 }
650
651 SB_DLOG(INFO) << "Unknown safe browsing list " << list_name;
652 return URL_SAFE;
653}
654
initial.commit09911bf2008-07-26 23:55:29655void SafeBrowsingService::LogPauseDelay(TimeDelta time) {
[email protected]484c57a2009-03-21 01:24:01656 UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
initial.commit09911bf2008-07-26 23:55:29657}
658
659void SafeBrowsingService::CacheHashResults(
[email protected]200abc32008-09-05 01:44:33660 const std::vector<SBPrefix>& prefixes,
initial.commit09911bf2008-07-26 23:55:29661 const std::vector<SBFullHashResult>& full_hashes) {
[email protected]f3724ea2009-05-08 22:09:56662 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]200abc32008-09-05 01:44:33663 GetDatabase()->CacheHashResults(prefixes, full_hashes);
initial.commit09911bf2008-07-26 23:55:29664}
665
[email protected]0a3076572008-12-13 20:48:36666void SafeBrowsingService::OnSuspend(base::SystemMonitor*) {
initial.commit09911bf2008-07-26 23:55:29667}
668
669// Tell the SafeBrowsing database not to do expensive disk operations for a few
670// minutes after waking up. It's quite likely that the act of resuming from a
671// low power state will involve much disk activity, which we don't want to
672// exacerbate.
[email protected]0a3076572008-12-13 20:48:36673void SafeBrowsingService::OnResume(base::SystemMonitor*) {
initial.commit09911bf2008-07-26 23:55:29674 if (enabled_) {
[email protected]f3724ea2009-05-08 22:09:56675 safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
676 NewRunnableMethod(this, &SafeBrowsingService::HandleResume));
initial.commit09911bf2008-07-26 23:55:29677 }
678}
679
680void SafeBrowsingService::HandleResume() {
[email protected]f3724ea2009-05-08 22:09:56681 DCHECK(MessageLoop::current() == safe_browsing_thread_->message_loop());
[email protected]53ad8572008-11-13 21:50:34682 // We don't call GetDatabase() here, since we want to avoid unnecessary calls
683 // to Open, Reset, etc, or reload the bloom filter while we're coming out of
684 // a suspended state.
685 if (database_)
686 database_->HandleResume();
license.botbf09a502008-08-24 00:55:55687}
[email protected]613a03b2008-10-24 23:02:00688
689void SafeBrowsingService::RunQueuedClients() {
690 DCHECK(MessageLoop::current() == io_loop_);
[email protected]553dba62009-02-24 19:08:23691 HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
[email protected]613a03b2008-10-24 23:02:00692 while (!queued_checks_.empty()) {
693 QueuedCheck check = queued_checks_.front();
[email protected]553dba62009-02-24 19:08:23694 HISTOGRAM_TIMES("SB.QueueDelay", Time::Now() - check.start);
[email protected]613a03b2008-10-24 23:02:00695 CheckUrl(check.url, check.client);
696 queued_checks_.pop_front();
697 }
698}
699
[email protected]dfdb0de72009-02-19 21:58:14700void SafeBrowsingService::ReportMalware(const GURL& malware_url,
701 const GURL& page_url,
702 const GURL& referrer_url) {
703 DCHECK(MessageLoop::current() == io_loop_);
704
[email protected]484c57a2009-03-21 01:24:01705 if (!enabled_ || !database_)
[email protected]dfdb0de72009-02-19 21:58:14706 return;
707
708 // Check if 'page_url' is already blacklisted (exists in our cache). Only
709 // report if it's not there.
710 std::string list;
711 std::vector<SBPrefix> prefix_hits;
712 std::vector<SBFullHashResult> full_hits;
713 database_->ContainsUrl(page_url, &list, &prefix_hits, &full_hits,
714 protocol_manager_->last_update());
715
716 if (full_hits.empty())
717 protocol_manager_->ReportMalware(malware_url, page_url, referrer_url);
718}