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 #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:
     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 
     44   scoped_refptr<SafeBrowsingDatabaseManager> get() {
     45     return instance_;
     46   }
     47 
     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.
     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>;
     86   virtual ~SafeBrowsingClientImpl() {}
     87 
     88   // Pass |database_manager| as a parameter to avoid touching
     89   // SafeBrowsingService on the IO thread.
     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 
    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 
    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 
    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 
    139 Blacklist::Observer::Observer(Blacklist* blacklist) : blacklist_(blacklist) {
    140   blacklist_->AddObserver(this);
    141 }
    142 
    143 Blacklist::Observer::~Observer() {
    144   blacklist_->RemoveObserver(this);
    145 }
    146 
    147 Blacklist::ScopedDatabaseManagerForTest::ScopedDatabaseManagerForTest(
    148     scoped_refptr<SafeBrowsingDatabaseManager> database_manager)
    149     : original_(GetDatabaseManager()) {
    150   SetDatabaseManager(database_manager);
    151 }
    152 
    153 Blacklist::ScopedDatabaseManagerForTest::~ScopedDatabaseManagerForTest() {
    154   SetDatabaseManager(original_);
    155 }
    156 
    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 
    179 Blacklist::~Blacklist() {
    180 }
    181 
    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 
    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 
    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 
    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 
    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 
    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 
    277 void Blacklist::AddObserver(Observer* observer) {
    278   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    279   observers_.AddObserver(observer);
    280 }
    281 
    282 void Blacklist::RemoveObserver(Observer* observer) {
    283   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    284   observers_.RemoveObserver(observer);
    285 }
    286 
    287 // static
    288 void Blacklist::SetDatabaseManager(
    289     scoped_refptr<SafeBrowsingDatabaseManager> database_manager) {
    290   g_database_manager.Get().set(database_manager);
    291 }
    292 
    293 // static
    294 scoped_refptr<SafeBrowsingDatabaseManager> Blacklist::GetDatabaseManager() {
    295   return g_database_manager.Get().get();
    296 }
    297 
    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