blob: 3a7e2ef9eeff67daca13cd6d09829a918f4d74db [file] [log] [blame]
[email protected]fdd679b2012-11-15 20:49:391// Copyright 2012 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.
4
5#include "chrome/browser/extensions/blacklist.h"
6
[email protected]695b5712012-12-06 23:55:287#include <algorithm>
[email protected]c014f2b32013-09-03 23:29:128#include <iterator>
[email protected]695b5712012-12-06 23:55:289
10#include "base/bind.h"
Avi Drissmane222a562018-03-27 03:25:4811#include "base/callback_list.h"
[email protected]3e72ed752013-02-02 00:47:4712#include "base/lazy_instance.h"
avia2f4804a2015-12-24 23:11:1313#include "base/macros.h"
[email protected]3e72ed752013-02-02 00:47:4714#include "base/memory/ref_counted.h"
pranay.kumar07300782015-05-04 14:03:5815#include "base/single_thread_task_runner.h"
[email protected]16bf7ba72013-08-23 11:52:5416#include "base/stl_util.h"
gabb15e19072016-05-11 20:45:4117#include "base/threading/thread_task_runner_handle.h"
[email protected]3e72ed752013-02-02 00:47:4718#include "chrome/browser/browser_process.h"
reillyg121e8892014-11-03 22:12:5919#include "chrome/browser/extensions/blacklist_factory.h"
[email protected]8e289f0b2013-12-17 17:49:0720#include "chrome/browser/extensions/blacklist_state_fetcher.h"
[email protected]3e72ed752013-02-02 00:47:4721#include "chrome/browser/safe_browsing/safe_browsing_service.h"
brettwb1fc1b82016-02-02 00:19:0822#include "components/prefs/pref_service.h"
Tim Volodinee45938472017-09-21 10:08:2223#include "components/safe_browsing/db/util.h"
Gabriel Charette790754c2018-03-16 21:32:5924#include "content/public/browser/browser_thread.h"
[email protected]489db0842014-01-22 18:20:0325#include "extensions/browser/extension_prefs.h"
[email protected]3e72ed752013-02-02 00:47:4726
27using content::BrowserThread;
vakh9a474d832015-11-13 01:43:0928using safe_browsing::SafeBrowsingDatabaseManager;
[email protected]fdd679b2012-11-15 20:49:3929
30namespace extensions {
31
[email protected]3e72ed752013-02-02 00:47:4732namespace {
33
34// The safe browsing database manager to use. Make this a global/static variable
35// rather than a member of Blacklist because Blacklist accesses the real
36// database manager before it has a chance to get a fake one.
37class LazySafeBrowsingDatabaseManager {
38 public:
39 LazySafeBrowsingDatabaseManager() {
nparker333f169b2015-04-18 13:33:0740#if defined(SAFE_BROWSING_DB_LOCAL)
[email protected]3e72ed752013-02-02 00:47:4741 if (g_browser_process && g_browser_process->safe_browsing_service()) {
42 instance_ =
43 g_browser_process->safe_browsing_service()->database_manager();
44 }
[email protected]04f7d8292013-02-18 09:31:1045#endif
[email protected]3e72ed752013-02-02 00:47:4746 }
47
48 scoped_refptr<SafeBrowsingDatabaseManager> get() {
49 return instance_;
50 }
51
52 void set(scoped_refptr<SafeBrowsingDatabaseManager> instance) {
53 instance_ = instance;
Avi Drissmane222a562018-03-27 03:25:4854 database_changed_callback_list_.Notify();
55 }
56
57 std::unique_ptr<base::CallbackList<void()>::Subscription>
58 RegisterDatabaseChangedCallback(const base::RepeatingClosure& cb) {
59 return database_changed_callback_list_.Add(cb);
[email protected]3e72ed752013-02-02 00:47:4760 }
61
62 private:
63 scoped_refptr<SafeBrowsingDatabaseManager> instance_;
Avi Drissmane222a562018-03-27 03:25:4864 base::CallbackList<void()> database_changed_callback_list_;
[email protected]3e72ed752013-02-02 00:47:4765};
66
scottmg5e65e3a2017-03-08 08:48:4667static base::LazyInstance<LazySafeBrowsingDatabaseManager>::DestructorAtExit
68 g_database_manager = LAZY_INSTANCE_INITIALIZER;
[email protected]3e72ed752013-02-02 00:47:4769
70// Implementation of SafeBrowsingDatabaseManager::Client, the class which is
71// called back from safebrowsing queries.
72//
73// Constructed on any thread but lives on the IO from then on.
74class SafeBrowsingClientImpl
75 : public SafeBrowsingDatabaseManager::Client,
76 public base::RefCountedThreadSafe<SafeBrowsingClientImpl> {
77 public:
Avi Drissman6ee0692f2018-03-26 17:08:0178 using OnResultCallback = base::Callback<void(const std::set<std::string>&)>;
[email protected]3e72ed752013-02-02 00:47:4779
80 // Constructs a client to query the database manager for |extension_ids| and
81 // run |callback| with the IDs of those which have been blacklisted.
82 SafeBrowsingClientImpl(
83 const std::set<std::string>& extension_ids,
84 const OnResultCallback& callback)
pranay.kumar07300782015-05-04 14:03:5885 : callback_task_runner_(base::ThreadTaskRunnerHandle::Get()),
[email protected]3e72ed752013-02-02 00:47:4786 callback_(callback) {
87 BrowserThread::PostTask(
tzik8d880ee2017-04-20 19:46:2488 BrowserThread::IO, FROM_HERE,
89 base::BindOnce(&SafeBrowsingClientImpl::StartCheck, this,
90 g_database_manager.Get().get(), extension_ids));
[email protected]3e72ed752013-02-02 00:47:4791 }
92
93 private:
94 friend class base::RefCountedThreadSafe<SafeBrowsingClientImpl>;
dchengae36a4a2014-10-21 12:36:3695 ~SafeBrowsingClientImpl() override {}
[email protected]3e72ed752013-02-02 00:47:4796
97 // Pass |database_manager| as a parameter to avoid touching
98 // SafeBrowsingService on the IO thread.
99 void StartCheck(scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
100 const std::set<std::string>& extension_ids) {
[email protected]54ee8192014-03-29 17:37:24101 DCHECK_CURRENTLY_ON(BrowserThread::IO);
[email protected]3e72ed752013-02-02 00:47:47102 if (database_manager->CheckExtensionIDs(extension_ids, this)) {
103 // Definitely not blacklisted. Callback immediately.
pranay.kumar07300782015-05-04 14:03:58104 callback_task_runner_->PostTask(
tzik8d880ee2017-04-20 19:46:24105 FROM_HERE, base::BindOnce(callback_, std::set<std::string>()));
[email protected]3e72ed752013-02-02 00:47:47106 return;
107 }
108 // Something might be blacklisted, response will come in
109 // OnCheckExtensionsResult.
110 AddRef(); // Balanced in OnCheckExtensionsResult
111 }
112
dchengae36a4a2014-10-21 12:36:36113 void OnCheckExtensionsResult(const std::set<std::string>& hits) override {
[email protected]54ee8192014-03-29 17:37:24114 DCHECK_CURRENTLY_ON(BrowserThread::IO);
tzik8d880ee2017-04-20 19:46:24115 callback_task_runner_->PostTask(FROM_HERE, base::BindOnce(callback_, hits));
[email protected]3e72ed752013-02-02 00:47:47116 Release(); // Balanced in StartCheck.
117 }
118
pranay.kumar07300782015-05-04 14:03:58119 scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner_;
[email protected]3e72ed752013-02-02 00:47:47120 OnResultCallback callback_;
121
122 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingClientImpl);
123};
124
[email protected]48a359342013-10-30 00:22:00125void CheckOneExtensionState(
126 const Blacklist::IsBlacklistedCallback& callback,
127 const Blacklist::BlacklistStateMap& state_map) {
[email protected]8e289f0b2013-12-17 17:49:07128 callback.Run(state_map.empty() ? NOT_BLACKLISTED : state_map.begin()->second);
[email protected]48a359342013-10-30 00:22:00129}
130
131void GetMalwareFromBlacklistStateMap(
132 const Blacklist::GetMalwareIDsCallback& callback,
133 const Blacklist::BlacklistStateMap& state_map) {
134 std::set<std::string> malware;
Avi Drissman6ee0692f2018-03-26 17:08:01135 for (const auto& state_pair : state_map) {
[email protected]8e289f0b2013-12-17 17:49:07136 // TODO(oleg): UNKNOWN is treated as MALWARE for backwards compatibility.
137 // In future GetMalwareIDs will be removed and the caller will have to
138 // deal with BLACKLISTED_UNKNOWN state returned from GetBlacklistedIDs.
Avi Drissman6ee0692f2018-03-26 17:08:01139 if (state_pair.second == BLACKLISTED_MALWARE ||
140 state_pair.second == BLACKLISTED_UNKNOWN) {
141 malware.insert(state_pair.first);
142 }
[email protected]48a359342013-10-30 00:22:00143 }
144 callback.Run(malware);
[email protected]bc151cf92013-02-12 04:57:26145}
146
[email protected]3e72ed752013-02-02 00:47:47147} // namespace
148
[email protected]fdd679b2012-11-15 20:49:39149Blacklist::Observer::Observer(Blacklist* blacklist) : blacklist_(blacklist) {
150 blacklist_->AddObserver(this);
151}
152
153Blacklist::Observer::~Observer() {
154 blacklist_->RemoveObserver(this);
155}
156
[email protected]3e72ed752013-02-02 00:47:47157Blacklist::ScopedDatabaseManagerForTest::ScopedDatabaseManagerForTest(
158 scoped_refptr<SafeBrowsingDatabaseManager> database_manager)
159 : original_(GetDatabaseManager()) {
160 SetDatabaseManager(database_manager);
161}
162
163Blacklist::ScopedDatabaseManagerForTest::~ScopedDatabaseManagerForTest() {
164 SetDatabaseManager(original_);
165}
166
[email protected]3f2a2fa2013-09-24 02:55:25167Blacklist::Blacklist(ExtensionPrefs* prefs) {
Avi Drissmane222a562018-03-27 03:25:48168 auto& lazy_database_manager = g_database_manager.Get();
169 // Using base::Unretained is safe because when this object goes away, the
170 // subscription will automatically be destroyed.
171 database_changed_subscription_ =
172 lazy_database_manager.RegisterDatabaseChangedCallback(base::BindRepeating(
173 &Blacklist::ObserveNewDatabase, base::Unretained(this)));
174
175 ObserveNewDatabase();
[email protected]fdd679b2012-11-15 20:49:39176}
177
178Blacklist::~Blacklist() {
179}
180
reillyg121e8892014-11-03 22:12:59181// static
182Blacklist* Blacklist::Get(content::BrowserContext* context) {
183 return BlacklistFactory::GetForBrowserContext(context);
184}
185
[email protected]695b5712012-12-06 23:55:28186void Blacklist::GetBlacklistedIDs(const std::set<std::string>& ids,
187 const GetBlacklistedIDsCallback& callback) {
[email protected]54ee8192014-03-29 17:37:24188 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]3e72ed752013-02-02 00:47:47189
Avi Drissman6ee0692f2018-03-26 17:08:01190 if (ids.empty() || !GetDatabaseManager().get()) {
pranay.kumar07300782015-05-04 14:03:58191 base::ThreadTaskRunnerHandle::Get()->PostTask(
tzik8d880ee2017-04-20 19:46:24192 FROM_HERE, base::BindOnce(callback, BlacklistStateMap()));
[email protected]3e72ed752013-02-02 00:47:47193 return;
194 }
195
196 // Constructing the SafeBrowsingClientImpl begins the process of asking
[email protected]48a359342013-10-30 00:22:00197 // safebrowsing for the blacklisted extensions. The set of blacklisted
198 // extensions returned by SafeBrowsing will then be passed to
199 // GetBlacklistStateIDs to get the particular BlacklistState for each id.
200 new SafeBrowsingClientImpl(
201 ids, base::Bind(&Blacklist::GetBlacklistStateForIDs, AsWeakPtr(),
202 callback));
[email protected]fdd679b2012-11-15 20:49:39203}
204
[email protected]48a359342013-10-30 00:22:00205void Blacklist::GetMalwareIDs(const std::set<std::string>& ids,
206 const GetMalwareIDsCallback& callback) {
207 GetBlacklistedIDs(ids, base::Bind(&GetMalwareFromBlacklistStateMap,
208 callback));
209}
210
211
[email protected]bc151cf92013-02-12 04:57:26212void Blacklist::IsBlacklisted(const std::string& extension_id,
213 const IsBlacklistedCallback& callback) {
214 std::set<std::string> check;
215 check.insert(extension_id);
[email protected]48a359342013-10-30 00:22:00216 GetBlacklistedIDs(check, base::Bind(&CheckOneExtensionState, callback));
217}
218
219void Blacklist::GetBlacklistStateForIDs(
220 const GetBlacklistedIDsCallback& callback,
221 const std::set<std::string>& blacklisted_ids) {
[email protected]54ee8192014-03-29 17:37:24222 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]48a359342013-10-30 00:22:00223
224 std::set<std::string> ids_unknown_state;
225 BlacklistStateMap extensions_state;
Avi Drissman6ee0692f2018-03-26 17:08:01226 for (const auto& blacklisted_id : blacklisted_ids) {
227 auto cache_it = blacklist_state_cache_.find(blacklisted_id);
[email protected]8e289f0b2013-12-17 17:49:07228 if (cache_it == blacklist_state_cache_.end() ||
Avi Drissman6ee0692f2018-03-26 17:08:01229 cache_it->second ==
230 BLACKLISTED_UNKNOWN) { // Do not return UNKNOWN
231 // from cache, retry request.
232 ids_unknown_state.insert(blacklisted_id);
233 } else {
234 extensions_state[blacklisted_id] = cache_it->second;
235 }
[email protected]48a359342013-10-30 00:22:00236 }
237
238 if (ids_unknown_state.empty()) {
239 callback.Run(extensions_state);
240 } else {
241 // After the extension blacklist states have been downloaded, call this
242 // functions again, but prevent infinite cycle in case server is offline
243 // or some other reason prevents us from receiving the blacklist state for
244 // these extensions.
245 RequestExtensionsBlacklistState(
246 ids_unknown_state,
Avi Drissman6ee0692f2018-03-26 17:08:01247 base::BindOnce(&Blacklist::ReturnBlacklistStateMap, AsWeakPtr(),
248 callback, blacklisted_ids));
[email protected]48a359342013-10-30 00:22:00249 }
250}
251
252void Blacklist::ReturnBlacklistStateMap(
253 const GetBlacklistedIDsCallback& callback,
254 const std::set<std::string>& blacklisted_ids) {
255 BlacklistStateMap extensions_state;
Avi Drissman6ee0692f2018-03-26 17:08:01256 for (const auto& blacklisted_id : blacklisted_ids) {
257 auto cache_it = blacklist_state_cache_.find(blacklisted_id);
[email protected]48a359342013-10-30 00:22:00258 if (cache_it != blacklist_state_cache_.end())
Avi Drissman6ee0692f2018-03-26 17:08:01259 extensions_state[blacklisted_id] = cache_it->second;
[email protected]48a359342013-10-30 00:22:00260 // If for some reason we still haven't cached the state of this extension,
261 // we silently skip it.
262 }
263
264 callback.Run(extensions_state);
265}
266
267void Blacklist::RequestExtensionsBlacklistState(
Avi Drissman6ee0692f2018-03-26 17:08:01268 const std::set<std::string>& ids,
269 base::OnceClosure callback) {
[email protected]54ee8192014-03-29 17:37:24270 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]8e289f0b2013-12-17 17:49:07271 if (!state_fetcher_)
272 state_fetcher_.reset(new BlacklistStateFetcher());
273
Avi Drissman6ee0692f2018-03-26 17:08:01274 state_requests_.emplace_back(std::vector<std::string>(ids.begin(), ids.end()),
275 std::move(callback));
276 for (const auto& id : ids) {
[email protected]8e289f0b2013-12-17 17:49:07277 state_fetcher_->Request(
Avi Drissman6ee0692f2018-03-26 17:08:01278 id, base::Bind(&Blacklist::OnBlacklistStateReceived, AsWeakPtr(), id));
[email protected]48a359342013-10-30 00:22:00279 }
[email protected]8e289f0b2013-12-17 17:49:07280}
281
282void Blacklist::OnBlacklistStateReceived(const std::string& id,
283 BlacklistState state) {
[email protected]54ee8192014-03-29 17:37:24284 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]8e289f0b2013-12-17 17:49:07285 blacklist_state_cache_[id] = state;
286
287 // Go through the opened requests and call the callbacks for those requests
288 // for which we already got all the required blacklist states.
Avi Drissman6ee0692f2018-03-26 17:08:01289 auto requests_it = state_requests_.begin();
[email protected]8e289f0b2013-12-17 17:49:07290 while (requests_it != state_requests_.end()) {
291 const std::vector<std::string>& ids = requests_it->first;
292
293 bool have_all_in_cache = true;
Avi Drissman6ee0692f2018-03-26 17:08:01294 for (const auto& id : ids) {
295 if (!base::ContainsKey(blacklist_state_cache_, id)) {
[email protected]8e289f0b2013-12-17 17:49:07296 have_all_in_cache = false;
297 break;
298 }
299 }
300
301 if (have_all_in_cache) {
Avi Drissman6ee0692f2018-03-26 17:08:01302 std::move(requests_it->second).Run();
[email protected]8e289f0b2013-12-17 17:49:07303 requests_it = state_requests_.erase(requests_it); // returns next element
304 } else {
305 ++requests_it;
306 }
307 }
308}
309
310void Blacklist::SetBlacklistStateFetcherForTest(
311 BlacklistStateFetcher* fetcher) {
312 state_fetcher_.reset(fetcher);
[email protected]bc151cf92013-02-12 04:57:26313}
314
[email protected]f71b582c2014-01-10 17:03:15315BlacklistStateFetcher* Blacklist::ResetBlacklistStateFetcherForTest() {
316 return state_fetcher_.release();
317}
318
Avi Drissmane222a562018-03-27 03:25:48319void Blacklist::ResetDatabaseUpdatedListenerForTest() {
320 database_updated_subscription_.reset();
321}
322
[email protected]fdd679b2012-11-15 20:49:39323void Blacklist::AddObserver(Observer* observer) {
[email protected]54ee8192014-03-29 17:37:24324 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]fdd679b2012-11-15 20:49:39325 observers_.AddObserver(observer);
326}
327
328void Blacklist::RemoveObserver(Observer* observer) {
[email protected]54ee8192014-03-29 17:37:24329 DCHECK_CURRENTLY_ON(BrowserThread::UI);
[email protected]fdd679b2012-11-15 20:49:39330 observers_.RemoveObserver(observer);
331}
332
[email protected]3e72ed752013-02-02 00:47:47333// static
334void Blacklist::SetDatabaseManager(
335 scoped_refptr<SafeBrowsingDatabaseManager> database_manager) {
336 g_database_manager.Get().set(database_manager);
337}
338
339// static
340scoped_refptr<SafeBrowsingDatabaseManager> Blacklist::GetDatabaseManager() {
341 return g_database_manager.Get().get();
342}
343
Avi Drissmane222a562018-03-27 03:25:48344void Blacklist::ObserveNewDatabase() {
345 auto database_manager = GetDatabaseManager();
346 if (database_manager.get()) {
347 // Using base::Unretained is safe because when this object goes away, the
348 // subscription to the callback list will automatically be destroyed.
349 database_updated_subscription_ =
350 database_manager.get()->RegisterDatabaseUpdatedCallback(
351 base::BindRepeating(&Blacklist::NotifyObservers,
352 base::Unretained(this)));
353 } else {
354 database_updated_subscription_.reset();
355 }
356}
357
358void Blacklist::NotifyObservers() {
ericwilligersb5f79de2016-10-19 04:15:10359 for (auto& observer : observers_)
360 observer.OnBlacklistUpdated();
[email protected]3e72ed752013-02-02 00:47:47361}
362
[email protected]fdd679b2012-11-15 20:49:39363} // namespace extensions