1 // 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.
5 #include "chrome/browser/extensions/blacklist.h"
10 #include "base/bind.h"
11 #include "base/lazy_instance.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/stl_util.h"
16 #include "base/thread_task_runner_handle.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/extensions/blacklist_factory.h"
20 #include "chrome/browser/extensions/blacklist_state_fetcher.h"
21 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
22 #include "chrome/browser/safe_browsing/safe_browsing_util.h"
23 #include "content/public/browser/notification_details.h"
24 #include "content/public/browser/notification_source.h"
25 #include "extensions/browser/extension_prefs.h"
27 using content::BrowserThread
;
29 namespace extensions
{
33 // The safe browsing database manager to use. Make this a global/static variable
34 // rather than a member of Blacklist because Blacklist accesses the real
35 // database manager before it has a chance to get a fake one.
36 class LazySafeBrowsingDatabaseManager
{
38 LazySafeBrowsingDatabaseManager() {
39 #if defined(SAFE_BROWSING_DB_LOCAL)
40 if (g_browser_process
&& g_browser_process
->safe_browsing_service()) {
42 g_browser_process
->safe_browsing_service()->database_manager();
47 scoped_refptr
<SafeBrowsingDatabaseManager
> get() {
51 void set(scoped_refptr
<SafeBrowsingDatabaseManager
> instance
) {
56 scoped_refptr
<SafeBrowsingDatabaseManager
> instance_
;
59 static base::LazyInstance
<LazySafeBrowsingDatabaseManager
> g_database_manager
=
60 LAZY_INSTANCE_INITIALIZER
;
62 // Implementation of SafeBrowsingDatabaseManager::Client, the class which is
63 // called back from safebrowsing queries.
65 // Constructed on any thread but lives on the IO from then on.
66 class SafeBrowsingClientImpl
67 : public SafeBrowsingDatabaseManager::Client
,
68 public base::RefCountedThreadSafe
<SafeBrowsingClientImpl
> {
70 typedef base::Callback
<void(const std::set
<std::string
>&)> OnResultCallback
;
72 // Constructs a client to query the database manager for |extension_ids| and
73 // run |callback| with the IDs of those which have been blacklisted.
74 SafeBrowsingClientImpl(
75 const std::set
<std::string
>& extension_ids
,
76 const OnResultCallback
& callback
)
77 : callback_task_runner_(base::ThreadTaskRunnerHandle::Get()),
79 BrowserThread::PostTask(
82 base::Bind(&SafeBrowsingClientImpl::StartCheck
, this,
83 g_database_manager
.Get().get(),
88 friend class base::RefCountedThreadSafe
<SafeBrowsingClientImpl
>;
89 ~SafeBrowsingClientImpl() override
{}
91 // Pass |database_manager| as a parameter to avoid touching
92 // SafeBrowsingService on the IO thread.
93 void StartCheck(scoped_refptr
<SafeBrowsingDatabaseManager
> database_manager
,
94 const std::set
<std::string
>& extension_ids
) {
95 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
96 if (database_manager
->CheckExtensionIDs(extension_ids
, this)) {
97 // Definitely not blacklisted. Callback immediately.
98 callback_task_runner_
->PostTask(
100 base::Bind(callback_
, std::set
<std::string
>()));
103 // Something might be blacklisted, response will come in
104 // OnCheckExtensionsResult.
105 AddRef(); // Balanced in OnCheckExtensionsResult
108 void OnCheckExtensionsResult(const std::set
<std::string
>& hits
) override
{
109 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
110 callback_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback_
, hits
));
111 Release(); // Balanced in StartCheck.
114 scoped_refptr
<base::SingleThreadTaskRunner
> callback_task_runner_
;
115 OnResultCallback callback_
;
117 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingClientImpl
);
120 void CheckOneExtensionState(
121 const Blacklist::IsBlacklistedCallback
& callback
,
122 const Blacklist::BlacklistStateMap
& state_map
) {
123 callback
.Run(state_map
.empty() ? NOT_BLACKLISTED
: state_map
.begin()->second
);
126 void GetMalwareFromBlacklistStateMap(
127 const Blacklist::GetMalwareIDsCallback
& callback
,
128 const Blacklist::BlacklistStateMap
& state_map
) {
129 std::set
<std::string
> malware
;
130 for (Blacklist::BlacklistStateMap::const_iterator it
= state_map
.begin();
131 it
!= state_map
.end(); ++it
) {
132 // TODO(oleg): UNKNOWN is treated as MALWARE for backwards compatibility.
133 // In future GetMalwareIDs will be removed and the caller will have to
134 // deal with BLACKLISTED_UNKNOWN state returned from GetBlacklistedIDs.
135 if (it
->second
== BLACKLISTED_MALWARE
|| it
->second
== BLACKLISTED_UNKNOWN
)
136 malware
.insert(it
->first
);
138 callback
.Run(malware
);
143 Blacklist::Observer::Observer(Blacklist
* blacklist
) : blacklist_(blacklist
) {
144 blacklist_
->AddObserver(this);
147 Blacklist::Observer::~Observer() {
148 blacklist_
->RemoveObserver(this);
151 Blacklist::ScopedDatabaseManagerForTest::ScopedDatabaseManagerForTest(
152 scoped_refptr
<SafeBrowsingDatabaseManager
> database_manager
)
153 : original_(GetDatabaseManager()) {
154 SetDatabaseManager(database_manager
);
157 Blacklist::ScopedDatabaseManagerForTest::~ScopedDatabaseManagerForTest() {
158 SetDatabaseManager(original_
);
161 Blacklist::Blacklist(ExtensionPrefs
* prefs
) {
162 scoped_refptr
<SafeBrowsingDatabaseManager
> database_manager
=
163 g_database_manager
.Get().get();
164 if (database_manager
.get()) {
167 chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE
,
168 content::Source
<SafeBrowsingDatabaseManager
>(database_manager
.get()));
171 // Clear out the old prefs-backed blacklist, stored as empty extension entries
172 // with just a "blacklisted" property.
174 // TODO(kalman): Delete this block of code, see http://crbug.com/295882.
175 std::set
<std::string
> blacklisted
= prefs
->GetBlacklistedExtensions();
176 for (std::set
<std::string
>::iterator it
= blacklisted
.begin();
177 it
!= blacklisted
.end(); ++it
) {
178 if (!prefs
->GetInstalledExtensionInfo(*it
))
179 prefs
->DeleteExtensionPrefs(*it
);
183 Blacklist::~Blacklist() {
187 Blacklist
* Blacklist::Get(content::BrowserContext
* context
) {
188 return BlacklistFactory::GetForBrowserContext(context
);
191 void Blacklist::GetBlacklistedIDs(const std::set
<std::string
>& ids
,
192 const GetBlacklistedIDsCallback
& callback
) {
193 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
195 if (ids
.empty() || !g_database_manager
.Get().get().get()) {
196 base::ThreadTaskRunnerHandle::Get()->PostTask(
197 FROM_HERE
, base::Bind(callback
, BlacklistStateMap()));
201 // Constructing the SafeBrowsingClientImpl begins the process of asking
202 // safebrowsing for the blacklisted extensions. The set of blacklisted
203 // extensions returned by SafeBrowsing will then be passed to
204 // GetBlacklistStateIDs to get the particular BlacklistState for each id.
205 new SafeBrowsingClientImpl(
206 ids
, base::Bind(&Blacklist::GetBlacklistStateForIDs
, AsWeakPtr(),
210 void Blacklist::GetMalwareIDs(const std::set
<std::string
>& ids
,
211 const GetMalwareIDsCallback
& callback
) {
212 GetBlacklistedIDs(ids
, base::Bind(&GetMalwareFromBlacklistStateMap
,
217 void Blacklist::IsBlacklisted(const std::string
& extension_id
,
218 const IsBlacklistedCallback
& callback
) {
219 std::set
<std::string
> check
;
220 check
.insert(extension_id
);
221 GetBlacklistedIDs(check
, base::Bind(&CheckOneExtensionState
, callback
));
224 void Blacklist::GetBlacklistStateForIDs(
225 const GetBlacklistedIDsCallback
& callback
,
226 const std::set
<std::string
>& blacklisted_ids
) {
227 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
229 std::set
<std::string
> ids_unknown_state
;
230 BlacklistStateMap extensions_state
;
231 for (std::set
<std::string
>::const_iterator it
= blacklisted_ids
.begin();
232 it
!= blacklisted_ids
.end(); ++it
) {
233 BlacklistStateMap::const_iterator cache_it
=
234 blacklist_state_cache_
.find(*it
);
235 if (cache_it
== blacklist_state_cache_
.end() ||
236 cache_it
->second
== BLACKLISTED_UNKNOWN
) // Do not return UNKNOWN
237 // from cache, retry request.
238 ids_unknown_state
.insert(*it
);
240 extensions_state
[*it
] = cache_it
->second
;
243 if (ids_unknown_state
.empty()) {
244 callback
.Run(extensions_state
);
246 // After the extension blacklist states have been downloaded, call this
247 // functions again, but prevent infinite cycle in case server is offline
248 // or some other reason prevents us from receiving the blacklist state for
250 RequestExtensionsBlacklistState(
252 base::Bind(&Blacklist::ReturnBlacklistStateMap
, AsWeakPtr(),
253 callback
, blacklisted_ids
));
257 void Blacklist::ReturnBlacklistStateMap(
258 const GetBlacklistedIDsCallback
& callback
,
259 const std::set
<std::string
>& blacklisted_ids
) {
260 BlacklistStateMap extensions_state
;
261 for (std::set
<std::string
>::const_iterator it
= blacklisted_ids
.begin();
262 it
!= blacklisted_ids
.end(); ++it
) {
263 BlacklistStateMap::const_iterator cache_it
=
264 blacklist_state_cache_
.find(*it
);
265 if (cache_it
!= blacklist_state_cache_
.end())
266 extensions_state
[*it
] = cache_it
->second
;
267 // If for some reason we still haven't cached the state of this extension,
268 // we silently skip it.
271 callback
.Run(extensions_state
);
274 void Blacklist::RequestExtensionsBlacklistState(
275 const std::set
<std::string
>& ids
, const base::Callback
<void()>& callback
) {
276 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
278 state_fetcher_
.reset(new BlacklistStateFetcher());
280 state_requests_
.push_back(
281 make_pair(std::vector
<std::string
>(ids
.begin(), ids
.end()), callback
));
282 for (std::set
<std::string
>::const_iterator it
= ids
.begin();
285 state_fetcher_
->Request(
287 base::Bind(&Blacklist::OnBlacklistStateReceived
, AsWeakPtr(), *it
));
291 void Blacklist::OnBlacklistStateReceived(const std::string
& id
,
292 BlacklistState state
) {
293 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
294 blacklist_state_cache_
[id
] = state
;
296 // Go through the opened requests and call the callbacks for those requests
297 // for which we already got all the required blacklist states.
298 StateRequestsList::iterator requests_it
= state_requests_
.begin();
299 while (requests_it
!= state_requests_
.end()) {
300 const std::vector
<std::string
>& ids
= requests_it
->first
;
302 bool have_all_in_cache
= true;
303 for (std::vector
<std::string
>::const_iterator ids_it
= ids
.begin();
306 if (!ContainsKey(blacklist_state_cache_
, *ids_it
)) {
307 have_all_in_cache
= false;
312 if (have_all_in_cache
) {
313 requests_it
->second
.Run();
314 requests_it
= state_requests_
.erase(requests_it
); // returns next element
321 void Blacklist::SetBlacklistStateFetcherForTest(
322 BlacklistStateFetcher
* fetcher
) {
323 state_fetcher_
.reset(fetcher
);
326 BlacklistStateFetcher
* Blacklist::ResetBlacklistStateFetcherForTest() {
327 return state_fetcher_
.release();
330 void Blacklist::AddObserver(Observer
* observer
) {
331 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
332 observers_
.AddObserver(observer
);
335 void Blacklist::RemoveObserver(Observer
* observer
) {
336 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
337 observers_
.RemoveObserver(observer
);
341 void Blacklist::SetDatabaseManager(
342 scoped_refptr
<SafeBrowsingDatabaseManager
> database_manager
) {
343 g_database_manager
.Get().set(database_manager
);
347 scoped_refptr
<SafeBrowsingDatabaseManager
> Blacklist::GetDatabaseManager() {
348 return g_database_manager
.Get().get();
351 void Blacklist::Observe(int type
,
352 const content::NotificationSource
& source
,
353 const content::NotificationDetails
& details
) {
354 DCHECK_EQ(chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE
, type
);
355 FOR_EACH_OBSERVER(Observer
, observers_
, OnBlacklistUpdated());
358 } // namespace extensions