blacklist.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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
9#include "base/bind.h"
10#include "base/lazy_instance.h"
11#include "base/memory/ref_counted.h"
12#include "base/prefs/pref_service.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/chrome_notification_types.h"
15#include "chrome/browser/extensions/extension_prefs.h"
16#include "chrome/browser/safe_browsing/database_manager.h"
17#include "chrome/browser/safe_browsing/safe_browsing_service.h"
18#include "chrome/browser/safe_browsing/safe_browsing_util.h"
19#include "chrome/common/pref_names.h"
20#include "content/public/browser/notification_details.h"
21#include "content/public/browser/notification_source.h"
22
23using content::BrowserThread;
24
25namespace extensions {
26
27namespace {
28
29// The safe browsing database manager to use. Make this a global/static variable
30// rather than a member of Blacklist because Blacklist accesses the real
31// database manager before it has a chance to get a fake one.
32class LazySafeBrowsingDatabaseManager {
33 public:
34  LazySafeBrowsingDatabaseManager() {
35#if defined(FULL_SAFE_BROWSING) || defined(MOBILE_SAFE_BROWSING)
36    if (g_browser_process && g_browser_process->safe_browsing_service()) {
37      instance_ =
38          g_browser_process->safe_browsing_service()->database_manager();
39    }
40#endif
41  }
42
43  scoped_refptr<SafeBrowsingDatabaseManager> get() {
44    return instance_;
45  }
46
47  void set(scoped_refptr<SafeBrowsingDatabaseManager> instance) {
48    instance_ = instance;
49  }
50
51 private:
52  scoped_refptr<SafeBrowsingDatabaseManager> instance_;
53};
54
55static base::LazyInstance<LazySafeBrowsingDatabaseManager> g_database_manager =
56    LAZY_INSTANCE_INITIALIZER;
57
58// Implementation of SafeBrowsingDatabaseManager::Client, the class which is
59// called back from safebrowsing queries.
60//
61// Constructed on any thread but lives on the IO from then on.
62class SafeBrowsingClientImpl
63    : public SafeBrowsingDatabaseManager::Client,
64      public base::RefCountedThreadSafe<SafeBrowsingClientImpl> {
65 public:
66  typedef base::Callback<void(const std::set<std::string>&)> OnResultCallback;
67
68  // Constructs a client to query the database manager for |extension_ids| and
69  // run |callback| with the IDs of those which have been blacklisted.
70  SafeBrowsingClientImpl(
71      const std::set<std::string>& extension_ids,
72      const OnResultCallback& callback)
73      : callback_message_loop_(base::MessageLoopProxy::current()),
74        callback_(callback) {
75    BrowserThread::PostTask(
76        BrowserThread::IO,
77        FROM_HERE,
78        base::Bind(&SafeBrowsingClientImpl::StartCheck, this,
79                   g_database_manager.Get().get(),
80                   extension_ids));
81  }
82
83 private:
84  friend class base::RefCountedThreadSafe<SafeBrowsingClientImpl>;
85  virtual ~SafeBrowsingClientImpl() {}
86
87  // Pass |database_manager| as a parameter to avoid touching
88  // SafeBrowsingService on the IO thread.
89  void StartCheck(scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
90                  const std::set<std::string>& extension_ids) {
91    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
92    if (database_manager->CheckExtensionIDs(extension_ids, this)) {
93      // Definitely not blacklisted. Callback immediately.
94      callback_message_loop_->PostTask(
95          FROM_HERE,
96          base::Bind(callback_, std::set<std::string>()));
97      return;
98    }
99    // Something might be blacklisted, response will come in
100    // OnCheckExtensionsResult.
101    AddRef();  // Balanced in OnCheckExtensionsResult
102  }
103
104  virtual void OnCheckExtensionsResult(
105      const std::set<std::string>& hits) OVERRIDE {
106    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
107    callback_message_loop_->PostTask(FROM_HERE, base::Bind(callback_, hits));
108    Release();  // Balanced in StartCheck.
109  }
110
111  scoped_refptr<base::MessageLoopProxy> callback_message_loop_;
112  OnResultCallback callback_;
113
114  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingClientImpl);
115};
116
117void IsNotEmpty(const Blacklist::IsBlacklistedCallback& callback,
118                const std::set<std::string>& set) {
119  callback.Run(!set.empty());
120}
121
122}  // namespace
123
124Blacklist::Observer::Observer(Blacklist* blacklist) : blacklist_(blacklist) {
125  blacklist_->AddObserver(this);
126}
127
128Blacklist::Observer::~Observer() {
129  blacklist_->RemoveObserver(this);
130}
131
132Blacklist::ScopedDatabaseManagerForTest::ScopedDatabaseManagerForTest(
133    scoped_refptr<SafeBrowsingDatabaseManager> database_manager)
134    : original_(GetDatabaseManager()) {
135  SetDatabaseManager(database_manager);
136}
137
138Blacklist::ScopedDatabaseManagerForTest::~ScopedDatabaseManagerForTest() {
139  SetDatabaseManager(original_);
140}
141
142Blacklist::Blacklist(ExtensionPrefs* prefs) : prefs_(prefs) {
143  scoped_refptr<SafeBrowsingDatabaseManager> database_manager =
144      g_database_manager.Get().get();
145  if (database_manager.get()) {
146    registrar_.Add(
147        this,
148        chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
149        content::Source<SafeBrowsingDatabaseManager>(database_manager.get()));
150  }
151
152  // TODO(kalman): Delete anything from the pref blacklist that is in the
153  // safebrowsing blacklist (of course, only entries for which the extension
154  // hasn't been installed).
155  //
156  // Or maybe just wait until we're able to delete the pref blacklist
157  // altogether (when we're sure it's a strict subset of the safebrowsing one).
158}
159
160Blacklist::~Blacklist() {
161}
162
163void Blacklist::GetBlacklistedIDs(const std::set<std::string>& ids,
164                                  const GetBlacklistedIDsCallback& callback) {
165  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
166
167  if (ids.empty()) {
168    base::MessageLoopProxy::current()->PostTask(
169        FROM_HERE,
170        base::Bind(callback, std::set<std::string>()));
171    return;
172  }
173
174  // The blacklisted IDs are the union of those blacklisted in prefs and
175  // those blacklisted from safe browsing.
176  std::set<std::string> pref_blacklisted_ids;
177  for (std::set<std::string>::const_iterator it = ids.begin();
178       it != ids.end(); ++it) {
179    if (prefs_->IsExtensionBlacklisted(*it))
180      pref_blacklisted_ids.insert(*it);
181  }
182
183  if (!g_database_manager.Get().get().get()) {
184    base::MessageLoopProxy::current()->PostTask(
185        FROM_HERE, base::Bind(callback, pref_blacklisted_ids));
186    return;
187  }
188
189  // Constructing the SafeBrowsingClientImpl begins the process of asking
190  // safebrowsing for the blacklisted extensions.
191  new SafeBrowsingClientImpl(
192      ids,
193      base::Bind(&Blacklist::OnSafeBrowsingResponse, AsWeakPtr(),
194                 pref_blacklisted_ids,
195                 callback));
196}
197
198void Blacklist::IsBlacklisted(const std::string& extension_id,
199                              const IsBlacklistedCallback& callback) {
200  std::set<std::string> check;
201  check.insert(extension_id);
202  GetBlacklistedIDs(check, base::Bind(&IsNotEmpty, callback));
203}
204
205void Blacklist::SetFromUpdater(const std::vector<std::string>& ids,
206                               const std::string& version) {
207  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
208
209  std::set<std::string> ids_as_set;
210  for (std::vector<std::string>::const_iterator it = ids.begin();
211       it != ids.end(); ++it) {
212    if (Extension::IdIsValid(*it))
213      ids_as_set.insert(*it);
214    else
215      LOG(WARNING) << "Got invalid extension ID \"" << *it << "\"";
216  }
217
218  std::set<std::string> from_prefs = prefs_->GetBlacklistedExtensions();
219
220  std::set<std::string> no_longer_blacklisted;
221  std::set_difference(from_prefs.begin(), from_prefs.end(),
222                      ids_as_set.begin(), ids_as_set.end(),
223                      std::inserter(no_longer_blacklisted,
224                                    no_longer_blacklisted.begin()));
225  std::set<std::string> not_yet_blacklisted;
226  std::set_difference(ids_as_set.begin(), ids_as_set.end(),
227                      from_prefs.begin(), from_prefs.end(),
228                      std::inserter(not_yet_blacklisted,
229                                    not_yet_blacklisted.begin()));
230
231  for (std::set<std::string>::iterator it = no_longer_blacklisted.begin();
232       it != no_longer_blacklisted.end(); ++it) {
233    prefs_->SetExtensionBlacklisted(*it, false);
234  }
235  for (std::set<std::string>::iterator it = not_yet_blacklisted.begin();
236       it != not_yet_blacklisted.end(); ++it) {
237    prefs_->SetExtensionBlacklisted(*it, true);
238  }
239
240  prefs_->pref_service()->SetString(prefs::kExtensionBlacklistUpdateVersion,
241                                    version);
242
243  FOR_EACH_OBSERVER(Observer, observers_, OnBlacklistUpdated());
244}
245
246void Blacklist::AddObserver(Observer* observer) {
247  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
248  observers_.AddObserver(observer);
249}
250
251void Blacklist::RemoveObserver(Observer* observer) {
252  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
253  observers_.RemoveObserver(observer);
254}
255
256// static
257void Blacklist::SetDatabaseManager(
258    scoped_refptr<SafeBrowsingDatabaseManager> database_manager) {
259  g_database_manager.Get().set(database_manager);
260}
261
262// static
263scoped_refptr<SafeBrowsingDatabaseManager> Blacklist::GetDatabaseManager() {
264  return g_database_manager.Get().get();
265}
266
267void Blacklist::OnSafeBrowsingResponse(
268    const std::set<std::string>& pref_blacklisted_ids,
269    const GetBlacklistedIDsCallback& callback,
270    const std::set<std::string>& safebrowsing_blacklisted_ids) {
271  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
272
273  std::set<std::string> blacklist = pref_blacklisted_ids;
274  blacklist.insert(safebrowsing_blacklisted_ids.begin(),
275                   safebrowsing_blacklisted_ids.end());
276
277  callback.Run(blacklist);
278}
279
280void Blacklist::Observe(int type,
281                        const content::NotificationSource& source,
282                        const content::NotificationDetails& details) {
283  DCHECK_EQ(chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE, type);
284  FOR_EACH_OBSERVER(Observer, observers_, OnBlacklistUpdated());
285}
286
287}  // namespace extensions
288