[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 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 | |
[email protected] | 695b571 | 2012-12-06 23:55:28 | [diff] [blame] | 7 | #include <algorithm> |
| 8 | |
| 9 | #include "base/bind.h" |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 10 | #include "base/lazy_instance.h" |
| 11 | #include "base/memory/ref_counted.h" |
[email protected] | 3853a4c | 2013-02-11 17:15:57 | [diff] [blame] | 12 | #include "base/prefs/pref_service.h" |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 13 | #include "chrome/browser/browser_process.h" |
[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 14 | #include "chrome/browser/extensions/extension_prefs.h" |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 15 | #include "chrome/browser/safe_browsing/database_manager.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/chrome_notification_types.h" |
[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 19 | #include "chrome/common/pref_names.h" |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 20 | #include "content/public/browser/notification_details.h" |
| 21 | #include "content/public/browser/notification_source.h" |
| 22 | |
| 23 | using content::BrowserThread; |
[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 24 | |
| 25 | namespace extensions { |
| 26 | |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 27 | namespace { |
| 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. |
| 32 | class LazySafeBrowsingDatabaseManager { |
| 33 | public: |
| 34 | LazySafeBrowsingDatabaseManager() { |
[email protected] | 04f7d829 | 2013-02-18 09:31:10 | [diff] [blame] | 35 | #if defined(FULL_SAFE_BROWSING) || defined(MOBILE_SAFE_BROWSING) |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 36 | if (g_browser_process && g_browser_process->safe_browsing_service()) { |
| 37 | instance_ = |
| 38 | g_browser_process->safe_browsing_service()->database_manager(); |
| 39 | } |
[email protected] | 04f7d829 | 2013-02-18 09:31:10 | [diff] [blame] | 40 | #endif |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 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 | |
| 55 | static 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. |
| 62 | class 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 | |
[email protected] | 49aeab6 | 2013-02-07 02:53:11 | [diff] [blame] | 104 | virtual void OnCheckExtensionsResult( |
| 105 | const std::set<std::string>& hits) OVERRIDE { |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 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 | |
[email protected] | bc151cf9 | 2013-02-12 04:57:26 | [diff] [blame] | 117 | void IsNotEmpty(const Blacklist::IsBlacklistedCallback& callback, |
| 118 | const std::set<std::string>& set) { |
| 119 | callback.Run(!set.empty()); |
| 120 | } |
| 121 | |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 122 | } // namespace |
| 123 | |
[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 124 | Blacklist::Observer::Observer(Blacklist* blacklist) : blacklist_(blacklist) { |
| 125 | blacklist_->AddObserver(this); |
| 126 | } |
| 127 | |
| 128 | Blacklist::Observer::~Observer() { |
| 129 | blacklist_->RemoveObserver(this); |
| 130 | } |
| 131 | |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 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 | |
[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 142 | Blacklist::Blacklist(ExtensionPrefs* prefs) : prefs_(prefs) { |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 143 | scoped_refptr<SafeBrowsingDatabaseManager> database_manager = |
| 144 | g_database_manager.Get().get(); |
[email protected] | dc24976f | 2013-06-02 21:15:09 | [diff] [blame] | 145 | if (database_manager.get()) { |
| 146 | registrar_.Add( |
| 147 | this, |
| 148 | chrome::NOTIFICATION_SAFE_BROWSING_UPDATE_COMPLETE, |
| 149 | content::Source<SafeBrowsingDatabaseManager>(database_manager.get())); |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 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). |
[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 158 | } |
| 159 | |
| 160 | Blacklist::~Blacklist() { |
| 161 | } |
| 162 | |
[email protected] | 695b571 | 2012-12-06 23:55:28 | [diff] [blame] | 163 | void Blacklist::GetBlacklistedIDs(const std::set<std::string>& ids, |
| 164 | const GetBlacklistedIDsCallback& callback) { |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 165 | DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 166 | |
[email protected] | bc151cf9 | 2013-02-12 04:57:26 | [diff] [blame] | 167 | if (ids.empty()) { |
| 168 | base::MessageLoopProxy::current()->PostTask( |
| 169 | FROM_HERE, |
| 170 | base::Bind(callback, std::set<std::string>())); |
| 171 | return; |
| 172 | } |
| 173 | |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 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; |
[email protected] | 695b571 | 2012-12-06 23:55:28 | [diff] [blame] | 177 | for (std::set<std::string>::const_iterator it = ids.begin(); |
| 178 | it != ids.end(); ++it) { |
| 179 | if (prefs_->IsExtensionBlacklisted(*it)) |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 180 | pref_blacklisted_ids.insert(*it); |
[email protected] | 695b571 | 2012-12-06 23:55:28 | [diff] [blame] | 181 | } |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 182 | |
[email protected] | cadac62 | 2013-06-11 16:46:36 | [diff] [blame^] | 183 | if (!g_database_manager.Get().get().get()) { |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 184 | base::MessageLoopProxy::current()->PostTask( |
[email protected] | cadac62 | 2013-06-11 16:46:36 | [diff] [blame^] | 185 | FROM_HERE, base::Bind(callback, pref_blacklisted_ids)); |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 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)); |
[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 196 | } |
| 197 | |
[email protected] | bc151cf9 | 2013-02-12 04:57:26 | [diff] [blame] | 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 | |
[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 205 | void Blacklist::SetFromUpdater(const std::vector<std::string>& ids, |
| 206 | const std::string& version) { |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 207 | DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 208 | |
[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 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 | |
[email protected] | 695b571 | 2012-12-06 23:55:28 | [diff] [blame] | 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 | |
[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 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) { |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 247 | DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 248 | observers_.AddObserver(observer); |
| 249 | } |
| 250 | |
| 251 | void Blacklist::RemoveObserver(Observer* observer) { |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 252 | DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 253 | observers_.RemoveObserver(observer); |
| 254 | } |
| 255 | |
[email protected] | 3e72ed75 | 2013-02-02 00:47:47 | [diff] [blame] | 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 | |
[email protected] | fdd679b | 2012-11-15 20:49:39 | [diff] [blame] | 287 | } // namespace extensions |