blob: 9dce147f8f5063965143d8e84ec1db517b78888a [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/safe_browsing/safe_browsing_service.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/debug/leak_tracker.h"
#include "base/lazy_instance.h"
#include "base/path_service.h"
#include "base/stl_util.h"
#include "base/string_util.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/api/prefs/pref_change_registrar.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/metrics/metrics_service.h"
#include "chrome/browser/net/sqlite_persistent_cookie_store.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/safe_browsing/client_side_detection_service.h"
#include "chrome/browser/safe_browsing/download_protection_service.h"
#include "chrome/browser/safe_browsing/malware_details.h"
#include "chrome/browser/safe_browsing/protocol_manager.h"
#include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
#include "chrome/browser/safe_browsing/safe_browsing_database.h"
#include "chrome/browser/tab_contents/tab_util.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_client.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "net/cookies/cookie_monster.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#if defined(OS_WIN)
#include "chrome/installer/util/browser_distribution.h"
#endif
using content::BrowserThread;
using content::NavigationEntry;
using content::WebContents;
namespace {
// Filename suffix for the cookie database.
const FilePath::CharType kCookiesFile[] = FILE_PATH_LITERAL(" Cookies");
// The default URL prefix where browser fetches chunk updates, hashes,
// and reports safe browsing hits and malware details.
const char* const kSbDefaultURLPrefix =
"https://safebrowsing.google.com/safebrowsing";
// When download url check takes this long, client's callback will be called
// without waiting for the result.
const int64 kDownloadUrlCheckTimeoutMs = 10000;
// Similar to kDownloadUrlCheckTimeoutMs, but for download hash checks.
const int64 kDownloadHashCheckTimeoutMs = 10000;
// Records disposition information about the check. |hit| should be
// |true| if there were any prefix hits in |full_hashes|.
void RecordGetHashCheckStatus(
bool hit,
bool is_download,
const std::vector<SBFullHashResult>& full_hashes) {
SafeBrowsingProtocolManager::ResultType result;
if (full_hashes.empty()) {
result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_EMPTY;
} else if (hit) {
result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_HIT;
} else {
result = SafeBrowsingProtocolManager::GET_HASH_FULL_HASH_MISS;
}
SafeBrowsingProtocolManager::RecordGetHashResult(is_download, result);
}
FilePath BaseFilename() {
FilePath path;
bool result = PathService::Get(chrome::DIR_USER_DATA, &path);
DCHECK(result);
return path.Append(chrome::kSafeBrowsingBaseFilename);
}
FilePath CookieFilePath() {
return FilePath(BaseFilename().value() + kCookiesFile);
}
} // namespace
// Custom URLRequestContext used by SafeBrowsing requests, which are not
// associated with a particular profile. We need to use a subclass of
// URLRequestContext in order to provide the correct User-Agent.
class SafeBrowsingURLRequestContext : public net::URLRequestContext {
public:
virtual const std::string& GetUserAgent(
const GURL& url) const OVERRIDE {
return content::GetUserAgent(url);
}
private:
virtual ~SafeBrowsingURLRequestContext() {}
base::debug::LeakTracker<SafeBrowsingURLRequestContext> leak_tracker_;
};
class SafeBrowsingURLRequestContextGetter
: public net::URLRequestContextGetter {
public:
explicit SafeBrowsingURLRequestContextGetter(
SafeBrowsingService* sb_service_);
// Implementation for net::UrlRequestContextGetter.
virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE;
virtual scoped_refptr<base::SingleThreadTaskRunner>
GetNetworkTaskRunner() const OVERRIDE;
protected:
virtual ~SafeBrowsingURLRequestContextGetter();
private:
SafeBrowsingService* const sb_service_; // Owned by BrowserProcess.
scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_;
base::debug::LeakTracker<SafeBrowsingURLRequestContextGetter> leak_tracker_;
};
SafeBrowsingURLRequestContextGetter::SafeBrowsingURLRequestContextGetter(
SafeBrowsingService* sb_service)
: sb_service_(sb_service),
network_task_runner_(
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)) {
}
SafeBrowsingURLRequestContextGetter::~SafeBrowsingURLRequestContextGetter() {}
net::URLRequestContext*
SafeBrowsingURLRequestContextGetter::GetURLRequestContext() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(sb_service_->url_request_context_.get());
return sb_service_->url_request_context_.get();
}
scoped_refptr<base::SingleThreadTaskRunner>
SafeBrowsingURLRequestContextGetter::GetNetworkTaskRunner() const {
return network_task_runner_;
}
// static
SafeBrowsingServiceFactory* SafeBrowsingService::factory_ = NULL;
// The default SafeBrowsingServiceFactory. Global, made a singleton so we
// don't leak it.
class SafeBrowsingServiceFactoryImpl : public SafeBrowsingServiceFactory {
public:
virtual SafeBrowsingService* CreateSafeBrowsingService() {
return new SafeBrowsingService();
}
private:
friend struct base::DefaultLazyInstanceTraits<SafeBrowsingServiceFactoryImpl>;
SafeBrowsingServiceFactoryImpl() { }
DISALLOW_COPY_AND_ASSIGN(SafeBrowsingServiceFactoryImpl);
};
static base::LazyInstance<SafeBrowsingServiceFactoryImpl>
g_safe_browsing_service_factory_impl = LAZY_INSTANCE_INITIALIZER;
struct SafeBrowsingService::WhiteListedEntry {
int render_process_host_id;
int render_view_id;
std::string domain;
UrlCheckResult result;
};
SafeBrowsingService::UnsafeResource::UnsafeResource()
: is_subresource(false),
threat_type(SAFE),
render_process_host_id(-1),
render_view_id(-1) {
}
SafeBrowsingService::UnsafeResource::~UnsafeResource() {}
SafeBrowsingService::SafeBrowsingCheck::SafeBrowsingCheck()
: full_hash(NULL),
client(NULL),
need_get_hash(false),
result(SAFE),
is_download(false),
timeout_factory_(NULL) {
}
SafeBrowsingService::SafeBrowsingCheck::~SafeBrowsingCheck() {}
void SafeBrowsingService::Client::OnSafeBrowsingResult(
const SafeBrowsingCheck& check) {
if (!check.urls.empty()) {
DCHECK(!check.full_hash.get());
if (!check.is_download) {
DCHECK_EQ(1U, check.urls.size());
OnBrowseUrlCheckResult(check.urls[0], check.result);
} else {
OnDownloadUrlCheckResult(check.urls, check.result);
}
} else if (check.full_hash.get()) {
OnDownloadHashCheckResult(
safe_browsing_util::SBFullHashToString(*check.full_hash),
check.result);
} else {
NOTREACHED();
}
}
// static
FilePath SafeBrowsingService::GetCookieFilePathForTesting() {
return CookieFilePath();
}
// static
SafeBrowsingService* SafeBrowsingService::CreateSafeBrowsingService() {
if (!factory_)
factory_ = g_safe_browsing_service_factory_impl.Pointer();
return factory_->CreateSafeBrowsingService();
}
SafeBrowsingService::SafeBrowsingService()
: database_(NULL),
protocol_manager_(NULL),
enabled_(false),
enable_download_protection_(false),
enable_csd_whitelist_(false),
enable_download_whitelist_(false),
update_in_progress_(false),
database_update_in_progress_(false),
closing_database_(false),
download_urlcheck_timeout_ms_(kDownloadUrlCheckTimeoutMs),
download_hashcheck_timeout_ms_(kDownloadHashCheckTimeoutMs) {
}
SafeBrowsingService::~SafeBrowsingService() {
// Deletes the PrefChangeRegistrars, whose dtors also unregister |this| as an
// observer of the preferences.
STLDeleteValues(&prefs_map_);
// We should have already been shut down. If we're still enabled, then the
// database isn't going to be closed properly, which could lead to corruption.
DCHECK(!enabled_);
}
void SafeBrowsingService::Initialize() {
url_request_context_getter_ =
new SafeBrowsingURLRequestContextGetter(this);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(
&SafeBrowsingService::InitURLRequestContextOnIOThread, this,
make_scoped_refptr(g_browser_process->system_request_context())));
if (!CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableClientSidePhishingDetection)) {
csd_service_.reset(
safe_browsing::ClientSideDetectionService::Create(
url_request_context_getter_));
}
download_service_.reset(new safe_browsing::DownloadProtectionService(
this,
url_request_context_getter_));
// Track the safe browsing preference of existing profiles.
// The SafeBrowsingService will be started if any existing profile has the
// preference enabled. It will also listen for updates to the preferences.
ProfileManager* profile_manager = g_browser_process->profile_manager();
if (profile_manager) {
std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles();
for (size_t i = 0; i < profiles.size(); ++i) {
if (profiles[i]->IsOffTheRecord())
continue;
AddPrefService(profiles[i]->GetPrefs());
}
}
// Track profile creation and destruction.
prefs_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CREATED,
content::NotificationService::AllSources());
prefs_registrar_.Add(this, chrome::NOTIFICATION_PROFILE_DESTROYED,
content::NotificationService::AllSources());
}
void SafeBrowsingService::ShutDown() {
Stop();
// The IO thread is going away, so make sure the ClientSideDetectionService
// dtor executes now since it may call the dtor of URLFetcher which relies
// on it.
csd_service_.reset();
download_service_.reset();
url_request_context_getter_ = NULL;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&SafeBrowsingService::DestroyURLRequestContextOnIOThread,
this));
}
bool SafeBrowsingService::CanCheckUrl(const GURL& url) const {
return url.SchemeIs(chrome::kFtpScheme) ||
url.SchemeIs(chrome::kHttpScheme) ||
url.SchemeIs(chrome::kHttpsScheme);
}
// Only report SafeBrowsing related stats when UMA is enabled. User must also
// ensure that safe browsing is enabled from the calling profile.
bool SafeBrowsingService::CanReportStats() const {
const MetricsService* metrics = g_browser_process->metrics_service();
return metrics && metrics->reporting_active();
}
// Binhash verification is only enabled for UMA users for now.
bool SafeBrowsingService::DownloadBinHashNeeded() const {
return (enable_download_protection_ && CanReportStats()) ||
(download_protection_service() &&
download_protection_service()->enabled());
}
bool SafeBrowsingService::CheckDownloadUrl(const std::vector<GURL>& url_chain,
Client* client) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!enabled_ || !enable_download_protection_)
return true;
// We need to check the database for url prefix, and later may fetch the url
// from the safebrowsing backends. These need to be asynchronous.
SafeBrowsingCheck* check = new SafeBrowsingCheck();
check->urls = url_chain;
StartDownloadCheck(
check,
client,
base::Bind(&SafeBrowsingService::CheckDownloadUrlOnSBThread, this, check),
download_urlcheck_timeout_ms_);
return false;
}
bool SafeBrowsingService::CheckDownloadHash(const std::string& full_hash,
Client* client) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(!full_hash.empty());
if (!enabled_ || !enable_download_protection_ || full_hash.empty())
return true;
// We need to check the database for url prefix, and later may fetch the url
// from the safebrowsing backends. These need to be asynchronous.
SafeBrowsingCheck* check = new SafeBrowsingCheck();
check->full_hash.reset(new SBFullHash);
safe_browsing_util::StringToSBFullHash(full_hash, check->full_hash.get());
StartDownloadCheck(
check,
client,
base::Bind(&SafeBrowsingService::CheckDownloadHashOnSBThread,this, check),
download_hashcheck_timeout_ms_);
return false;
}
bool SafeBrowsingService::MatchCsdWhitelistUrl(const GURL& url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!enabled_ || !enable_csd_whitelist_ || !MakeDatabaseAvailable()) {
// There is something funky going on here -- for example, perhaps the user
// has not restarted since enabling metrics reporting, so we haven't
// enabled the csd whitelist yet. Just to be safe we return true in this
// case.
return true;
}
return database_->ContainsCsdWhitelistedUrl(url);
}
bool SafeBrowsingService::MatchDownloadWhitelistUrl(const GURL& url) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!enabled_ || !enable_download_whitelist_ || !MakeDatabaseAvailable()) {
return true;
}
return database_->ContainsDownloadWhitelistedUrl(url);
}
bool SafeBrowsingService::MatchDownloadWhitelistString(
const std::string& str) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!enabled_ || !enable_download_whitelist_ || !MakeDatabaseAvailable()) {
return true;
}
return database_->ContainsDownloadWhitelistedString(str);
}
bool SafeBrowsingService::CheckBrowseUrl(const GURL& url,
Client* client) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!enabled_)
return true;
if (!CanCheckUrl(url))
return true;
const base::TimeTicks start = base::TimeTicks::Now();
if (!MakeDatabaseAvailable()) {
QueuedCheck check;
check.client = client;
check.url = url;
check.start = start;
queued_checks_.push_back(check);
return false;
}
std::string list;
std::vector<SBPrefix> prefix_hits;
std::vector<SBFullHashResult> full_hits;
bool prefix_match =
database_->ContainsBrowseUrl(url, &list, &prefix_hits,
&full_hits,
protocol_manager_->last_update());
UMA_HISTOGRAM_TIMES("SB2.FilterCheck", base::TimeTicks::Now() - start);
if (!prefix_match)
return true; // URL is okay.
// Needs to be asynchronous, since we could be in the constructor of a
// ResourceDispatcherHost event handler which can't pause there.
SafeBrowsingCheck* check = new SafeBrowsingCheck();
check->urls.push_back(url);
check->client = client;
check->result = SAFE;
check->is_download = false;
check->need_get_hash = full_hits.empty();
check->prefix_hits.swap(prefix_hits);
check->full_hits.swap(full_hits);
checks_.insert(check);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&SafeBrowsingService::OnCheckDone, this, check));
return false;
}
void SafeBrowsingService::CancelCheck(Client* client) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
for (CurrentChecks::iterator i = checks_.begin(); i != checks_.end(); ++i) {
// We can't delete matching checks here because the db thread has a copy of
// the pointer. Instead, we simply NULL out the client, and when the db
// thread calls us back, we'll clean up the check.
if ((*i)->client == client)
(*i)->client = NULL;
}
// Scan the queued clients store. Clients may be here if they requested a URL
// check before the database has finished loading.
for (std::deque<QueuedCheck>::iterator it(queued_checks_.begin());
it != queued_checks_.end(); ) {
// In this case it's safe to delete matches entirely since nothing has a
// pointer to them.
if (it->client == client)
it = queued_checks_.erase(it);
else
++it;
}
}
void SafeBrowsingService::DisplayBlockingPage(
const GURL& url,
const GURL& original_url,
const std::vector<GURL>& redirect_urls,
bool is_subresource,
UrlCheckResult result,
const UrlCheckCallback& callback,
int render_process_host_id,
int render_view_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
UnsafeResource resource;
resource.url = url;
resource.original_url = original_url;
resource.redirect_urls = redirect_urls;
resource.is_subresource = is_subresource;
resource.threat_type= result;
resource.callback = callback;
resource.render_process_host_id = render_process_host_id;
resource.render_view_id = render_view_id;
// The blocking page must be created from the UI thread.
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&SafeBrowsingService::DoDisplayBlockingPage, this, resource));
}
void SafeBrowsingService::HandleGetHashResults(
SafeBrowsingCheck* check,
const std::vector<SBFullHashResult>& full_hashes,
bool can_cache) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!enabled_)
return;
// If the service has been shut down, |check| should have been deleted.
DCHECK(checks_.find(check) != checks_.end());
// |start| is set before calling |GetFullHash()|, which should be
// the only path which gets to here.
DCHECK(!check->start.is_null());
UMA_HISTOGRAM_LONG_TIMES("SB2.Network",
base::TimeTicks::Now() - check->start);
std::vector<SBPrefix> prefixes = check->prefix_hits;
OnHandleGetHashResults(check, full_hashes); // 'check' is deleted here.
if (can_cache && MakeDatabaseAvailable()) {
// Cache the GetHash results in memory:
database_->CacheHashResults(prefixes, full_hashes);
}
}
void SafeBrowsingService::HandleChunk(const std::string& list,
SBChunkList* chunks) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(enabled_);
safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, base::Bind(
&SafeBrowsingService::HandleChunkForDatabase, this, list, chunks));
}
void SafeBrowsingService::HandleChunkDelete(
std::vector<SBChunkDelete>* chunk_deletes) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(enabled_);
safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, base::Bind(
&SafeBrowsingService::DeleteChunks, this, chunk_deletes));
}
void SafeBrowsingService::UpdateStarted() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(enabled_);
DCHECK(!update_in_progress_);
update_in_progress_ = true;
safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, base::Bind(
&SafeBrowsingService::GetAllChunksFromDatabase, this));
}
void SafeBrowsingService::UpdateFinished(bool update_succeeded) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(enabled_);
if (update_in_progress_) {
update_in_progress_ = false;
safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
base::Bind(&SafeBrowsingService::DatabaseUpdateFinished,
this, update_succeeded));
}
}
bool SafeBrowsingService::IsUpdateInProgress() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
return update_in_progress_;
}
void SafeBrowsingService::OnBlockingPageDone(
const std::vector<UnsafeResource>& resources,
bool proceed) {
for (std::vector<UnsafeResource>::const_iterator iter = resources.begin();
iter != resources.end(); ++iter) {
const UnsafeResource& resource = *iter;
if (!resource.callback.is_null())
resource.callback.Run(proceed);
if (proceed) {
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&SafeBrowsingService::UpdateWhitelist, this, resource));
}
}
}
net::URLRequestContextGetter* SafeBrowsingService::url_request_context() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return url_request_context_getter_.get();
}
void SafeBrowsingService::ResetDatabase() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(enabled_);
safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, base::Bind(
&SafeBrowsingService::OnResetDatabase, this));
}
void SafeBrowsingService::PurgeMemory() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
CloseDatabase();
}
void SafeBrowsingService::LogPauseDelay(base::TimeDelta time) {
UMA_HISTOGRAM_LONG_TIMES("SB2.Delay", time);
}
void SafeBrowsingService::InitURLRequestContextOnIOThread(
net::URLRequestContextGetter* system_url_request_context_getter) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(!url_request_context_.get());
scoped_refptr<net::CookieStore> cookie_store = new net::CookieMonster(
new SQLitePersistentCookieStore(CookieFilePath(), false, NULL),
NULL);
url_request_context_.reset(new SafeBrowsingURLRequestContext);
// |system_url_request_context_getter| may be NULL during tests.
if (system_url_request_context_getter)
url_request_context_->CopyFrom(
system_url_request_context_getter->GetURLRequestContext());
url_request_context_->set_cookie_store(cookie_store);
}
void SafeBrowsingService::DestroyURLRequestContextOnIOThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
url_request_context_->AssertNoURLRequests();
// Need to do the CheckForLeaks on IOThread instead of in ShutDown where
// url_request_context_getter_ is cleared, since the URLRequestContextGetter
// will PostTask to IOTread to delete itself.
using base::debug::LeakTracker;
LeakTracker<SafeBrowsingURLRequestContextGetter>::CheckForLeaks();
url_request_context_.reset();
}
void SafeBrowsingService::StartOnIOThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (enabled_)
return;
DCHECK(!safe_browsing_thread_.get());
safe_browsing_thread_.reset(new base::Thread("Chrome_SafeBrowsingThread"));
if (!safe_browsing_thread_->Start())
return;
enabled_ = true;
registrar_.reset(new content::NotificationRegistrar);
MakeDatabaseAvailable();
// On Windows, get the safe browsing client name from the browser
// distribution classes in installer util. These classes don't yet have
// an analog on non-Windows builds so just keep the name specified here.
#if defined(OS_WIN)
BrowserDistribution* dist = BrowserDistribution::GetDistribution();
std::string client_name(dist->GetSafeBrowsingName());
#else
#if defined(GOOGLE_CHROME_BUILD)
std::string client_name("googlechrome");
#else
std::string client_name("chromium");
#endif
#endif
CommandLine* cmdline = CommandLine::ForCurrentProcess();
bool disable_auto_update =
cmdline->HasSwitch(switches::kSbDisableAutoUpdate) ||
cmdline->HasSwitch(switches::kDisableBackgroundNetworking);
std::string url_prefix =
cmdline->HasSwitch(switches::kSbURLPrefix) ?
cmdline->GetSwitchValueASCII(switches::kSbURLPrefix) :
kSbDefaultURLPrefix;
DCHECK(!protocol_manager_);
protocol_manager_ =
SafeBrowsingProtocolManager::Create(this,
client_name,
url_request_context_getter_,
url_prefix,
disable_auto_update);
protocol_manager_->Initialize();
}
void SafeBrowsingService::StopOnIOThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!enabled_)
return;
enabled_ = false;
registrar_.reset();
// This cancels all in-flight GetHash requests.
delete protocol_manager_;
protocol_manager_ = NULL;
// Delete queued checks, calling back any clients with 'SAFE'.
// If we don't do this here we may fail to close the database below.
while (!queued_checks_.empty()) {
QueuedCheck queued = queued_checks_.front();
if (queued.client) {
SafeBrowsingCheck sb_check;
sb_check.urls.push_back(queued.url);
sb_check.client = queued.client;
sb_check.result = SAFE;
queued.client->OnSafeBrowsingResult(sb_check);
}
queued_checks_.pop_front();
}
// Close the database. We don't simply DeleteSoon() because if a close is
// already pending, we'll double-free, and we don't set |database_| to NULL
// because if there is still anything running on the db thread, it could
// create a new database object (via GetDatabase()) that would then leak.
CloseDatabase();
// Flush the database thread. Any in-progress database check results will be
// ignored and cleaned up below.
//
// Note that to avoid leaking the database, we rely on the fact that no new
// tasks will be added to the db thread between the call above and this one.
// See comments on the declaration of |safe_browsing_thread_|.
{
// A ScopedAllowIO object is required to join the thread when calling Stop.
// See http://crbug.com/72696.
base::ThreadRestrictions::ScopedAllowIO allow_io_for_thread_join;
safe_browsing_thread_.reset();
}
// Delete pending checks, calling back any clients with 'SAFE'. We have
// to do this after the db thread returns because methods on it can have
// copies of these pointers, so deleting them might lead to accessing garbage.
for (CurrentChecks::iterator it = checks_.begin();
it != checks_.end(); ++it) {
SafeBrowsingCheck* check = *it;
if (check->client) {
check->result = SAFE;
check->client->OnSafeBrowsingResult(*check);
}
}
STLDeleteElements(&checks_);
gethash_requests_.clear();
}
bool SafeBrowsingService::DatabaseAvailable() const {
base::AutoLock lock(database_lock_);
return !closing_database_ && (database_ != NULL);
}
bool SafeBrowsingService::MakeDatabaseAvailable() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(enabled_);
if (DatabaseAvailable())
return true;
safe_browsing_thread_->message_loop()->PostTask(
FROM_HERE,
base::Bind(base::IgnoreResult(&SafeBrowsingService::GetDatabase), this));
return false;
}
void SafeBrowsingService::CloseDatabase() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
// Cases to avoid:
// * If |closing_database_| is true, continuing will queue up a second
// request, |closing_database_| will be reset after handling the first
// request, and if any functions on the db thread recreate the database, we
// could start using it on the IO thread and then have the second request
// handler delete it out from under us.
// * If |database_| is NULL, then either no creation request is in flight, in
// which case we don't need to do anything, or one is in flight, in which
// case the database will be recreated before our deletion request is
// handled, and could be used on the IO thread in that time period, leading
// to the same problem as above.
// * If |queued_checks_| is non-empty and |database_| is non-NULL, we're
// about to be called back (in DatabaseLoadComplete()). This will call
// CheckUrl(), which will want the database. Closing the database here
// would lead to an infinite loop in DatabaseLoadComplete(), and even if it
// didn't, it would be pointless since we'd just want to recreate.
//
// The first two cases above are handled by checking DatabaseAvailable().
if (!DatabaseAvailable() || !queued_checks_.empty())
return;
closing_database_ = true;
if (safe_browsing_thread_.get()) {
safe_browsing_thread_->message_loop()->PostTask(FROM_HERE,
base::Bind(&SafeBrowsingService::OnCloseDatabase, this));
}
}
SafeBrowsingDatabase* SafeBrowsingService::GetDatabase() {
DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
if (database_)
return database_;
const base::TimeTicks before = base::TimeTicks::Now();
SafeBrowsingDatabase* database =
SafeBrowsingDatabase::Create(enable_download_protection_,
enable_csd_whitelist_,
enable_download_whitelist_);
database->Init(BaseFilename());
{
// Acquiring the lock here guarantees correct ordering between the writes to
// the new database object above, and the setting of |databse_| below.
base::AutoLock lock(database_lock_);
database_ = database;
}
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&SafeBrowsingService::DatabaseLoadComplete, this));
UMA_HISTOGRAM_TIMES("SB2.DatabaseOpen", base::TimeTicks::Now() - before);
return database_;
}
void SafeBrowsingService::OnCheckDone(SafeBrowsingCheck* check) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!enabled_)
return;
// If the service has been shut down, |check| should have been deleted.
DCHECK(checks_.find(check) != checks_.end());
if (check->client && check->need_get_hash) {
// We have a partial match so we need to query Google for the full hash.
// Clean up will happen in HandleGetHashResults.
// See if we have a GetHash request already in progress for this particular
// prefix. If so, we just append ourselves to the list of interested parties
// when the results arrive. We only do this for checks involving one prefix,
// since that is the common case (multiple prefixes will issue the request
// as normal).
if (check->prefix_hits.size() == 1) {
SBPrefix prefix = check->prefix_hits[0];
GetHashRequests::iterator it = gethash_requests_.find(prefix);
if (it != gethash_requests_.end()) {
// There's already a request in progress.
it->second.push_back(check);
return;
}
// No request in progress, so we're the first for this prefix.
GetHashRequestors requestors;
requestors.push_back(check);
gethash_requests_[prefix] = requestors;
}
// Reset the start time so that we can measure the network time without the
// database time.
check->start = base::TimeTicks::Now();
protocol_manager_->GetFullHash(check, check->prefix_hits);
} else {
// We may have cached results for previous GetHash queries. Since
// this data comes from cache, don't histogram hits.
HandleOneCheck(check, check->full_hits);
}
}
void SafeBrowsingService::GetAllChunksFromDatabase() {
DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
bool database_error = true;
std::vector<SBListChunkRanges> lists;
DCHECK(!database_update_in_progress_);
database_update_in_progress_ = true;
GetDatabase(); // This guarantees that |database_| is non-NULL.
if (database_->UpdateStarted(&lists)) {
database_error = false;
} else {
database_->UpdateFinished(false);
}
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&SafeBrowsingService::OnGetAllChunksFromDatabase,
this, lists, database_error));
}
void SafeBrowsingService::OnGetAllChunksFromDatabase(
const std::vector<SBListChunkRanges>& lists, bool database_error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (enabled_)
protocol_manager_->OnGetChunksComplete(lists, database_error);
}
void SafeBrowsingService::OnChunkInserted() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (enabled_)
protocol_manager_->OnChunkInserted();
}
void SafeBrowsingService::DatabaseLoadComplete() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!enabled_)
return;
HISTOGRAM_COUNTS("SB.QueueDepth", queued_checks_.size());
if (queued_checks_.empty())
return;
// If the database isn't already available, calling CheckUrl() in the loop
// below will add the check back to the queue, and we'll infinite-loop.
DCHECK(DatabaseAvailable());
while (!queued_checks_.empty()) {
QueuedCheck check = queued_checks_.front();
DCHECK(!check.start.is_null());
HISTOGRAM_TIMES("SB.QueueDelay", base::TimeTicks::Now() - check.start);
// If CheckUrl() determines the URL is safe immediately, it doesn't call the
// client's handler function (because normally it's being directly called by
// the client). Since we're not the client, we have to convey this result.
if (check.client && CheckBrowseUrl(check.url, check.client)) {
SafeBrowsingCheck sb_check;
sb_check.urls.push_back(check.url);
sb_check.client = check.client;
sb_check.result = SAFE;
check.client->OnSafeBrowsingResult(sb_check);
}
queued_checks_.pop_front();
}
}
void SafeBrowsingService::HandleChunkForDatabase(
const std::string& list_name, SBChunkList* chunks) {
DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
if (chunks) {
GetDatabase()->InsertChunks(list_name, *chunks);
delete chunks;
}
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&SafeBrowsingService::OnChunkInserted, this));
}
void SafeBrowsingService::DeleteChunks(
std::vector<SBChunkDelete>* chunk_deletes) {
DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
if (chunk_deletes) {
GetDatabase()->DeleteChunks(*chunk_deletes);
delete chunk_deletes;
}
}
SafeBrowsingService::UrlCheckResult SafeBrowsingService::GetResultFromListname(
const std::string& list_name) {
if (safe_browsing_util::IsPhishingList(list_name)) {
return URL_PHISHING;
}
if (safe_browsing_util::IsMalwareList(list_name)) {
return URL_MALWARE;
}
if (safe_browsing_util::IsBadbinurlList(list_name)) {
return BINARY_MALWARE_URL;
}
if (safe_browsing_util::IsBadbinhashList(list_name)) {
return BINARY_MALWARE_HASH;
}
DVLOG(1) << "Unknown safe browsing list " << list_name;
return SAFE;
}
void SafeBrowsingService::DatabaseUpdateFinished(bool update_succeeded) {
DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
GetDatabase()->UpdateFinished(update_succeeded);
DCHECK(database_update_in_progress_);
database_update_in_progress_ = false;
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&SafeBrowsingService::NotifyDatabaseUpdateFinished,
this, update_succeeded));
}
void SafeBrowsingService::NotifyDatabaseUpdateFinished(bool update_succeeded) {
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
content::Source<SafeBrowsingService>(this),
content::Details<bool>(&update_succeeded));
}
void SafeBrowsingService::Start() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
CommandLine* cmdline = CommandLine::ForCurrentProcess();
enable_download_protection_ =
!cmdline->HasSwitch(switches::kSbDisableDownloadProtection);
// We only download the csd-whitelist if client-side phishing detection is
// enabled.
enable_csd_whitelist_ =
!cmdline->HasSwitch(switches::kDisableClientSidePhishingDetection);
// TODO(noelutz): remove this boolean variable since it should always be true
// if SafeBrowsing is enabled. Unfortunately, we have no test data for this
// list right now. This means that we need to be able to disable this list
// for the SafeBrowsing test to pass.
enable_download_whitelist_ = enable_csd_whitelist_;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&SafeBrowsingService::StartOnIOThread, this));
}
void SafeBrowsingService::Stop() {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&SafeBrowsingService::StopOnIOThread, this));
}
void SafeBrowsingService::OnCloseDatabase() {
DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
DCHECK(closing_database_);
// Because |closing_database_| is true, nothing on the IO thread will be
// accessing the database, so it's safe to delete and then NULL the pointer.
delete database_;
database_ = NULL;
// Acquiring the lock here guarantees correct ordering between the resetting
// of |database_| above and of |closing_database_| below, which ensures there
// won't be a window during which the IO thread falsely believes the database
// is available.
base::AutoLock lock(database_lock_);
closing_database_ = false;
}
void SafeBrowsingService::OnResetDatabase() {
DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
GetDatabase()->ResetDatabase();
}
void SafeBrowsingService::CacheHashResults(
const std::vector<SBPrefix>& prefixes,
const std::vector<SBFullHashResult>& full_hashes) {
DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
GetDatabase()->CacheHashResults(prefixes, full_hashes);
}
void SafeBrowsingService::OnHandleGetHashResults(
SafeBrowsingCheck* check,
const std::vector<SBFullHashResult>& full_hashes) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
bool is_download = check->is_download;
SBPrefix prefix = check->prefix_hits[0];
GetHashRequests::iterator it = gethash_requests_.find(prefix);
if (check->prefix_hits.size() > 1 || it == gethash_requests_.end()) {
const bool hit = HandleOneCheck(check, full_hashes);
RecordGetHashCheckStatus(hit, is_download, full_hashes);
return;
}
// Call back all interested parties, noting if any has a hit.
GetHashRequestors& requestors = it->second;
bool hit = false;
for (GetHashRequestors::iterator r = requestors.begin();
r != requestors.end(); ++r) {
if (HandleOneCheck(*r, full_hashes))
hit = true;
}
RecordGetHashCheckStatus(hit, is_download, full_hashes);
gethash_requests_.erase(it);
}
bool SafeBrowsingService::HandleOneCheck(
SafeBrowsingCheck* check,
const std::vector<SBFullHashResult>& full_hashes) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(check);
// Always calculate the index, for recording hits.
int index = -1;
if (!check->urls.empty()) {
for (size_t i = 0; i < check->urls.size(); ++i) {
index = safe_browsing_util::GetUrlHashIndex(check->urls[i], full_hashes);
if (index != -1)
break;
}
} else {
index = safe_browsing_util::GetHashIndex(*(check->full_hash), full_hashes);
}
// |client| is NULL if the request was cancelled.
if (check->client) {
check->result = SAFE;
if (index != -1)
check->result = GetResultFromListname(full_hashes[index].list_name);
}
SafeBrowsingCheckDone(check);
return (index != -1);
}
void SafeBrowsingService::DoDisplayBlockingPage(
const UnsafeResource& resource) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Check if the user has already ignored our warning for this render_view
// and domain.
if (IsWhitelisted(resource)) {
if (!resource.callback.is_null()) {
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE, base::Bind(resource.callback, true));
}
return;
}
// The tab might have been closed.
WebContents* web_contents =
tab_util::GetWebContentsByID(resource.render_process_host_id,
resource.render_view_id);
if (!web_contents) {
// The tab is gone and we did not have a chance at showing the interstitial.
// Just act as if "Don't Proceed" were chosen.
std::vector<UnsafeResource> resources;
resources.push_back(resource);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&SafeBrowsingService::OnBlockingPageDone,
this, resources, false));
return;
}
if (resource.threat_type != SafeBrowsingService::SAFE &&
CanReportStats()) {
GURL page_url = web_contents->GetURL();
GURL referrer_url;
NavigationEntry* entry = web_contents->GetController().GetActiveEntry();
if (entry)
referrer_url = entry->GetReferrer().url;
// When the malicious url is on the main frame, and resource.original_url
// is not the same as the resource.url, that means we have a redirect from
// resource.original_url to resource.url.
// Also, at this point, page_url points to the _previous_ page that we
// were on. We replace page_url with resource.original_url and referrer
// with page_url.
if (!resource.is_subresource &&
!resource.original_url.is_empty() &&
resource.original_url != resource.url) {
referrer_url = page_url;
page_url = resource.original_url;
}
ReportSafeBrowsingHit(resource.url, page_url, referrer_url,
resource.is_subresource, resource.threat_type,
std::string() /* post_data */);
}
if (resource.threat_type != SafeBrowsingService::SAFE) {
FOR_EACH_OBSERVER(Observer, observer_list_, OnSafeBrowsingHit(resource));
}
SafeBrowsingBlockingPage::ShowBlockingPage(this, resource);
}
// A safebrowsing hit is sent after a blocking page for malware/phishing
// or after the warning dialog for download urls, only for UMA users.
void SafeBrowsingService::ReportSafeBrowsingHit(
const GURL& malicious_url,
const GURL& page_url,
const GURL& referrer_url,
bool is_subresource,
SafeBrowsingService::UrlCheckResult threat_type,
const std::string& post_data) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!CanReportStats())
return;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&SafeBrowsingService::ReportSafeBrowsingHitOnIOThread, this,
malicious_url, page_url, referrer_url, is_subresource,
threat_type, post_data));
}
void SafeBrowsingService::AddObserver(Observer* observer) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
observer_list_.AddObserver(observer);
}
void SafeBrowsingService::RemoveObserver(Observer* observer) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
observer_list_.RemoveObserver(observer);
}
void SafeBrowsingService::ReportSafeBrowsingHitOnIOThread(
const GURL& malicious_url,
const GURL& page_url,
const GURL& referrer_url,
bool is_subresource,
SafeBrowsingService::UrlCheckResult threat_type,
const std::string& post_data) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!enabled_)
return;
DVLOG(1) << "ReportSafeBrowsingHit: " << malicious_url << " " << page_url
<< " " << referrer_url << " " << is_subresource << " "
<< threat_type;
protocol_manager_->ReportSafeBrowsingHit(malicious_url, page_url,
referrer_url, is_subresource,
threat_type, post_data);
}
// If the user had opted-in to send MalwareDetails, this gets called
// when the report is ready.
void SafeBrowsingService::SendSerializedMalwareDetails(
const std::string& serialized) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (!enabled_)
return;
if (!serialized.empty()) {
DVLOG(1) << "Sending serialized malware details.";
protocol_manager_->ReportMalwareDetails(serialized);
}
}
void SafeBrowsingService::CheckDownloadHashOnSBThread(
SafeBrowsingCheck* check) {
DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
DCHECK(enable_download_protection_);
if (!database_->ContainsDownloadHashPrefix(check->full_hash->prefix)) {
// Good, we don't have hash for this url prefix.
check->result = SAFE;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&SafeBrowsingService::CheckDownloadHashDone, this, check));
return;
}
check->need_get_hash = true;
check->prefix_hits.push_back(check->full_hash->prefix);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&SafeBrowsingService::OnCheckDone, this, check));
}
void SafeBrowsingService::CheckDownloadUrlOnSBThread(SafeBrowsingCheck* check) {
DCHECK_EQ(MessageLoop::current(), safe_browsing_thread_->message_loop());
DCHECK(enable_download_protection_);
std::vector<SBPrefix> prefix_hits;
if (!database_->ContainsDownloadUrl(check->urls, &prefix_hits)) {
// Good, we don't have hash for this url prefix.
check->result = SAFE;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&SafeBrowsingService::CheckDownloadUrlDone, this, check));
return;
}
check->need_get_hash = true;
check->prefix_hits.clear();
check->prefix_hits = prefix_hits;
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&SafeBrowsingService::OnCheckDone, this, check));
}
void SafeBrowsingService::TimeoutCallback(SafeBrowsingCheck* check) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(check);
if (!enabled_)
return;
DCHECK(checks_.find(check) != checks_.end());
DCHECK_EQ(check->result, SAFE);
if (check->client) {
check->client->OnSafeBrowsingResult(*check);
check->client = NULL;
}
}
void SafeBrowsingService::CheckDownloadUrlDone(SafeBrowsingCheck* check) {
DCHECK(enable_download_protection_);
SafeBrowsingCheckDone(check);
}
void SafeBrowsingService::CheckDownloadHashDone(SafeBrowsingCheck* check) {
DCHECK(enable_download_protection_);
SafeBrowsingCheckDone(check);
}
void SafeBrowsingService::SafeBrowsingCheckDone(SafeBrowsingCheck* check) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(check);
if (!enabled_)
return;
VLOG(1) << "SafeBrowsingCheckDone: " << check->result;
DCHECK(checks_.find(check) != checks_.end());
if (check->client)
check->client->OnSafeBrowsingResult(*check);
checks_.erase(check);
delete check;
}
void SafeBrowsingService::StartDownloadCheck(SafeBrowsingCheck* check,
Client* client,
const base::Closure& task,
int64 timeout_ms) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
check->client = client;
check->result = SAFE;
check->is_download = true;
check->timeout_factory_.reset(
new base::WeakPtrFactory<SafeBrowsingService>(this));
checks_.insert(check);
safe_browsing_thread_->message_loop()->PostTask(FROM_HERE, task);
MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&SafeBrowsingService::TimeoutCallback,
check->timeout_factory_->GetWeakPtr(), check),
base::TimeDelta::FromMilliseconds(timeout_ms));
}
void SafeBrowsingService::UpdateWhitelist(const UnsafeResource& resource) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Whitelist this domain and warning type for the given tab.
WhiteListedEntry entry;
entry.render_process_host_id = resource.render_process_host_id;
entry.render_view_id = resource.render_view_id;
entry.domain = net::RegistryControlledDomainService::GetDomainAndRegistry(
resource.url);
entry.result = resource.threat_type;
white_listed_entries_.push_back(entry);
}
void SafeBrowsingService::Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
switch (type) {
case chrome::NOTIFICATION_PROFILE_CREATED: {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
Profile* profile = content::Source<Profile>(source).ptr();
if (!profile->IsOffTheRecord())
AddPrefService(profile->GetPrefs());
break;
}
case chrome::NOTIFICATION_PROFILE_DESTROYED: {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
Profile* profile = content::Source<Profile>(source).ptr();
if (!profile->IsOffTheRecord())
RemovePrefService(profile->GetPrefs());
break;
}
case chrome::NOTIFICATION_PREF_CHANGED: {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
std::string* pref = content::Details<std::string>(details).ptr();
DCHECK(*pref == prefs::kSafeBrowsingEnabled);
RefreshState();
break;
}
default:
NOTREACHED();
}
}
bool SafeBrowsingService::IsWhitelisted(const UnsafeResource& resource) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Check if the user has already ignored our warning for this render_view
// and domain.
for (size_t i = 0; i < white_listed_entries_.size(); ++i) {
const WhiteListedEntry& entry = white_listed_entries_[i];
if (entry.render_process_host_id == resource.render_process_host_id &&
entry.render_view_id == resource.render_view_id &&
// Threat type must be the same or in the case of phishing they can
// either be client-side phishing URL or a SafeBrowsing phishing URL.
// If we show one type of phishing warning we don't want to show a
// second phishing warning.
(entry.result == resource.threat_type ||
(entry.result == URL_PHISHING &&
resource.threat_type == CLIENT_SIDE_PHISHING_URL) ||
(entry.result == CLIENT_SIDE_PHISHING_URL &&
resource.threat_type == URL_PHISHING)) &&
entry.domain ==
net::RegistryControlledDomainService::GetDomainAndRegistry(
resource.url)) {
return true;
}
}
return false;
}
void SafeBrowsingService::AddPrefService(PrefService* pref_service) {
DCHECK(prefs_map_.find(pref_service) == prefs_map_.end());
PrefChangeRegistrar* registrar = new PrefChangeRegistrar();
registrar->Init(pref_service);
registrar->Add(prefs::kSafeBrowsingEnabled, this);
prefs_map_[pref_service] = registrar;
RefreshState();
}
void SafeBrowsingService::RemovePrefService(PrefService* pref_service) {
if (prefs_map_.find(pref_service) != prefs_map_.end()) {
delete prefs_map_[pref_service];
prefs_map_.erase(pref_service);
RefreshState();
} else {
NOTREACHED();
}
}
void SafeBrowsingService::RefreshState() {
// Check if any profile requires the service to be active.
bool enable = false;
std::map<PrefService*, PrefChangeRegistrar*>::iterator iter;
for (iter = prefs_map_.begin(); iter != prefs_map_.end(); ++iter) {
if (iter->first->GetBoolean(prefs::kSafeBrowsingEnabled)) {
enable = true;
break;
}
}
if (enable)
Start();
else
Stop();
if (csd_service_.get())
csd_service_->SetEnabledAndRefreshState(enable);
if (download_service_.get()) {
download_service_->SetEnabled(
enable && !CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableImprovedDownloadProtection));
}
}