Home | History | Annotate | Download | only in extensions
      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/safe_browsing_service.h"
     17 #include "chrome/browser/safe_browsing/safe_browsing_util.h"
     18 #include "chrome/common/pref_names.h"
     19 #include "content/public/browser/notification_details.h"
     20 #include "content/public/browser/notification_source.h"
     21 
     22 using content::BrowserThread;
     23 
     24 namespace extensions {
     25 
     26 namespace {
     27 
     28 // The safe browsing database manager to use. Make this a global/static variable
     29 // rather than a member of Blacklist because Blacklist accesses the real
     30 // database manager before it has a chance to get a fake one.
     31 class LazySafeBrowsingDatabaseManager {
     32  public:
     33   LazySafeBrowsingDatabaseManager() {
     34 #if defined(FULL_SAFE_BROWSING) || defined(MOBILE_SAFE_BROWSING)
     35     if (g_browser_process && g_browser_process->safe_browsing_service()) {
     36       instance_ =
     37           g_browser_process->safe_browsing_service()->database_manager();
     38     }
     39 #endif
     40   }
     41 
     42   scoped_refptr<SafeBrowsingDatabaseManager> get() {
     43     return instance_;
     44   }
     45 
     46   void set(scoped_refptr<SafeBrowsingDatabaseManager> instance) {
     47     instance_ = instance;
     48   }
     49 
     50  private:
     51   scoped_refptr<SafeBrowsingDatabaseManager> instance_;
     52 };
     53 
     54 static base::LazyInstance<LazySafeBrowsingDatabaseManager> g_database_manager =
     55     LAZY_INSTANCE_INITIALIZER;
     56 
     57 // Implementation of SafeBrowsingDatabaseManager::Client, the class which is
     58 // called back from safebrowsing queries.
     59 //
     60 // Constructed on any thread but lives on the IO from then on.
     61 class SafeBrowsingClientImpl
     62     : public SafeBrowsingDatabaseManager::Client,
     63       public base::RefCountedThreadSafe<SafeBrowsingClientImpl> {
     64  public:
     65   typedef base::Callback<void(const std::set<std::string>&)> OnResultCallback;
     66 
     67   // Constructs a client to query the database manager for |extension_ids| and
     68   // run |callback| with the IDs of those which have been blacklisted.
     69   SafeBrowsingClientImpl(
     70       const std::set<std::string>& extension_ids,
     71       const OnResultCallback& callback)
     72       : callback_message_loop_(base::MessageLoopProxy::current()),
     73         callback_(callback) {
     74     BrowserThread::PostTask(
     75         BrowserThread::IO,
     76         FROM_HERE,
     77         base::Bind(&SafeBrowsingClientImpl::StartCheck, this,
     78                    g_database_manager.Get().get(),
     79                    extension_ids));
     80   }
     81 
     82  private:
     83   friend class base::RefCountedThreadSafe<SafeBrowsingClientImpl>;
     84   virtual ~SafeBrowsingClientImpl() {}
     85 
     86   // Pass |database_manager| as a parameter to avoid touching
     87   // SafeBrowsingService on the IO thread.
     88   void StartCheck(scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
     89                   const std::set<std::string>& extension_ids) {
     90     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     91     if (database_manager->CheckExtensionIDs(extension_ids, this)) {
     92       // Definitely not blacklisted. Callback immediately.
     93       callback_message_loop_->PostTask(
     94           FROM_HERE,
     95           base::Bind(callback_, std::set<std::string>()));
     96       return;
     97     }
     98     // Something might be blacklisted, response will come in
     99     // OnCheckExtensionsResult.
    100     AddRef();  // Balanced in OnCheckExtensionsResult
    101   }
    102 
    103   virtual void OnCheckExtensionsResult(
    104       const std::set<std::string>& hits) OVERRIDE {
    105     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    106     callback_message_loop_->PostTask(FROM_HERE, base::Bind(callback_, hits));
    107     Release();  // Balanced in StartCheck.
    108   }
    109 
    110   scoped_refptr<base::MessageLoopProxy> callback_message_loop_;
    111   OnResultCallback callback_;
    112 
    113   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingClientImpl);
    114 };
    115 
    116 void IsNotEmpty(const Blacklist::IsBlacklistedCallback& callback,
    117                 const std::set<std::string>& set) {
    118   callback.Run(set.empty() ? Blacklist::NOT_BLACKLISTED
    119                            : Blacklist::BLACKLISTED);
    120 }
    121 
    122 }  // namespace
    123 
    124 Blacklist::Observer::Observer(Blacklist* blacklist) : blacklist_(blacklist) {
    125   blacklist_->AddObserver(this);
    126 }
    127 
    128 Blacklist::Observer::~Observer() {
    129   blacklist_->RemoveObserver(this);
    130 }
    131 
    132 Blacklist::ScopedDatabaseManagerForTest::ScopedDatabaseManagerForTest(
    133     scoped_refptr<SafeBrowsingDatabaseManager> database_manager)
    134     : original_(GetDatabaseManager()) {
    135   SetDatabaseManager(database_manager);
    136 }
    137 
    138 Blacklist::ScopedDatabaseManagerForTest::~ScopedDatabaseManagerForTest() {
    139   SetDatabaseManager(original_);
    140 }
    141 
    142 Blacklist::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 
    160 Blacklist::~Blacklist() {
    161 }
    162 
    163 void 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 
    198 void 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 
    205 void 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 
    246 void Blacklist::AddObserver(Observer* observer) {
    247   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    248   observers_.AddObserver(observer);
    249 }
    250 
    251 void Blacklist::RemoveObserver(Observer* observer) {
    252   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    253   observers_.RemoveObserver(observer);
    254 }
    255 
    256 // static
    257 void Blacklist::SetDatabaseManager(
    258     scoped_refptr<SafeBrowsingDatabaseManager> database_manager) {
    259   g_database_manager.Get().set(database_manager);
    260 }
    261 
    262 // static
    263 scoped_refptr<SafeBrowsingDatabaseManager> Blacklist::GetDatabaseManager() {
    264   return g_database_manager.Get().get();
    265 }
    266 
    267 void 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 
    280 void 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