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