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.
5 #include "chrome/browser/extensions/install_verifier.h"
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_service.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/browser/browser_context.h"
22 #include "content/public/common/content_switches.h"
23 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/browser/extension_registry.h"
25 #include "extensions/browser/extension_system.h"
26 #include "extensions/browser/pref_names.h"
27 #include "extensions/common/extension_set.h"
28 #include "extensions/common/manifest.h"
29 #include "extensions/common/one_shot_event.h"
30 #include "grit/generated_resources.h"
31 #include "ui/base/l10n/l10n_util.h"
33 namespace extensions {
38 NONE = 0, // Do not request install signatures, and do not enforce them.
39 BOOTSTRAP, // Request install signatures, but do not enforce them.
40 ENFORCE, // Request install signatures, and enforce them.
41 ENFORCE_STRICT, // Same as ENFORCE, but hard fail if we can't fetch
44 // This is used in histograms - do not remove or reorder entries above! Also
45 // the "MAX" item below should always be the last element.
49 #if defined(GOOGLE_CHROME_BUILD)
50 const char kExperimentName[] = "ExtensionInstallVerification";
51 #endif // defined(GOOGLE_CHROME_BUILD)
53 VerifyStatus GetExperimentStatus() {
54 #if defined(GOOGLE_CHROME_BUILD)
55 const std::string group = base::FieldTrialList::FindFullName(
58 std::string forced_trials = CommandLine::ForCurrentProcess()->
59 GetSwitchValueASCII(switches::kForceFieldTrials);
60 if (forced_trials.find(kExperimentName) != std::string::npos) {
61 // We don't want to allow turning off enforcement by forcing the field
62 // trial group to something other than enforcement.
63 return ENFORCE_STRICT;
66 VerifyStatus default_status = NONE;
68 if (group == "EnforceStrict")
69 return ENFORCE_STRICT;
70 else if (group == "Enforce")
72 else if (group == "Bootstrap")
74 else if (group == "None" || group == "Control")
77 return default_status;
78 #endif // defined(GOOGLE_CHROME_BUILD)
83 VerifyStatus GetCommandLineStatus() {
84 const CommandLine* cmdline = CommandLine::ForCurrentProcess();
85 if (!InstallSigner::GetForcedNotFromWebstore().empty())
88 if (cmdline->HasSwitch(switches::kExtensionsInstallVerification)) {
89 std::string value = cmdline->GetSwitchValueASCII(
90 switches::kExtensionsInstallVerification);
91 if (value == "bootstrap")
93 else if (value == "enforce_strict")
94 return ENFORCE_STRICT;
102 VerifyStatus GetStatus() {
103 return std::max(GetExperimentStatus(), GetCommandLineStatus());
106 bool ShouldFetchSignature() {
107 return GetStatus() >= BOOTSTRAP;
110 bool ShouldEnforce() {
111 return GetStatus() >= ENFORCE;
116 INIT_UNPARSEABLE_PREF,
117 INIT_INVALID_SIGNATURE,
118 INIT_VALID_SIGNATURE,
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.
126 void LogInitResultHistogram(InitResult result) {
127 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.InitResult",
128 result, INIT_RESULT_MAX);
131 bool FromStore(const Extension& extension) {
132 if (extension.from_webstore() || ManifestURL::UpdatesFromGallery(&extension))
135 // If an extension has no update url, our autoupdate code will ask the
136 // webstore about it (to aid in migrating to the webstore from self-hosting
137 // or sideloading based installs). So we want to do verification checks on
138 // such extensions too so that we don't accidentally disable old installs of
139 // extensions that did migrate to the webstore.
140 return (ManifestURL::GetUpdateURL(&extension).is_empty() &&
141 Manifest::IsAutoUpdateableLocation(extension.location()));
144 bool CanUseExtensionApis(const Extension& extension) {
145 return extension.is_extension() || extension.is_legacy_packaged_app();
148 enum VerifyAllSuccess {
149 VERIFY_ALL_BOOTSTRAP_SUCCESS = 0,
150 VERIFY_ALL_BOOTSTRAP_FAILURE,
151 VERIFY_ALL_NON_BOOTSTRAP_SUCCESS,
152 VERIFY_ALL_NON_BOOTSTRAP_FAILURE,
154 // Used in histograms. Do not remove/reorder any entries above, and the below
155 // MAX entry should always come last.
156 VERIFY_ALL_SUCCESS_MAX
159 // Record the success or failure of verifying all extensions, and whether or
160 // not it was a bootstrapping.
161 void LogVerifyAllSuccessHistogram(bool bootstrap, bool success) {
162 VerifyAllSuccess result;
163 if (bootstrap && success)
164 result = VERIFY_ALL_BOOTSTRAP_SUCCESS;
165 else if (bootstrap && !success)
166 result = VERIFY_ALL_BOOTSTRAP_FAILURE;
167 else if (!bootstrap && success)
168 result = VERIFY_ALL_NON_BOOTSTRAP_SUCCESS;
170 result = VERIFY_ALL_NON_BOOTSTRAP_FAILURE;
172 // This used to be part of ExtensionService, but moved here. In order to keep
173 // our histograms accurate, the name is unchanged.
174 UMA_HISTOGRAM_ENUMERATION(
175 "ExtensionService.VerifyAllSuccess", result, VERIFY_ALL_SUCCESS_MAX);
178 // Record the success or failure of a single verification.
179 void LogAddVerifiedSuccess(bool success) {
180 // This used to be part of ExtensionService, but moved here. In order to keep
181 // our histograms accurate, the name is unchanged.
182 UMA_HISTOGRAM_BOOLEAN("ExtensionService.AddVerified", success);
187 InstallVerifier::InstallVerifier(ExtensionPrefs* prefs,
188 content::BrowserContext* context)
191 bootstrap_check_complete_(false),
192 weak_factory_(this) {
195 InstallVerifier::~InstallVerifier() {}
198 bool InstallVerifier::NeedsVerification(const Extension& extension) {
199 return FromStore(extension) && CanUseExtensionApis(extension);
202 void InstallVerifier::Init() {
203 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ExperimentStatus",
204 GetExperimentStatus(), VERIFY_STATUS_MAX);
205 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ActualStatus",
206 GetStatus(), VERIFY_STATUS_MAX);
208 const base::DictionaryValue* pref = prefs_->GetInstallSignature();
210 scoped_ptr<InstallSignature> signature_from_prefs =
211 InstallSignature::FromValue(*pref);
212 if (!signature_from_prefs.get()) {
213 LogInitResultHistogram(INIT_UNPARSEABLE_PREF);
214 } else if (!InstallSigner::VerifySignature(*signature_from_prefs.get())) {
215 LogInitResultHistogram(INIT_INVALID_SIGNATURE);
216 DVLOG(1) << "Init - ignoring invalid signature";
218 signature_ = signature_from_prefs.Pass();
219 LogInitResultHistogram(INIT_VALID_SIGNATURE);
220 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallVerifier.InitSignatureCount",
221 signature_->ids.size());
225 LogInitResultHistogram(INIT_NO_PREF);
228 ExtensionSystem::Get(context_)->ready().Post(
230 base::Bind(&InstallVerifier::MaybeBootstrapSelf,
231 weak_factory_.GetWeakPtr()));
234 void InstallVerifier::VerifyAllExtensions() {
235 AddMany(GetExtensionsToVerify(), ADD_ALL);
238 base::Time InstallVerifier::SignatureTimestamp() {
239 if (signature_.get())
240 return signature_->timestamp;
245 bool InstallVerifier::IsKnownId(const std::string& id) {
246 return signature_.get() && (ContainsKey(signature_->ids, id) ||
247 ContainsKey(signature_->invalid_ids, id));
250 void InstallVerifier::VerifyExtension(const std::string& extension_id) {
252 ids.insert(extension_id);
253 AddMany(ids, ADD_SINGLE);
256 void InstallVerifier::AddMany(const ExtensionIdSet& ids, OperationType type) {
257 if (!ShouldFetchSignature()) {
258 OnVerificationComplete(true, type); // considered successful.
262 if (signature_.get()) {
263 ExtensionIdSet not_allowed_yet =
264 base::STLSetDifference<ExtensionIdSet>(ids, signature_->ids);
265 if (not_allowed_yet.empty()) {
266 OnVerificationComplete(true, type); // considered successful.
271 InstallVerifier::PendingOperation* operation =
272 new InstallVerifier::PendingOperation(type);
273 operation->ids.insert(ids.begin(), ids.end());
275 operation_queue_.push(linked_ptr<PendingOperation>(operation));
277 // If there are no ongoing pending requests, we need to kick one off.
278 if (operation_queue_.size() == 1)
282 void InstallVerifier::AddProvisional(const ExtensionIdSet& ids) {
283 provisional_.insert(ids.begin(), ids.end());
284 AddMany(ids, ADD_PROVISIONAL);
287 void InstallVerifier::Remove(const std::string& id) {
293 void InstallVerifier::RemoveMany(const ExtensionIdSet& ids) {
294 if (!signature_.get() || !ShouldFetchSignature())
297 bool found_any = false;
298 for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); ++i) {
299 if (ContainsKey(signature_->ids, *i) ||
300 ContainsKey(signature_->invalid_ids, *i)) {
308 InstallVerifier::PendingOperation* operation =
309 new InstallVerifier::PendingOperation(InstallVerifier::REMOVE);
310 operation->ids = ids;
312 operation_queue_.push(linked_ptr<PendingOperation>(operation));
313 if (operation_queue_.size() == 1)
317 std::string InstallVerifier::GetDebugPolicyProviderName() const {
318 return std::string("InstallVerifier");
323 enum MustRemainDisabledOutcome {
327 ENTERPRISE_POLICY_ALLOWED,
331 NOT_VERIFIED_BUT_NOT_ENFORCING,
333 NOT_VERIFIED_BUT_INSTALL_TIME_NEWER_THAN_SIGNATURE,
334 NOT_VERIFIED_BUT_UNKNOWN_ID,
337 // This is used in histograms - do not remove or reorder entries above! Also
338 // the "MAX" item below should always be the last element.
339 MUST_REMAIN_DISABLED_OUTCOME_MAX
342 void MustRemainDisabledHistogram(MustRemainDisabledOutcome outcome) {
343 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.MustRemainDisabled",
344 outcome, MUST_REMAIN_DISABLED_OUTCOME_MAX);
349 bool InstallVerifier::MustRemainDisabled(const Extension* extension,
350 Extension::DisableReason* reason,
351 base::string16* error) const {
353 if (!CanUseExtensionApis(*extension)) {
354 MustRemainDisabledHistogram(NOT_EXTENSION);
357 if (Manifest::IsUnpackedLocation(extension->location())) {
358 MustRemainDisabledHistogram(UNPACKED);
361 if (extension->location() == Manifest::COMPONENT) {
362 MustRemainDisabledHistogram(COMPONENT);
365 if (AllowedByEnterprisePolicy(extension->id())) {
366 MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED);
370 bool verified = true;
371 MustRemainDisabledOutcome outcome = VERIFIED;
372 if (ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension->id())) {
374 outcome = FORCED_NOT_VERIFIED;
375 } else if (!FromStore(*extension)) {
377 outcome = NOT_FROM_STORE;
378 } else if (signature_.get() == NULL &&
379 (!bootstrap_check_complete_ || GetStatus() < ENFORCE_STRICT)) {
380 // If we don't have a signature yet, we'll temporarily consider every
381 // extension from the webstore verified to avoid false positives on existing
382 // profiles hitting this code for the first time. The InstallVerifier
383 // will bootstrap itself once the ExtensionsSystem is ready.
384 outcome = NO_SIGNATURE;
385 } else if (!IsVerified(extension->id())) {
386 if (signature_.get() &&
387 !ContainsKey(signature_->invalid_ids, extension->id())) {
388 outcome = NOT_VERIFIED_BUT_UNKNOWN_ID;
391 outcome = NOT_VERIFIED;
394 if (!verified && !ShouldEnforce()) {
396 outcome = NOT_VERIFIED_BUT_NOT_ENFORCING;
398 MustRemainDisabledHistogram(outcome);
402 *reason = Extension::DISABLE_NOT_VERIFIED;
404 *error = l10n_util::GetStringFUTF16(
405 IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
406 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE));
411 InstallVerifier::PendingOperation::PendingOperation(OperationType type)
414 InstallVerifier::PendingOperation::~PendingOperation() {
417 ExtensionIdSet InstallVerifier::GetExtensionsToVerify() const {
418 ExtensionIdSet result;
419 scoped_ptr<ExtensionSet> extensions =
420 ExtensionRegistry::Get(context_)->GenerateInstalledExtensionsSet();
421 for (ExtensionSet::const_iterator iter = extensions->begin();
422 iter != extensions->end();
424 if (NeedsVerification(**iter))
425 result.insert((*iter)->id());
430 void InstallVerifier::MaybeBootstrapSelf() {
431 bool needs_bootstrap = false;
433 ExtensionIdSet extension_ids = GetExtensionsToVerify();
434 if (signature_.get() == NULL && ShouldFetchSignature()) {
435 needs_bootstrap = true;
437 for (ExtensionIdSet::const_iterator iter = extension_ids.begin();
438 iter != extension_ids.end();
440 if (!IsKnownId(*iter)) {
441 needs_bootstrap = true;
448 AddMany(extension_ids, ADD_ALL_BOOTSTRAP);
450 bootstrap_check_complete_ = true;
453 void InstallVerifier::OnVerificationComplete(bool success, OperationType type) {
456 LogAddVerifiedSuccess(success);
459 case ADD_ALL_BOOTSTRAP:
460 LogVerifyAllSuccessHistogram(type == ADD_ALL_BOOTSTRAP, success);
461 bootstrap_check_complete_ = true;
463 // Iterate through the extensions and, if any are newly-verified and
464 // should have the DISABLE_NOT_VERIFIED reason lifted, do so.
465 const ExtensionSet& disabled_extensions =
466 ExtensionRegistry::Get(context_)->disabled_extensions();
467 for (ExtensionSet::const_iterator iter = disabled_extensions.begin();
468 iter != disabled_extensions.end();
470 int disable_reasons = prefs_->GetDisableReasons((*iter)->id());
471 if (disable_reasons & Extension::DISABLE_NOT_VERIFIED &&
472 !MustRemainDisabled(*iter, NULL, NULL)) {
473 prefs_->RemoveDisableReason((*iter)->id(),
474 Extension::DISABLE_NOT_VERIFIED);
478 if (success || GetStatus() == ENFORCE_STRICT) {
479 ExtensionSystem::Get(context_)
480 ->extension_service()
481 ->CheckManagementPolicy();
484 // We don't need to check disable reasons or report UMA stats for
485 // provisional adds or removals.
486 case ADD_PROVISIONAL:
492 void InstallVerifier::GarbageCollect() {
493 if (!ShouldFetchSignature()) {
496 CHECK(signature_.get());
497 ExtensionIdSet leftovers = signature_->ids;
498 leftovers.insert(signature_->invalid_ids.begin(),
499 signature_->invalid_ids.end());
500 ExtensionIdList all_ids;
501 prefs_->GetExtensions(&all_ids);
502 for (ExtensionIdList::const_iterator i = all_ids.begin();
503 i != all_ids.end(); ++i) {
504 ExtensionIdSet::iterator found = leftovers.find(*i);
505 if (found != leftovers.end())
506 leftovers.erase(found);
508 if (!leftovers.empty()) {
509 RemoveMany(leftovers);
513 bool InstallVerifier::AllowedByEnterprisePolicy(const std::string& id) const {
514 PrefService* pref_service = prefs_->pref_service();
515 if (pref_service->IsManagedPreference(pref_names::kInstallAllowList)) {
516 const base::ListValue* whitelist =
517 pref_service->GetList(pref_names::kInstallAllowList);
518 base::StringValue id_value(id);
519 if (whitelist && whitelist->Find(id_value) != whitelist->end())
522 if (pref_service->IsManagedPreference(pref_names::kInstallForceList)) {
523 const base::DictionaryValue* forcelist =
524 pref_service->GetDictionary(pref_names::kInstallForceList);
525 if (forcelist && forcelist->HasKey(id))
531 bool InstallVerifier::IsVerified(const std::string& id) const {
532 return ((signature_.get() && ContainsKey(signature_->ids, id)) ||
533 ContainsKey(provisional_, id));
536 void InstallVerifier::BeginFetch() {
537 DCHECK(ShouldFetchSignature());
539 // TODO(asargent) - It would be possible to coalesce all operations in the
540 // queue into one fetch - we'd probably just need to change the queue to
541 // hold (set of ids, list of operation type) pairs.
542 CHECK(!operation_queue_.empty());
543 const PendingOperation& operation = *operation_queue_.front();
545 ExtensionIdSet ids_to_sign;
546 if (signature_.get()) {
547 ids_to_sign.insert(signature_->ids.begin(), signature_->ids.end());
549 if (operation.type == InstallVerifier::REMOVE) {
550 for (ExtensionIdSet::const_iterator i = operation.ids.begin();
551 i != operation.ids.end(); ++i) {
552 if (ContainsKey(ids_to_sign, *i))
553 ids_to_sign.erase(*i);
555 } else { // All other operation types are some form of "ADD".
556 ids_to_sign.insert(operation.ids.begin(), operation.ids.end());
559 signer_.reset(new InstallSigner(context_->GetRequestContext(), ids_to_sign));
560 signer_->GetSignature(base::Bind(&InstallVerifier::SignatureCallback,
561 weak_factory_.GetWeakPtr()));
564 void InstallVerifier::SaveToPrefs() {
565 if (signature_.get())
566 DCHECK(InstallSigner::VerifySignature(*signature_));
568 if (!signature_.get() || signature_->ids.empty()) {
569 DVLOG(1) << "SaveToPrefs - saving NULL";
570 prefs_->SetInstallSignature(NULL);
572 base::DictionaryValue pref;
573 signature_->ToValue(&pref);
575 DVLOG(1) << "SaveToPrefs - saving";
577 DCHECK(InstallSigner::VerifySignature(*signature_.get()));
578 scoped_ptr<InstallSignature> rehydrated =
579 InstallSignature::FromValue(pref);
580 DCHECK(InstallSigner::VerifySignature(*rehydrated.get()));
582 prefs_->SetInstallSignature(&pref);
588 enum CallbackResult {
589 CALLBACK_NO_SIGNATURE = 0,
590 CALLBACK_INVALID_SIGNATURE,
591 CALLBACK_VALID_SIGNATURE,
593 // This is used in histograms - do not remove or reorder entries above! Also
594 // the "MAX" item below should always be the last element.
599 void GetSignatureResultHistogram(CallbackResult result) {
600 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult",
601 result, CALLBACK_RESULT_MAX);
606 void InstallVerifier::SignatureCallback(
607 scoped_ptr<InstallSignature> signature) {
609 linked_ptr<PendingOperation> operation = operation_queue_.front();
610 operation_queue_.pop();
612 bool success = false;
613 if (!signature.get()) {
614 GetSignatureResultHistogram(CALLBACK_NO_SIGNATURE);
615 } else if (!InstallSigner::VerifySignature(*signature)) {
616 GetSignatureResultHistogram(CALLBACK_INVALID_SIGNATURE);
618 GetSignatureResultHistogram(CALLBACK_VALID_SIGNATURE);
623 OnVerificationComplete(false, operation->type);
625 // TODO(asargent) - if this was something like a network error, we need to
626 // do retries with exponential back off.
628 signature_ = signature.Pass();
631 if (!provisional_.empty()) {
632 // Update |provisional_| to remove ids that were successfully signed.
633 provisional_ = base::STLSetDifference<ExtensionIdSet>(
634 provisional_, signature_->ids);
637 OnVerificationComplete(success, operation->type);
640 if (!operation_queue_.empty())
644 } // namespace extensions