install_verifier.cc revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright 2013 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/install_verifier.h" 6 7#include "base/bind.h" 8#include "base/command_line.h" 9#include "base/metrics/histogram.h" 10#include "base/prefs/pref_service.h" 11#include "base/stl_util.h" 12#include "chrome/browser/extensions/extension_prefs.h" 13#include "chrome/browser/extensions/install_signer.h" 14#include "chrome/common/chrome_switches.h" 15#include "chrome/common/extensions/manifest_url_handler.h" 16#include "chrome/common/pref_names.h" 17#include "extensions/common/manifest.h" 18#include "grit/generated_resources.h" 19#include "ui/base/l10n/l10n_util.h" 20 21namespace { 22 23enum VerifyStatus { 24 NONE, // Do not request install signatures, and do not enforce them. 25 BOOTSTRAP, // Request install signatures, but do not enforce them. 26 ENFORCE // Request install signatures, and enforce them. 27}; 28 29VerifyStatus GetStatus() { 30 const CommandLine* cmdline = CommandLine::ForCurrentProcess(); 31 if (!extensions::InstallSigner::GetForcedNotFromWebstore().empty()) 32 return ENFORCE; 33 34 if (cmdline->HasSwitch(switches::kExtensionsInstallVerification)) { 35 std::string value = cmdline->GetSwitchValueASCII( 36 switches::kExtensionsInstallVerification); 37 if (value == "bootstrap") 38 return BOOTSTRAP; 39 else 40 return ENFORCE; 41 } 42 43 return NONE; 44} 45 46bool ShouldFetchSignature() { 47 VerifyStatus status = GetStatus(); 48 return (status == BOOTSTRAP || status == ENFORCE); 49} 50 51bool ShouldEnforce() { 52 return GetStatus() == ENFORCE; 53} 54 55} // namespace 56 57namespace extensions { 58 59InstallVerifier::InstallVerifier(ExtensionPrefs* prefs, 60 net::URLRequestContextGetter* context_getter) 61 : prefs_(prefs), context_getter_(context_getter) { 62} 63 64InstallVerifier::~InstallVerifier() {} 65 66void InstallVerifier::Init() { 67 const DictionaryValue* pref = prefs_->GetInstallSignature(); 68 if (pref) { 69 scoped_ptr<InstallSignature> signature_from_prefs = 70 InstallSignature::FromValue(*pref); 71 if (!signature_from_prefs.get()) { 72 UMA_HISTOGRAM_BOOLEAN("InstallVerifier.InitUnparseablePref", true); 73 } else if (!InstallSigner::VerifySignature(*signature_from_prefs.get())) { 74 UMA_HISTOGRAM_BOOLEAN("InstallVerifier.InitInvalidSignature", true); 75 DVLOG(1) << "Init - ignoring invalid signature"; 76 } else { 77 signature_ = signature_from_prefs.Pass(); 78 GarbageCollect(); 79 } 80 } 81 82 if (!signature_.get() && ShouldFetchSignature()) { 83 // We didn't have any signature but are in fetch mode, so do a request for 84 // a signature if needed. 85 scoped_ptr<ExtensionPrefs::ExtensionsInfo> all_info = 86 prefs_->GetInstalledExtensionsInfo(); 87 ExtensionIdSet to_add; 88 if (all_info.get()) { 89 for (ExtensionPrefs::ExtensionsInfo::const_iterator i = all_info->begin(); 90 i != all_info->end(); ++i) { 91 const ExtensionInfo& info = **i; 92 const base::DictionaryValue* manifest = info.extension_manifest.get(); 93 if (manifest && ManifestURL::UpdatesFromGallery(manifest)) 94 to_add.insert(info.extension_id); 95 } 96 } 97 if (!to_add.empty()) 98 AddMany(to_add, AddResultCallback()); 99 } 100} 101 102void InstallVerifier::Add(const std::string& id, 103 const AddResultCallback& callback) { 104 ExtensionIdSet ids; 105 ids.insert(id); 106 AddMany(ids, callback); 107} 108 109void InstallVerifier::AddMany(const ExtensionIdSet& ids, 110 const AddResultCallback& callback) { 111 if (!ShouldFetchSignature()) 112 return; 113 114 if (signature_.get()) { 115 ExtensionIdSet not_allowed_yet = 116 base::STLSetDifference<ExtensionIdSet>(ids, signature_->ids); 117 if (not_allowed_yet.empty()) { 118 if (!callback.is_null()) 119 callback.Run(true); 120 return; 121 } 122 } 123 124 InstallVerifier::PendingOperation* operation = 125 new InstallVerifier::PendingOperation(); 126 operation->type = InstallVerifier::ADD; 127 operation->ids.insert(ids.begin(), ids.end()); 128 operation->callback = callback; 129 130 operation_queue_.push(linked_ptr<PendingOperation>(operation)); 131 132 // If there are no ongoing pending requests, we need to kick one off. 133 if (operation_queue_.size() == 1) 134 BeginFetch(); 135} 136 137void InstallVerifier::AddProvisional(const ExtensionIdSet& ids) { 138 provisional_.insert(ids.begin(), ids.end()); 139 AddMany(ids, AddResultCallback()); 140} 141 142void InstallVerifier::Remove(const std::string& id) { 143 ExtensionIdSet ids; 144 ids.insert(id); 145 RemoveMany(ids); 146} 147 148void InstallVerifier::RemoveMany(const ExtensionIdSet& ids) { 149 if (!signature_.get() || !ShouldFetchSignature()) 150 return; 151 152 bool found_any = false; 153 for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); ++i) { 154 if (ContainsKey(signature_->ids, *i)) { 155 found_any = true; 156 break; 157 } 158 } 159 if (!found_any) 160 return; 161 162 InstallVerifier::PendingOperation* operation = 163 new InstallVerifier::PendingOperation(); 164 operation->type = InstallVerifier::REMOVE; 165 operation->ids = ids; 166 167 operation_queue_.push(linked_ptr<PendingOperation>(operation)); 168 if (operation_queue_.size() == 1) 169 BeginFetch(); 170} 171 172std::string InstallVerifier::GetDebugPolicyProviderName() const { 173 return std::string("InstallVerifier"); 174} 175 176static bool FromStore(const Extension* extension) { 177 bool updates_from_store = ManifestURL::UpdatesFromGallery(extension); 178 return extension->from_webstore() || updates_from_store; 179} 180 181bool InstallVerifier::MustRemainDisabled(const Extension* extension, 182 Extension::DisableReason* reason, 183 string16* error) const { 184 if (!ShouldEnforce() || !extension->is_extension() || 185 Manifest::IsUnpackedLocation(extension->location()) || 186 AllowedByEnterprisePolicy(extension->id())) 187 return false; 188 189 bool verified = 190 FromStore(extension) && 191 IsVerified(extension->id()) && 192 !ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension->id()); 193 194 if (!verified) { 195 if (reason) 196 *reason = Extension::DISABLE_NOT_VERIFIED; 197 if (error) 198 *error = l10n_util::GetStringFUTF16( 199 IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE, 200 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)); 201 } 202 return !verified; 203} 204 205InstallVerifier::PendingOperation::PendingOperation() { 206 type = InstallVerifier::ADD; 207} 208 209InstallVerifier::PendingOperation::~PendingOperation() { 210} 211 212void InstallVerifier::GarbageCollect() { 213 if (!ShouldFetchSignature()) { 214 return; 215 } 216 CHECK(signature_.get()); 217 ExtensionIdSet leftovers = signature_->ids; 218 ExtensionIdList all_ids; 219 prefs_->GetExtensions(&all_ids); 220 for (ExtensionIdList::const_iterator i = all_ids.begin(); 221 i != all_ids.end(); ++i) { 222 ExtensionIdSet::iterator found = leftovers.find(*i); 223 if (found != leftovers.end()) 224 leftovers.erase(found); 225 } 226 if (!leftovers.empty()) { 227 RemoveMany(leftovers); 228 } 229} 230 231bool InstallVerifier::AllowedByEnterprisePolicy(const std::string& id) const { 232 PrefService* pref_service = prefs_->pref_service(); 233 if (pref_service->IsManagedPreference(prefs::kExtensionInstallAllowList)) { 234 const base::ListValue* whitelist = 235 pref_service->GetList(prefs::kExtensionInstallAllowList); 236 base::StringValue id_value(id); 237 if (whitelist && whitelist->Find(id_value) != whitelist->end()) 238 return true; 239 } 240 if (pref_service->IsManagedPreference(prefs::kExtensionInstallForceList)) { 241 const base::DictionaryValue* forcelist = 242 pref_service->GetDictionary(prefs::kExtensionInstallForceList); 243 if (forcelist && forcelist->HasKey(id)) 244 return true; 245 } 246 return false; 247} 248 249bool InstallVerifier::IsVerified(const std::string& id) const { 250 return ((signature_.get() && ContainsKey(signature_->ids, id)) || 251 ContainsKey(provisional_, id)); 252} 253 254void InstallVerifier::BeginFetch() { 255 DCHECK(ShouldFetchSignature()); 256 257 // TODO(asargent) - It would be possible to coalesce all operations in the 258 // queue into one fetch - we'd probably just need to change the queue to 259 // hold (set of ids, list of callbacks) pairs. 260 CHECK(!operation_queue_.empty()); 261 const PendingOperation& operation = *operation_queue_.front(); 262 263 ExtensionIdSet ids_to_sign; 264 if (signature_.get()) { 265 ids_to_sign.insert(signature_->ids.begin(), signature_->ids.end()); 266 } 267 if (operation.type == InstallVerifier::ADD) { 268 ids_to_sign.insert(operation.ids.begin(), operation.ids.end()); 269 } else { 270 for (ExtensionIdSet::const_iterator i = operation.ids.begin(); 271 i != operation.ids.end(); ++i) { 272 if (ContainsKey(ids_to_sign, *i)) 273 ids_to_sign.erase(*i); 274 } 275 } 276 277 signer_.reset(new InstallSigner(context_getter_, ids_to_sign)); 278 signer_->GetSignature(base::Bind(&InstallVerifier::SignatureCallback, 279 base::Unretained(this))); 280} 281 282void InstallVerifier::SaveToPrefs() { 283 if (signature_.get()) 284 DCHECK(InstallSigner::VerifySignature(*signature_)); 285 286 if (!signature_.get() || signature_->ids.empty()) { 287 DVLOG(1) << "SaveToPrefs - saving NULL"; 288 prefs_->SetInstallSignature(NULL); 289 } else { 290 DictionaryValue pref; 291 signature_->ToValue(&pref); 292 if (VLOG_IS_ON(1)) { 293 DVLOG(1) << "SaveToPrefs - saving"; 294 295 DCHECK(InstallSigner::VerifySignature(*signature_.get())); 296 scoped_ptr<InstallSignature> rehydrated = 297 InstallSignature::FromValue(pref); 298 DCHECK(InstallSigner::VerifySignature(*rehydrated.get())); 299 } 300 prefs_->SetInstallSignature(&pref); 301 } 302} 303 304void InstallVerifier::SignatureCallback( 305 scoped_ptr<InstallSignature> signature) { 306 307 linked_ptr<PendingOperation> operation = operation_queue_.front(); 308 operation_queue_.pop(); 309 310 bool success = false; 311 if (!signature.get()) { 312 UMA_HISTOGRAM_BOOLEAN("InstallVerifier.CallbackNoSignature", true); 313 } else if (!InstallSigner::VerifySignature(*signature)) { 314 UMA_HISTOGRAM_BOOLEAN("InstallVerifier.CallbackInvalidSignature", true); 315 } else { 316 UMA_HISTOGRAM_BOOLEAN("InstallVerifier.CallbackValidSignature", true); 317 success = true; 318 } 319 320 if (!success) { 321 if (!operation->callback.is_null()) 322 operation->callback.Run(false); 323 324 // TODO(asargent) - if this was something like a network error, we need to 325 // do retries with exponential back off. 326 } else { 327 signature_ = signature.Pass(); 328 SaveToPrefs(); 329 330 if (!provisional_.empty()) { 331 // Update |provisional_| to remove ids that were successfully signed. 332 provisional_ = base::STLSetDifference<ExtensionIdSet>( 333 provisional_, signature_->ids); 334 } 335 336 // See if we were able to sign all of |ids|. 337 ExtensionIdSet not_allowed = 338 base::STLSetDifference<ExtensionIdSet>(operation->ids, 339 signature_->ids); 340 341 UMA_HISTOGRAM_COUNTS_100("InstallVerifier.CallbackNotAllowed", 342 not_allowed.size()); 343 344 if (!operation->callback.is_null()) 345 operation->callback.Run(not_allowed.empty()); 346 } 347 348 if (!operation_queue_.empty()) 349 BeginFetch(); 350} 351 352 353} // namespace extensions 354