• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/extensions/blacklist.h"
6 
7 #include <algorithm>
8 #include <iterator>
9 
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/stl_util.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/extensions/extension_prefs.h"
18 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
19 #include "chrome/browser/safe_browsing/safe_browsing_util.h"
20 #include "chrome/common/pref_names.h"
21 #include "content/public/browser/notification_details.h"
22 #include "content/public/browser/notification_source.h"
23 
24 using content::BrowserThread;
25 
26 namespace extensions {
27 
28 namespace {
29 
30 // The safe browsing database manager to use. Make this a global/static variable
31 // rather than a member of Blacklist because Blacklist accesses the real
32 // database manager before it has a chance to get a fake one.
33 class LazySafeBrowsingDatabaseManager {
34  public:
LazySafeBrowsingDatabaseManager()35   LazySafeBrowsingDatabaseManager() {
36 #if defined(FULL_SAFE_BROWSING) || defined(MOBILE_SAFE_BROWSING)
37     if (g_browser_process && g_browser_process->safe_browsing_service()) {
38       instance_ =
39           g_browser_process->safe_browsing_service()->database_manager();
40     }
41 #endif
42   }
43 
get()44   scoped_refptr<SafeBrowsingDatabaseManager> get() {
45     return instance_;
46   }
47 
set(scoped_refptr<SafeBrowsingDatabaseManager> instance)48   void set(scoped_refptr<SafeBrowsingDatabaseManager> instance) {
49     instance_ = instance;
50   }
51 
52  private:
53   scoped_refptr<SafeBrowsingDatabaseManager> instance_;
54 };
55 
56 static base::LazyInstance<LazySafeBrowsingDatabaseManager> g_database_manager =
57     LAZY_INSTANCE_INITIALIZER;
58 
59 // Implementation of SafeBrowsingDatabaseManager::Client, the class which is
60 // called back from safebrowsing queries.
61 //
62 // Constructed on any thread but lives on the IO from then on.
63 class SafeBrowsingClientImpl
64     : public SafeBrowsingDatabaseManager::Client,
65       public base::RefCountedThreadSafe<SafeBrowsingClientImpl> {
66  public:
67   typedef base::Callback<void(const std::set<std::string>&)> OnResultCallback;
68 
69   // Constructs a client to query the database manager for |extension_ids| and
70   // run |callback| with the IDs of those which have been blacklisted.
SafeBrowsingClientImpl(const std::set<std::string> & extension_ids,const OnResultCallback & callback)71   SafeBrowsingClientImpl(
72       const std::set<std::string>& extension_ids,
73       const OnResultCallback& callback)
74       : callback_message_loop_(base::MessageLoopProxy::current()),
75         callback_(callback) {
76     BrowserThread::PostTask(
77         BrowserThread::IO,
78         FROM_HERE,
79         base::Bind(&SafeBrowsingClientImpl::StartCheck, this,
80                    g_database_manager.Get().get(),
81                    extension_ids));
82   }
83 
84  private:
85   friend class base::RefCountedThreadSafe<SafeBrowsingClientImpl>;
~SafeBrowsingClientImpl()86   virtual ~SafeBrowsingClientImpl() {}
87 
88   // Pass |database_manager| as a parameter to avoid touching
89   // SafeBrowsingService on the IO thread.
StartCheck(scoped_refptr<SafeBrowsingDatabaseManager> database_manager,const std::set<std::string> & extension_ids)90   void StartCheck(scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
91                   const std::set<std::string>& extension_ids) {
92     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
93     if (database_manager->CheckExtensionIDs(extension_ids, this)) {
94       // Definitely not blacklisted. Callback immediately.
95       callback_message_loop_->PostTask(
96           FROM_HERE,
97           base::Bind(callback_, std::set<std::string>()));
98       return;
99     }
100     // Something might be blacklisted, response will come in
101     // OnCheckExtensionsResult.
102     AddRef();  // Balanced in OnCheckExtensionsResult
103   }
104 
OnCheckExtensionsResult(const std::set<std::string> & hits)105   virtual void OnCheckExtensionsResult(
106       const std::set<std::string>& hits) OVERRIDE {
107     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
108     callback_message_loop_->PostTask(FROM_HERE, base::Bind(callback_, hits));
109     Release();  // Balanced in StartCheck.
110   }
111 
112   scoped_refptr<base::MessageLoopProxy> callback_message_loop_;
113   OnResultCallback callback_;
114 
115   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingClientImpl);
116 };
117 
CheckOneExtensionState(const Blacklist::IsBlacklistedCallback & callback,const Blacklist::BlacklistStateMap & state_map)118 void CheckOneExtensionState(
119     const Blacklist::IsBlacklistedCallback& callback,
120     const Blacklist::BlacklistStateMap& state_map) {
121   callback.Run(state_map.empty() ? Blacklist::NOT_BLACKLISTED
122                                  : state_map.begin()->second);
123 }
124 
GetMalwareFromBlacklistStateMap(const Blacklist::GetMalwareIDsCallback & callback,const Blacklist::BlacklistStateMap & state_map)125 void GetMalwareFromBlacklistStateMap(
126     const Blacklist::GetMalwareIDsCallback& callback,
127     const Blacklist::BlacklistStateMap& state_map) {
128   std::set<std::string> malware;
129   for (Blacklist::BlacklistStateMap::const_iterator it = state_map.begin();
130        it != state_map.end(); ++it) {
131     if (it->second == Blacklist::BLACKLISTED_MALWARE)
132       malware.insert(it->first);
133   }
134   callback.Run(malware);
135 }
136 
137 }  // namespace
138 
Observer(Blacklist * blacklist)139 Blacklist::Observer::Observer(Blacklist* blacklist) : blacklist_(blacklist) {
140   blacklist_->AddObserver(this);
141 }
142 
~Observer()143 Blacklist::Observer::~Observer() {
144   blacklist_->RemoveObserver(this);
145 }
146 
ScopedDatabaseManagerForTest(scoped_refptr<SafeBrowsingDatabaseManager> database_manager)147 Blacklist::ScopedDatabaseManagerForTest::ScopedDatabaseManagerForTest(
148     scoped_refptr<SafeBrowsingDatabaseManager> database_manager)
149     : original_(GetDatabaseManager()) {
150   SetDatabaseManager(database_manager);
151 }
152 
~ScopedDatabaseManagerForTest()153 Blacklist::ScopedDatabaseManagerForTest::~ScopedDatabaseManagerForTest() {
154   SetDatabaseManager(original_);
155 }
156 
Blacklist(ExtensionPrefs * prefs)157 Blacklist::Blacklist(ExtensionPrefs* prefs) {
158   scoped_refptr<SafeBrowsingDatabaseManager> database_manager =
159       g_database_manager.Get().get();
160   if (database_manager) {
161     registrar_.Add(
162         this,
163         chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
164         content::Source<SafeBrowsingDatabaseManager>(database_manager.get()));
165   }
166 
167   // Clear out the old prefs-backed blacklist, stored as empty extension entries
168   // with just a "blacklisted" property.
169   //
170   // TODO(kalman): Delete this block of code, see http://crbug.com/295882.
171   std::set<std::string> blacklisted = prefs->GetBlacklistedExtensions();
172   for (std::set<std::string>::iterator it = blacklisted.begin();
173        it != blacklisted.end(); ++it) {
174     if (!prefs->GetInstalledExtensionInfo(*it))
175       prefs->DeleteExtensionPrefs(*it);
176   }
177 }
178 
~Blacklist()179 Blacklist::~Blacklist() {
180 }
181 
GetBlacklistedIDs(const std::set<std::string> & ids,const GetBlacklistedIDsCallback & callback)182 void Blacklist::GetBlacklistedIDs(const std::set<std::string>& ids,
183                                   const GetBlacklistedIDsCallback& callback) {
184   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
185 
186   if (ids.empty() || !g_database_manager.Get().get().get()) {
187     base::MessageLoopProxy::current()->PostTask(
188         FROM_HERE, base::Bind(callback, BlacklistStateMap()));
189     return;
190   }
191 
192   // Constructing the SafeBrowsingClientImpl begins the process of asking
193   // safebrowsing for the blacklisted extensions. The set of blacklisted
194   // extensions returned by SafeBrowsing will then be passed to
195   // GetBlacklistStateIDs to get the particular BlacklistState for each id.
196   new SafeBrowsingClientImpl(
197       ids, base::Bind(&Blacklist::GetBlacklistStateForIDs, AsWeakPtr(),
198                       callback));
199 }
200 
GetMalwareIDs(const std::set<std::string> & ids,const GetMalwareIDsCallback & callback)201 void Blacklist::GetMalwareIDs(const std::set<std::string>& ids,
202                               const GetMalwareIDsCallback& callback) {
203   GetBlacklistedIDs(ids, base::Bind(&GetMalwareFromBlacklistStateMap,
204                                     callback));
205 }
206 
207 
IsBlacklisted(const std::string & extension_id,const IsBlacklistedCallback & callback)208 void Blacklist::IsBlacklisted(const std::string& extension_id,
209                               const IsBlacklistedCallback& callback) {
210   std::set<std::string> check;
211   check.insert(extension_id);
212   GetBlacklistedIDs(check, base::Bind(&CheckOneExtensionState, callback));
213 }
214 
GetBlacklistStateForIDs(const GetBlacklistedIDsCallback & callback,const std::set<std::string> & blacklisted_ids)215 void Blacklist::GetBlacklistStateForIDs(
216     const GetBlacklistedIDsCallback& callback,
217     const std::set<std::string>& blacklisted_ids) {
218   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
219 
220   std::set<std::string> ids_unknown_state;
221   BlacklistStateMap extensions_state;
222   for (std::set<std::string>::const_iterator it = blacklisted_ids.begin();
223        it != blacklisted_ids.end(); ++it) {
224     BlacklistStateMap::const_iterator cache_it =
225         blacklist_state_cache_.find(*it);
226     if (cache_it == blacklist_state_cache_.end())
227       ids_unknown_state.insert(*it);
228     else
229       extensions_state[*it] = cache_it->second;
230   }
231 
232   if (ids_unknown_state.empty()) {
233     callback.Run(extensions_state);
234   } else {
235     // After the extension blacklist states have been downloaded, call this
236     // functions again, but prevent infinite cycle in case server is offline
237     // or some other reason prevents us from receiving the blacklist state for
238     // these extensions.
239     RequestExtensionsBlacklistState(
240         ids_unknown_state,
241         base::Bind(&Blacklist::ReturnBlacklistStateMap, AsWeakPtr(),
242                    callback, blacklisted_ids));
243   }
244 }
245 
ReturnBlacklistStateMap(const GetBlacklistedIDsCallback & callback,const std::set<std::string> & blacklisted_ids)246 void Blacklist::ReturnBlacklistStateMap(
247     const GetBlacklistedIDsCallback& callback,
248     const std::set<std::string>& blacklisted_ids) {
249   BlacklistStateMap extensions_state;
250   for (std::set<std::string>::const_iterator it = blacklisted_ids.begin();
251        it != blacklisted_ids.end(); ++it) {
252     BlacklistStateMap::const_iterator cache_it =
253         blacklist_state_cache_.find(*it);
254     if (cache_it != blacklist_state_cache_.end())
255       extensions_state[*it] = cache_it->second;
256     // If for some reason we still haven't cached the state of this extension,
257     // we silently skip it.
258   }
259 
260   callback.Run(extensions_state);
261 }
262 
RequestExtensionsBlacklistState(const std::set<std::string> ids,base::Callback<void ()> callback)263 void Blacklist::RequestExtensionsBlacklistState(
264     const std::set<std::string> ids, base::Callback<void()> callback) {
265   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
266   // This is a stub. The request will be made here, but the server is not up
267   // yet. For compatibility with current blacklist logic, mark all extensions
268   // as malicious.
269   for (std::set<std::string>::const_iterator it = ids.begin();
270        it != ids.end();
271        ++it) {
272     blacklist_state_cache_[*it] = BLACKLISTED_MALWARE;
273   }
274   callback.Run();
275 }
276 
AddObserver(Observer * observer)277 void Blacklist::AddObserver(Observer* observer) {
278   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
279   observers_.AddObserver(observer);
280 }
281 
RemoveObserver(Observer * observer)282 void Blacklist::RemoveObserver(Observer* observer) {
283   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
284   observers_.RemoveObserver(observer);
285 }
286 
287 // static
SetDatabaseManager(scoped_refptr<SafeBrowsingDatabaseManager> database_manager)288 void Blacklist::SetDatabaseManager(
289     scoped_refptr<SafeBrowsingDatabaseManager> database_manager) {
290   g_database_manager.Get().set(database_manager);
291 }
292 
293 // static
GetDatabaseManager()294 scoped_refptr<SafeBrowsingDatabaseManager> Blacklist::GetDatabaseManager() {
295   return g_database_manager.Get().get();
296 }
297 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)298 void Blacklist::Observe(int type,
299                         const content::NotificationSource& source,
300                         const content::NotificationDetails& details) {
301   DCHECK_EQ(chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE, type);
302   FOR_EACH_OBSERVER(Observer, observers_, OnBlacklistUpdated());
303 }
304 
305 }  // namespace extensions
306