install_verifier.cc revision 923bd855a3a49144a9f75d8a8200416a52bae775
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 <algorithm> 8#include <string> 9 10#include "base/bind.h" 11#include "base/command_line.h" 12#include "base/metrics/field_trial.h" 13#include "base/metrics/histogram.h" 14#include "base/prefs/pref_service.h" 15#include "base/stl_util.h" 16#include "chrome/browser/extensions/extension_prefs.h" 17#include "chrome/browser/extensions/install_signer.h" 18#include "chrome/common/chrome_switches.h" 19#include "chrome/common/extensions/manifest_url_handler.h" 20#include "chrome/common/pref_names.h" 21#include "content/public/common/content_switches.h" 22#include "extensions/common/manifest.h" 23#include "grit/generated_resources.h" 24#include "ui/base/l10n/l10n_util.h" 25 26namespace { 27 28enum VerifyStatus { 29 NONE = 0, // Do not request install signatures, and do not enforce them. 30 BOOTSTRAP, // Request install signatures, but do not enforce them. 31 ENFORCE, // Request install signatures, and enforce them. 32 33 // This is used in histograms - do not remove or reorder entries above! Also 34 // the "MAX" item below should always be the last element. 35 36 VERIFY_STATUS_MAX 37}; 38 39#if defined(GOOGLE_CHROME_BUILD) 40const char kExperimentName[] = "ExtensionInstallVerification"; 41#endif // defined(GOOGLE_CHROME_BUILD) 42 43VerifyStatus GetExperimentStatus() { 44#if defined(GOOGLE_CHROME_BUILD) 45 const std::string group = base::FieldTrialList::FindFullName( 46 kExperimentName); 47 48 std::string forced_trials = CommandLine::ForCurrentProcess()-> 49 GetSwitchValueASCII(switches::kForceFieldTrials); 50 if (forced_trials.find(kExperimentName) != std::string::npos) { 51 // We don't want to allow turning off enforcement by forcing the field 52 // trial group to something other than enforcement. 53 return ENFORCE; 54 } 55 56 VerifyStatus default_status = NONE; 57 58 if (group == "Enforce") 59 return ENFORCE; 60 else if (group == "Bootstrap") 61 return BOOTSTRAP; 62 else if (group == "None" || group == "Control") 63 return NONE; 64 else 65 return default_status; 66#endif // defined(GOOGLE_CHROME_BUILD) 67 68 return NONE; 69} 70 71VerifyStatus GetCommandLineStatus() { 72 const CommandLine* cmdline = CommandLine::ForCurrentProcess(); 73 if (!extensions::InstallSigner::GetForcedNotFromWebstore().empty()) 74 return ENFORCE; 75 76 if (cmdline->HasSwitch(switches::kExtensionsInstallVerification)) { 77 std::string value = cmdline->GetSwitchValueASCII( 78 switches::kExtensionsInstallVerification); 79 if (value == "bootstrap") 80 return BOOTSTRAP; 81 else 82 return ENFORCE; 83 } 84 85 return NONE; 86} 87 88VerifyStatus GetStatus() { 89 return std::max(GetExperimentStatus(), GetCommandLineStatus()); 90} 91 92bool ShouldFetchSignature() { 93 VerifyStatus status = GetStatus(); 94 return (status == BOOTSTRAP || status == ENFORCE); 95} 96 97bool ShouldEnforce() { 98 return GetStatus() == ENFORCE; 99} 100 101} // namespace 102 103namespace extensions { 104 105InstallVerifier::InstallVerifier(ExtensionPrefs* prefs, 106 net::URLRequestContextGetter* context_getter) 107 : prefs_(prefs), context_getter_(context_getter) { 108} 109 110InstallVerifier::~InstallVerifier() {} 111 112namespace { 113 114enum InitResult { 115 INIT_NO_PREF = 0, 116 INIT_UNPARSEABLE_PREF, 117 INIT_INVALID_SIGNATURE, 118 INIT_VALID_SIGNATURE, 119 120 // This is used in histograms - do not remove or reorder entries above! Also 121 // the "MAX" item below should always be the last element. 122 123 INIT_RESULT_MAX 124}; 125 126void LogInitResultHistogram(InitResult result) { 127 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.InitResult", 128 result, INIT_RESULT_MAX); 129} 130 131bool FromStore(const Extension& extension) { 132 bool updates_from_store = ManifestURL::UpdatesFromGallery(&extension); 133 return extension.from_webstore() || updates_from_store; 134} 135 136bool CanUseExtensionApis(const Extension& extension) { 137 return extension.is_extension() || extension.is_legacy_packaged_app(); 138} 139 140} // namespace 141 142// static 143bool InstallVerifier::NeedsVerification(const Extension& extension) { 144 return FromStore(extension) && CanUseExtensionApis(extension); 145} 146 147void InstallVerifier::Init() { 148 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ExperimentStatus", 149 GetExperimentStatus(), VERIFY_STATUS_MAX); 150 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ActualStatus", 151 GetStatus(), VERIFY_STATUS_MAX); 152 153 const DictionaryValue* pref = prefs_->GetInstallSignature(); 154 if (pref) { 155 scoped_ptr<InstallSignature> signature_from_prefs = 156 InstallSignature::FromValue(*pref); 157 if (!signature_from_prefs.get()) { 158 LogInitResultHistogram(INIT_UNPARSEABLE_PREF); 159 } else if (!InstallSigner::VerifySignature(*signature_from_prefs.get())) { 160 LogInitResultHistogram(INIT_INVALID_SIGNATURE); 161 DVLOG(1) << "Init - ignoring invalid signature"; 162 } else { 163 signature_ = signature_from_prefs.Pass(); 164 LogInitResultHistogram(INIT_VALID_SIGNATURE); 165 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallVerifier.InitSignatureCount", 166 signature_->ids.size()); 167 GarbageCollect(); 168 } 169 } else { 170 LogInitResultHistogram(INIT_NO_PREF); 171 } 172} 173 174bool InstallVerifier::NeedsBootstrap() { 175 return signature_.get() == NULL && ShouldFetchSignature(); 176} 177 178void InstallVerifier::Add(const std::string& id, 179 const AddResultCallback& callback) { 180 ExtensionIdSet ids; 181 ids.insert(id); 182 AddMany(ids, callback); 183} 184 185void InstallVerifier::AddMany(const ExtensionIdSet& ids, 186 const AddResultCallback& callback) { 187 if (!ShouldFetchSignature()) { 188 if (!callback.is_null()) 189 callback.Run(true); 190 return; 191 } 192 193 if (signature_.get()) { 194 ExtensionIdSet not_allowed_yet = 195 base::STLSetDifference<ExtensionIdSet>(ids, signature_->ids); 196 if (not_allowed_yet.empty()) { 197 if (!callback.is_null()) 198 callback.Run(true); 199 return; 200 } 201 } 202 203 InstallVerifier::PendingOperation* operation = 204 new InstallVerifier::PendingOperation(); 205 operation->type = InstallVerifier::ADD; 206 operation->ids.insert(ids.begin(), ids.end()); 207 operation->callback = callback; 208 209 operation_queue_.push(linked_ptr<PendingOperation>(operation)); 210 211 // If there are no ongoing pending requests, we need to kick one off. 212 if (operation_queue_.size() == 1) 213 BeginFetch(); 214} 215 216void InstallVerifier::AddProvisional(const ExtensionIdSet& ids) { 217 provisional_.insert(ids.begin(), ids.end()); 218 AddMany(ids, AddResultCallback()); 219} 220 221void InstallVerifier::Remove(const std::string& id) { 222 ExtensionIdSet ids; 223 ids.insert(id); 224 RemoveMany(ids); 225} 226 227void InstallVerifier::RemoveMany(const ExtensionIdSet& ids) { 228 if (!signature_.get() || !ShouldFetchSignature()) 229 return; 230 231 bool found_any = false; 232 for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); ++i) { 233 if (ContainsKey(signature_->ids, *i)) { 234 found_any = true; 235 break; 236 } 237 } 238 if (!found_any) 239 return; 240 241 InstallVerifier::PendingOperation* operation = 242 new InstallVerifier::PendingOperation(); 243 operation->type = InstallVerifier::REMOVE; 244 operation->ids = ids; 245 246 operation_queue_.push(linked_ptr<PendingOperation>(operation)); 247 if (operation_queue_.size() == 1) 248 BeginFetch(); 249} 250 251std::string InstallVerifier::GetDebugPolicyProviderName() const { 252 return std::string("InstallVerifier"); 253} 254 255namespace { 256 257enum MustRemainDisabledOutcome { 258 VERIFIED = 0, 259 NOT_EXTENSION, 260 UNPACKED, 261 ENTERPRISE_POLICY_ALLOWED, 262 FORCED_NOT_VERIFIED, 263 NOT_FROM_STORE, 264 NO_SIGNATURE, 265 NOT_VERIFIED_BUT_NOT_ENFORCING, 266 NOT_VERIFIED, 267 268 // This is used in histograms - do not remove or reorder entries above! Also 269 // the "MAX" item below should always be the last element. 270 271 MUST_REMAIN_DISABLED_OUTCOME_MAX 272}; 273 274void MustRemainDisabledHistogram(MustRemainDisabledOutcome outcome) { 275 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.MustRemainDisabled", 276 outcome, MUST_REMAIN_DISABLED_OUTCOME_MAX); 277} 278 279} // namespace 280 281bool InstallVerifier::MustRemainDisabled(const Extension* extension, 282 Extension::DisableReason* reason, 283 base::string16* error) const { 284 if (!CanUseExtensionApis(*extension)) { 285 MustRemainDisabledHistogram(NOT_EXTENSION); 286 return false; 287 } 288 if (Manifest::IsUnpackedLocation(extension->location())) { 289 MustRemainDisabledHistogram(UNPACKED); 290 return false; 291 } 292 if (AllowedByEnterprisePolicy(extension->id())) { 293 MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED); 294 return false; 295 } 296 297 bool verified = true; 298 MustRemainDisabledOutcome outcome = VERIFIED; 299 if (ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension->id())) { 300 verified = false; 301 outcome = FORCED_NOT_VERIFIED; 302 } else if (!FromStore(*extension)) { 303 verified = false; 304 outcome = NOT_FROM_STORE; 305 } else if (signature_.get() == NULL) { 306 // If we don't have a signature yet, we'll temporarily consider every 307 // extension from the webstore verified to avoid false positives on existing 308 // profiles hitting this code for the first time, and rely on consumers of 309 // this class to check NeedsBootstrap() and schedule a first check so we can 310 // get a signature. 311 outcome = NO_SIGNATURE; 312 } else if (!IsVerified(extension->id())) { 313 verified = false; 314 outcome = NOT_VERIFIED; 315 } 316 if (!verified && !ShouldEnforce()) { 317 verified = true; 318 outcome = NOT_VERIFIED_BUT_NOT_ENFORCING; 319 } 320 MustRemainDisabledHistogram(outcome); 321 322 if (!verified) { 323 if (reason) 324 *reason = Extension::DISABLE_NOT_VERIFIED; 325 if (error) 326 *error = l10n_util::GetStringFUTF16( 327 IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE, 328 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE)); 329 } 330 return !verified; 331} 332 333InstallVerifier::PendingOperation::PendingOperation() { 334 type = InstallVerifier::ADD; 335} 336 337InstallVerifier::PendingOperation::~PendingOperation() { 338} 339 340void InstallVerifier::GarbageCollect() { 341 if (!ShouldFetchSignature()) { 342 return; 343 } 344 CHECK(signature_.get()); 345 ExtensionIdSet leftovers = signature_->ids; 346 ExtensionIdList all_ids; 347 prefs_->GetExtensions(&all_ids); 348 for (ExtensionIdList::const_iterator i = all_ids.begin(); 349 i != all_ids.end(); ++i) { 350 ExtensionIdSet::iterator found = leftovers.find(*i); 351 if (found != leftovers.end()) 352 leftovers.erase(found); 353 } 354 if (!leftovers.empty()) { 355 RemoveMany(leftovers); 356 } 357} 358 359bool InstallVerifier::AllowedByEnterprisePolicy(const std::string& id) const { 360 PrefService* pref_service = prefs_->pref_service(); 361 if (pref_service->IsManagedPreference(prefs::kExtensionInstallAllowList)) { 362 const base::ListValue* whitelist = 363 pref_service->GetList(prefs::kExtensionInstallAllowList); 364 base::StringValue id_value(id); 365 if (whitelist && whitelist->Find(id_value) != whitelist->end()) 366 return true; 367 } 368 if (pref_service->IsManagedPreference(prefs::kExtensionInstallForceList)) { 369 const base::DictionaryValue* forcelist = 370 pref_service->GetDictionary(prefs::kExtensionInstallForceList); 371 if (forcelist && forcelist->HasKey(id)) 372 return true; 373 } 374 return false; 375} 376 377bool InstallVerifier::IsVerified(const std::string& id) const { 378 return ((signature_.get() && ContainsKey(signature_->ids, id)) || 379 ContainsKey(provisional_, id)); 380} 381 382void InstallVerifier::BeginFetch() { 383 DCHECK(ShouldFetchSignature()); 384 385 // TODO(asargent) - It would be possible to coalesce all operations in the 386 // queue into one fetch - we'd probably just need to change the queue to 387 // hold (set of ids, list of callbacks) pairs. 388 CHECK(!operation_queue_.empty()); 389 const PendingOperation& operation = *operation_queue_.front(); 390 391 ExtensionIdSet ids_to_sign; 392 if (signature_.get()) { 393 ids_to_sign.insert(signature_->ids.begin(), signature_->ids.end()); 394 } 395 if (operation.type == InstallVerifier::ADD) { 396 ids_to_sign.insert(operation.ids.begin(), operation.ids.end()); 397 } else { 398 for (ExtensionIdSet::const_iterator i = operation.ids.begin(); 399 i != operation.ids.end(); ++i) { 400 if (ContainsKey(ids_to_sign, *i)) 401 ids_to_sign.erase(*i); 402 } 403 } 404 405 signer_.reset(new InstallSigner(context_getter_, ids_to_sign)); 406 signer_->GetSignature(base::Bind(&InstallVerifier::SignatureCallback, 407 base::Unretained(this))); 408} 409 410void InstallVerifier::SaveToPrefs() { 411 if (signature_.get()) 412 DCHECK(InstallSigner::VerifySignature(*signature_)); 413 414 if (!signature_.get() || signature_->ids.empty()) { 415 DVLOG(1) << "SaveToPrefs - saving NULL"; 416 prefs_->SetInstallSignature(NULL); 417 } else { 418 DictionaryValue pref; 419 signature_->ToValue(&pref); 420 if (VLOG_IS_ON(1)) { 421 DVLOG(1) << "SaveToPrefs - saving"; 422 423 DCHECK(InstallSigner::VerifySignature(*signature_.get())); 424 scoped_ptr<InstallSignature> rehydrated = 425 InstallSignature::FromValue(pref); 426 DCHECK(InstallSigner::VerifySignature(*rehydrated.get())); 427 } 428 prefs_->SetInstallSignature(&pref); 429 } 430} 431 432namespace { 433 434enum CallbackResult { 435 CALLBACK_NO_SIGNATURE = 0, 436 CALLBACK_INVALID_SIGNATURE, 437 CALLBACK_VALID_SIGNATURE, 438 439 // This is used in histograms - do not remove or reorder entries above! Also 440 // the "MAX" item below should always be the last element. 441 442 CALLBACK_RESULT_MAX 443}; 444 445void GetSignatureResultHistogram(CallbackResult result) { 446 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult", 447 result, CALLBACK_RESULT_MAX); 448} 449 450} // namespace 451 452void InstallVerifier::SignatureCallback( 453 scoped_ptr<InstallSignature> signature) { 454 455 linked_ptr<PendingOperation> operation = operation_queue_.front(); 456 operation_queue_.pop(); 457 458 bool success = false; 459 if (!signature.get()) { 460 GetSignatureResultHistogram(CALLBACK_NO_SIGNATURE); 461 } else if (!InstallSigner::VerifySignature(*signature)) { 462 GetSignatureResultHistogram(CALLBACK_INVALID_SIGNATURE); 463 } else { 464 GetSignatureResultHistogram(CALLBACK_VALID_SIGNATURE); 465 success = true; 466 } 467 468 if (!success) { 469 if (!operation->callback.is_null()) 470 operation->callback.Run(false); 471 472 // TODO(asargent) - if this was something like a network error, we need to 473 // do retries with exponential back off. 474 } else { 475 signature_ = signature.Pass(); 476 SaveToPrefs(); 477 478 if (!provisional_.empty()) { 479 // Update |provisional_| to remove ids that were successfully signed. 480 provisional_ = base::STLSetDifference<ExtensionIdSet>( 481 provisional_, signature_->ids); 482 } 483 484 if (!operation->callback.is_null()) 485 operation->callback.Run(success); 486 } 487 488 if (!operation_queue_.empty()) 489 BeginFetch(); 490} 491 492 493} // namespace extensions 494