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/blacklist_state_fetcher.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 #include "extensions/browser/extension_prefs.h"
     24 
     25 using content::BrowserThread;
     26 
     27 namespace extensions {
     28 
     29 namespace {
     30 
     31 // The safe browsing database manager to use. Make this a global/static variable
     32 // rather than a member of Blacklist because Blacklist accesses the real
     33 // database manager before it has a chance to get a fake one.
     34 class LazySafeBrowsingDatabaseManager {
     35  public:
     36   LazySafeBrowsingDatabaseManager() {
     37 #if defined(FULL_SAFE_BROWSING) || defined(MOBILE_SAFE_BROWSING)
     38     if (g_browser_process && g_browser_process->safe_browsing_service()) {
     39       instance_ =
     40           g_browser_process->safe_browsing_service()->database_manager();
     41     }
     42 #endif
     43   }
     44 
     45   scoped_refptr<SafeBrowsingDatabaseManager> get() {
     46     return instance_;
     47   }
     48 
     49   void set(scoped_refptr<SafeBrowsingDatabaseManager> instance) {
     50     instance_ = instance;
     51   }
     52 
     53  private:
     54   scoped_refptr<SafeBrowsingDatabaseManager> instance_;
     55 };
     56 
     57 static base::LazyInstance<LazySafeBrowsingDatabaseManager> g_database_manager =
     58     LAZY_INSTANCE_INITIALIZER;
     59 
     60 // Implementation of SafeBrowsingDatabaseManager::Client, the class which is
     61 // called back from safebrowsing queries.
     62 //
     63 // Constructed on any thread but lives on the IO from then on.
     64 class SafeBrowsingClientImpl
     65     : public SafeBrowsingDatabaseManager::Client,
     66       public base::RefCountedThreadSafe<SafeBrowsingClientImpl> {
     67  public:
     68   typedef base::Callback<void(const std::set<std::string>&)> OnResultCallback;
     69 
     70   // Constructs a client to query the database manager for |extension_ids| and
     71   // run |callback| with the IDs of those which have been blacklisted.
     72   SafeBrowsingClientImpl(
     73       const std::set<std::string>& extension_ids,
     74       const OnResultCallback& callback)
     75       : callback_message_loop_(base::MessageLoopProxy::current()),
     76         callback_(callback) {
     77     BrowserThread::PostTask(
     78         BrowserThread::IO,
     79         FROM_HERE,
     80         base::Bind(&SafeBrowsingClientImpl::StartCheck, this,
     81                    g_database_manager.Get().get(),
     82                    extension_ids));
     83   }
     84 
     85  private:
     86   friend class base::RefCountedThreadSafe<SafeBrowsingClientImpl>;
     87   virtual ~SafeBrowsingClientImpl() {}
     88 
     89   // Pass |database_manager| as a parameter to avoid touching
     90   // SafeBrowsingService on the IO thread.
     91   void StartCheck(scoped_refptr<SafeBrowsingDatabaseManager> database_manager,
     92                   const std::set<std::string>& extension_ids) {
     93     DCHECK_CURRENTLY_ON(BrowserThread::IO);
     94     if (database_manager->CheckExtensionIDs(extension_ids, this)) {
     95       // Definitely not blacklisted. Callback immediately.
     96       callback_message_loop_->PostTask(
     97           FROM_HERE,
     98           base::Bind(callback_, std::set<std::string>()));
     99       return;
    100     }
    101     // Something might be blacklisted, response will come in
    102     // OnCheckExtensionsResult.
    103     AddRef();  // Balanced in OnCheckExtensionsResult
    104   }
    105 
    106   virtual void OnCheckExtensionsResult(
    107       const std::set<std::string>& hits) OVERRIDE {
    108     DCHECK_CURRENTLY_ON(BrowserThread::IO);
    109     callback_message_loop_->PostTask(FROM_HERE, base::Bind(callback_, hits));
    110     Release();  // Balanced in StartCheck.
    111   }
    112 
    113   scoped_refptr<base::MessageLoopProxy> callback_message_loop_;
    114   OnResultCallback callback_;
    115 
    116   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingClientImpl);
    117 };
    118 
    119 void CheckOneExtensionState(
    120     const Blacklist::IsBlacklistedCallback& callback,
    121     const Blacklist::BlacklistStateMap& state_map) {
    122   callback.Run(state_map.empty() ? NOT_BLACKLISTED : 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     // TODO(oleg): UNKNOWN is treated as MALWARE for backwards compatibility.
    132     // In future GetMalwareIDs will be removed and the caller will have to
    133     // deal with BLACKLISTED_UNKNOWN state returned from GetBlacklistedIDs.
    134     if (it->second == BLACKLISTED_MALWARE || it->second == BLACKLISTED_UNKNOWN)
    135       malware.insert(it->first);
    136   }
    137   callback.Run(malware);
    138 }
    139 
    140 }  // namespace
    141 
    142 Blacklist::Observer::Observer(Blacklist* blacklist) : blacklist_(blacklist) {
    143   blacklist_->AddObserver(this);
    144 }
    145 
    146 Blacklist::Observer::~Observer() {
    147   blacklist_->RemoveObserver(this);
    148 }
    149 
    150 Blacklist::ScopedDatabaseManagerForTest::ScopedDatabaseManagerForTest(
    151     scoped_refptr<SafeBrowsingDatabaseManager> database_manager)
    152     : original_(GetDatabaseManager()) {
    153   SetDatabaseManager(database_manager);
    154 }
    155 
    156 Blacklist::ScopedDatabaseManagerForTest::~ScopedDatabaseManagerForTest() {
    157   SetDatabaseManager(original_);
    158 }
    159 
    160 Blacklist::Blacklist(ExtensionPrefs* prefs) {
    161   scoped_refptr<SafeBrowsingDatabaseManager> database_manager =
    162       g_database_manager.Get().get();
    163   if (database_manager.get()) {
    164     registrar_.Add(
    165         this,
    166         chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE,
    167         content::Source<SafeBrowsingDatabaseManager>(database_manager.get()));
    168   }
    169 
    170   // Clear out the old prefs-backed blacklist, stored as empty extension entries
    171   // with just a "blacklisted" property.
    172   //
    173   // TODO(kalman): Delete this block of code, see http://crbug.com/295882.
    174   std::set<std::string> blacklisted = prefs->GetBlacklistedExtensions();
    175   for (std::set<std::string>::iterator it = blacklisted.begin();
    176        it != blacklisted.end(); ++it) {
    177     if (!prefs->GetInstalledExtensionInfo(*it))
    178       prefs->DeleteExtensionPrefs(*it);
    179   }
    180 }
    181 
    182 Blacklist::~Blacklist() {
    183 }
    184 
    185 void Blacklist::GetBlacklistedIDs(const std::set<std::string>& ids,
    186                                   const GetBlacklistedIDsCallback& callback) {
    187   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    188 
    189   if (ids.empty() || !g_database_manager.Get().get().get()) {
    190     base::MessageLoopProxy::current()->PostTask(
    191         FROM_HERE, base::Bind(callback, BlacklistStateMap()));
    192     return;
    193   }
    194 
    195   // Constructing the SafeBrowsingClientImpl begins the process of asking
    196   // safebrowsing for the blacklisted extensions. The set of blacklisted
    197   // extensions returned by SafeBrowsing will then be passed to
    198   // GetBlacklistStateIDs to get the particular BlacklistState for each id.
    199   new SafeBrowsingClientImpl(
    200       ids, base::Bind(&Blacklist::GetBlacklistStateForIDs, AsWeakPtr(),
    201                       callback));
    202 }
    203 
    204 void Blacklist::GetMalwareIDs(const std::set<std::string>& ids,
    205                               const GetMalwareIDsCallback& callback) {
    206   GetBlacklistedIDs(ids, base::Bind(&GetMalwareFromBlacklistStateMap,
    207                                     callback));
    208 }
    209 
    210 
    211 void Blacklist::IsBlacklisted(const std::string& extension_id,
    212                               const IsBlacklistedCallback& callback) {
    213   std::set<std::string> check;
    214   check.insert(extension_id);
    215   GetBlacklistedIDs(check, base::Bind(&CheckOneExtensionState, callback));
    216 }
    217 
    218 void Blacklist::GetBlacklistStateForIDs(
    219     const GetBlacklistedIDsCallback& callback,
    220     const std::set<std::string>& blacklisted_ids) {
    221   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    222 
    223   std::set<std::string> ids_unknown_state;
    224   BlacklistStateMap extensions_state;
    225   for (std::set<std::string>::const_iterator it = blacklisted_ids.begin();
    226        it != blacklisted_ids.end(); ++it) {
    227     BlacklistStateMap::const_iterator cache_it =
    228         blacklist_state_cache_.find(*it);
    229     if (cache_it == blacklist_state_cache_.end() ||
    230         cache_it->second == BLACKLISTED_UNKNOWN)  // Do not return UNKNOWN
    231                                                   // from cache, retry request.
    232       ids_unknown_state.insert(*it);
    233     else
    234       extensions_state[*it] = cache_it->second;
    235   }
    236 
    237   if (ids_unknown_state.empty()) {
    238     callback.Run(extensions_state);
    239   } else {
    240     // After the extension blacklist states have been downloaded, call this
    241     // functions again, but prevent infinite cycle in case server is offline
    242     // or some other reason prevents us from receiving the blacklist state for
    243     // these extensions.
    244     RequestExtensionsBlacklistState(
    245         ids_unknown_state,
    246         base::Bind(&Blacklist::ReturnBlacklistStateMap, AsWeakPtr(),
    247                    callback, blacklisted_ids));
    248   }
    249 }
    250 
    251 void Blacklist::ReturnBlacklistStateMap(
    252     const GetBlacklistedIDsCallback& callback,
    253     const std::set<std::string>& blacklisted_ids) {
    254   BlacklistStateMap extensions_state;
    255   for (std::set<std::string>::const_iterator it = blacklisted_ids.begin();
    256        it != blacklisted_ids.end(); ++it) {
    257     BlacklistStateMap::const_iterator cache_it =
    258         blacklist_state_cache_.find(*it);
    259     if (cache_it != blacklist_state_cache_.end())
    260       extensions_state[*it] = cache_it->second;
    261     // If for some reason we still haven't cached the state of this extension,
    262     // we silently skip it.
    263   }
    264 
    265   callback.Run(extensions_state);
    266 }
    267 
    268 void Blacklist::RequestExtensionsBlacklistState(
    269     const std::set<std::string>& ids, const base::Callback<void()>& callback) {
    270   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    271   if (!state_fetcher_)
    272     state_fetcher_.reset(new BlacklistStateFetcher());
    273 
    274   state_requests_.push_back(
    275       make_pair(std::vector<std::string>(ids.begin(), ids.end()), callback));
    276   for (std::set<std::string>::const_iterator it = ids.begin();
    277        it != ids.end();
    278        ++it) {
    279     state_fetcher_->Request(
    280         *it,
    281         base::Bind(&Blacklist::OnBlacklistStateReceived, AsWeakPtr(), *it));
    282   }
    283 }
    284 
    285 void Blacklist::OnBlacklistStateReceived(const std::string& id,
    286                                          BlacklistState state) {
    287   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    288   blacklist_state_cache_[id] = state;
    289 
    290   // Go through the opened requests and call the callbacks for those requests
    291   // for which we already got all the required blacklist states.
    292   StateRequestsList::iterator requests_it = state_requests_.begin();
    293   while (requests_it != state_requests_.end()) {
    294     const std::vector<std::string>& ids = requests_it->first;
    295 
    296     bool have_all_in_cache = true;
    297     for (std::vector<std::string>::const_iterator ids_it = ids.begin();
    298          ids_it != ids.end();
    299          ++ids_it) {
    300       if (!ContainsKey(blacklist_state_cache_, *ids_it)) {
    301         have_all_in_cache = false;
    302         break;
    303       }
    304     }
    305 
    306     if (have_all_in_cache) {
    307       requests_it->second.Run();
    308       requests_it = state_requests_.erase(requests_it); // returns next element
    309     } else {
    310       ++requests_it;
    311     }
    312   }
    313 }
    314 
    315 void Blacklist::SetBlacklistStateFetcherForTest(
    316     BlacklistStateFetcher* fetcher) {
    317   state_fetcher_.reset(fetcher);
    318 }
    319 
    320 BlacklistStateFetcher* Blacklist::ResetBlacklistStateFetcherForTest() {
    321   return state_fetcher_.release();
    322 }
    323 
    324 void Blacklist::AddObserver(Observer* observer) {
    325   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    326   observers_.AddObserver(observer);
    327 }
    328 
    329 void Blacklist::RemoveObserver(Observer* observer) {
    330   DCHECK_CURRENTLY_ON(BrowserThread::UI);
    331   observers_.RemoveObserver(observer);
    332 }
    333 
    334 // static
    335 void Blacklist::SetDatabaseManager(
    336     scoped_refptr<SafeBrowsingDatabaseManager> database_manager) {
    337   g_database_manager.Get().set(database_manager);
    338 }
    339 
    340 // static
    341 scoped_refptr<SafeBrowsingDatabaseManager> Blacklist::GetDatabaseManager() {
    342   return g_database_manager.Get().get();
    343 }
    344 
    345 void Blacklist::Observe(int type,
    346                         const content::NotificationSource& source,
    347                         const content::NotificationDetails& details) {
    348   DCHECK_EQ(chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE, type);
    349   FOR_EACH_OBSERVER(Observer, observers_, OnBlacklistUpdated());
    350 }
    351 
    352 }  // namespace extensions
    353