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 "base/trace_event/trace_event.h"
17 #include "chrome/browser/extensions/extension_management.h"
18 #include "chrome/browser/extensions/extension_service.h"
19 #include "chrome/browser/extensions/install_signer.h"
20 #include "chrome/browser/extensions/install_verifier_factory.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/grit/generated_resources.h"
23 #include "content/public/browser/browser_context.h"
24 #include "content/public/common/content_switches.h"
25 #include "extensions/browser/extension_prefs.h"
26 #include "extensions/browser/extension_registry.h"
27 #include "extensions/browser/extension_system.h"
28 #include "extensions/browser/pref_names.h"
29 #include "extensions/common/extension_set.h"
30 #include "extensions/common/manifest.h"
31 #include "extensions/common/manifest_url_handlers.h"
32 #include "extensions/common/one_shot_event.h"
33 #include "ui/base/l10n/l10n_util.h"
35 namespace extensions
{
40 NONE
= 0, // Do not request install signatures, and do not enforce them.
41 BOOTSTRAP
, // Request install signatures, but do not enforce them.
42 ENFORCE
, // Request install signatures, and enforce them.
43 ENFORCE_STRICT
, // Same as ENFORCE, but hard fail if we can't fetch
46 // This is used in histograms - do not remove or reorder entries above! Also
47 // the "MAX" item below should always be the last element.
51 #if defined(GOOGLE_CHROME_BUILD)
52 const char kExperimentName
[] = "ExtensionInstallVerification";
53 #endif // defined(GOOGLE_CHROME_BUILD)
55 VerifyStatus
GetExperimentStatus() {
56 #if defined(GOOGLE_CHROME_BUILD)
57 const std::string group
= base::FieldTrialList::FindFullName(
60 std::string forced_trials
=
61 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
62 switches::kForceFieldTrials
);
63 if (forced_trials
.find(kExperimentName
) != std::string::npos
) {
64 // We don't want to allow turning off enforcement by forcing the field
65 // trial group to something other than enforcement.
66 return ENFORCE_STRICT
;
69 VerifyStatus default_status
= NONE
;
71 if (group
== "EnforceStrict")
72 return ENFORCE_STRICT
;
73 else if (group
== "Enforce")
75 else if (group
== "Bootstrap")
77 else if (group
== "None" || group
== "Control")
80 return default_status
;
81 #endif // defined(GOOGLE_CHROME_BUILD)
86 VerifyStatus
GetCommandLineStatus() {
87 const base::CommandLine
* cmdline
= base::CommandLine::ForCurrentProcess();
88 if (!InstallSigner::GetForcedNotFromWebstore().empty())
91 if (cmdline
->HasSwitch(switches::kExtensionsInstallVerification
)) {
92 std::string value
= cmdline
->GetSwitchValueASCII(
93 switches::kExtensionsInstallVerification
);
94 if (value
== "bootstrap")
96 else if (value
== "enforce_strict")
97 return ENFORCE_STRICT
;
105 VerifyStatus
GetStatus() {
106 return std::max(GetExperimentStatus(), GetCommandLineStatus());
109 bool ShouldFetchSignature() {
110 return GetStatus() >= BOOTSTRAP
;
115 INIT_UNPARSEABLE_PREF
,
116 INIT_INVALID_SIGNATURE
,
117 INIT_VALID_SIGNATURE
,
119 // This is used in histograms - do not remove or reorder entries above! Also
120 // the "MAX" item below should always be the last element.
125 void LogInitResultHistogram(InitResult result
) {
126 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.InitResult",
127 result
, INIT_RESULT_MAX
);
130 bool CanUseExtensionApis(const Extension
& extension
) {
131 return extension
.is_extension() || extension
.is_legacy_packaged_app();
134 enum VerifyAllSuccess
{
135 VERIFY_ALL_BOOTSTRAP_SUCCESS
= 0,
136 VERIFY_ALL_BOOTSTRAP_FAILURE
,
137 VERIFY_ALL_NON_BOOTSTRAP_SUCCESS
,
138 VERIFY_ALL_NON_BOOTSTRAP_FAILURE
,
140 // Used in histograms. Do not remove/reorder any entries above, and the below
141 // MAX entry should always come last.
142 VERIFY_ALL_SUCCESS_MAX
145 // Record the success or failure of verifying all extensions, and whether or
146 // not it was a bootstrapping.
147 void LogVerifyAllSuccessHistogram(bool bootstrap
, bool success
) {
148 VerifyAllSuccess result
;
149 if (bootstrap
&& success
)
150 result
= VERIFY_ALL_BOOTSTRAP_SUCCESS
;
151 else if (bootstrap
&& !success
)
152 result
= VERIFY_ALL_BOOTSTRAP_FAILURE
;
153 else if (!bootstrap
&& success
)
154 result
= VERIFY_ALL_NON_BOOTSTRAP_SUCCESS
;
156 result
= VERIFY_ALL_NON_BOOTSTRAP_FAILURE
;
158 // This used to be part of ExtensionService, but moved here. In order to keep
159 // our histograms accurate, the name is unchanged.
160 UMA_HISTOGRAM_ENUMERATION(
161 "ExtensionService.VerifyAllSuccess", result
, VERIFY_ALL_SUCCESS_MAX
);
164 // Record the success or failure of a single verification.
165 void LogAddVerifiedSuccess(bool success
) {
166 // This used to be part of ExtensionService, but moved here. In order to keep
167 // our histograms accurate, the name is unchanged.
168 UMA_HISTOGRAM_BOOLEAN("ExtensionService.AddVerified", success
);
173 InstallVerifier::InstallVerifier(ExtensionPrefs
* prefs
,
174 content::BrowserContext
* context
)
177 bootstrap_check_complete_(false),
178 weak_factory_(this) {
181 InstallVerifier::~InstallVerifier() {}
184 InstallVerifier
* InstallVerifier::Get(
185 content::BrowserContext
* browser_context
) {
186 return InstallVerifierFactory::GetForBrowserContext(browser_context
);
190 bool InstallVerifier::ShouldEnforce() {
191 return GetStatus() >= ENFORCE
;
195 bool InstallVerifier::NeedsVerification(const Extension
& extension
) {
196 return IsFromStore(extension
) && CanUseExtensionApis(extension
);
202 bool InstallVerifier::IsFromStore(const Extension
& extension
) {
203 if (extension
.from_webstore() || ManifestURL::UpdatesFromGallery(&extension
))
206 // If an extension has no update url, our autoupdate code will ask the
207 // webstore about it (to aid in migrating to the webstore from self-hosting
208 // or sideloading based installs). So we want to do verification checks on
209 // such extensions too so that we don't accidentally disable old installs of
210 // extensions that did migrate to the webstore.
211 return (ManifestURL::GetUpdateURL(&extension
).is_empty() &&
212 Manifest::IsAutoUpdateableLocation(extension
.location()));
215 void InstallVerifier::Init() {
216 TRACE_EVENT0("browser,startup", "extensions::InstallVerifier::Init");
217 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ExperimentStatus",
218 GetExperimentStatus(), VERIFY_STATUS_MAX
);
219 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ActualStatus",
220 GetStatus(), VERIFY_STATUS_MAX
);
222 const base::DictionaryValue
* pref
= prefs_
->GetInstallSignature();
224 scoped_ptr
<InstallSignature
> signature_from_prefs
=
225 InstallSignature::FromValue(*pref
);
226 if (!signature_from_prefs
.get()) {
227 LogInitResultHistogram(INIT_UNPARSEABLE_PREF
);
228 } else if (!InstallSigner::VerifySignature(*signature_from_prefs
.get())) {
229 LogInitResultHistogram(INIT_INVALID_SIGNATURE
);
230 DVLOG(1) << "Init - ignoring invalid signature";
232 signature_
= signature_from_prefs
.Pass();
233 LogInitResultHistogram(INIT_VALID_SIGNATURE
);
234 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallVerifier.InitSignatureCount",
235 signature_
->ids
.size());
239 LogInitResultHistogram(INIT_NO_PREF
);
242 ExtensionSystem::Get(context_
)->ready().Post(
244 base::Bind(&InstallVerifier::MaybeBootstrapSelf
,
245 weak_factory_
.GetWeakPtr()));
248 void InstallVerifier::VerifyAllExtensions() {
249 AddMany(GetExtensionsToVerify(), ADD_ALL
);
252 base::Time
InstallVerifier::SignatureTimestamp() {
253 if (signature_
.get())
254 return signature_
->timestamp
;
259 bool InstallVerifier::IsKnownId(const std::string
& id
) const {
260 return signature_
.get() && (ContainsKey(signature_
->ids
, id
) ||
261 ContainsKey(signature_
->invalid_ids
, id
));
264 bool InstallVerifier::IsInvalid(const std::string
& id
) const {
265 return ((signature_
.get() && ContainsKey(signature_
->invalid_ids
, id
)));
268 void InstallVerifier::VerifyExtension(const std::string
& extension_id
) {
270 ids
.insert(extension_id
);
271 AddMany(ids
, ADD_SINGLE
);
274 void InstallVerifier::AddMany(const ExtensionIdSet
& ids
, OperationType type
) {
275 if (!ShouldFetchSignature()) {
276 OnVerificationComplete(true, type
); // considered successful.
280 if (signature_
.get()) {
281 ExtensionIdSet not_allowed_yet
=
282 base::STLSetDifference
<ExtensionIdSet
>(ids
, signature_
->ids
);
283 if (not_allowed_yet
.empty()) {
284 OnVerificationComplete(true, type
); // considered successful.
289 InstallVerifier::PendingOperation
* operation
=
290 new InstallVerifier::PendingOperation(type
);
291 operation
->ids
.insert(ids
.begin(), ids
.end());
293 operation_queue_
.push(linked_ptr
<PendingOperation
>(operation
));
295 // If there are no ongoing pending requests, we need to kick one off.
296 if (operation_queue_
.size() == 1)
300 void InstallVerifier::AddProvisional(const ExtensionIdSet
& ids
) {
301 provisional_
.insert(ids
.begin(), ids
.end());
302 AddMany(ids
, ADD_PROVISIONAL
);
305 void InstallVerifier::Remove(const std::string
& id
) {
311 void InstallVerifier::RemoveMany(const ExtensionIdSet
& ids
) {
312 if (!signature_
.get() || !ShouldFetchSignature())
315 bool found_any
= false;
316 for (ExtensionIdSet::const_iterator i
= ids
.begin(); i
!= ids
.end(); ++i
) {
317 if (ContainsKey(signature_
->ids
, *i
) ||
318 ContainsKey(signature_
->invalid_ids
, *i
)) {
326 InstallVerifier::PendingOperation
* operation
=
327 new InstallVerifier::PendingOperation(InstallVerifier::REMOVE
);
328 operation
->ids
= ids
;
330 operation_queue_
.push(linked_ptr
<PendingOperation
>(operation
));
331 if (operation_queue_
.size() == 1)
335 bool InstallVerifier::AllowedByEnterprisePolicy(const std::string
& id
) const {
336 return ExtensionManagementFactory::GetForBrowserContext(context_
)
337 ->IsInstallationExplicitlyAllowed(id
);
340 std::string
InstallVerifier::GetDebugPolicyProviderName() const {
341 return std::string("InstallVerifier");
346 enum MustRemainDisabledOutcome
{
350 ENTERPRISE_POLICY_ALLOWED
,
354 NOT_VERIFIED_BUT_NOT_ENFORCING
,
356 NOT_VERIFIED_BUT_INSTALL_TIME_NEWER_THAN_SIGNATURE
,
357 NOT_VERIFIED_BUT_UNKNOWN_ID
,
360 // This is used in histograms - do not remove or reorder entries above! Also
361 // the "MAX" item below should always be the last element.
362 MUST_REMAIN_DISABLED_OUTCOME_MAX
365 void MustRemainDisabledHistogram(MustRemainDisabledOutcome outcome
) {
366 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.MustRemainDisabled",
367 outcome
, MUST_REMAIN_DISABLED_OUTCOME_MAX
);
372 bool InstallVerifier::MustRemainDisabled(const Extension
* extension
,
373 Extension::DisableReason
* reason
,
374 base::string16
* error
) const {
376 if (!CanUseExtensionApis(*extension
)) {
377 MustRemainDisabledHistogram(NOT_EXTENSION
);
380 if (Manifest::IsUnpackedLocation(extension
->location())) {
381 MustRemainDisabledHistogram(UNPACKED
);
384 if (extension
->location() == Manifest::COMPONENT
) {
385 MustRemainDisabledHistogram(COMPONENT
);
388 if (AllowedByEnterprisePolicy(extension
->id())) {
389 MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED
);
393 bool verified
= true;
394 MustRemainDisabledOutcome outcome
= VERIFIED
;
395 if (ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension
->id())) {
397 outcome
= FORCED_NOT_VERIFIED
;
398 } else if (!IsFromStore(*extension
)) {
400 outcome
= NOT_FROM_STORE
;
401 } else if (signature_
.get() == NULL
&&
402 (!bootstrap_check_complete_
|| GetStatus() < ENFORCE_STRICT
)) {
403 // If we don't have a signature yet, we'll temporarily consider every
404 // extension from the webstore verified to avoid false positives on existing
405 // profiles hitting this code for the first time. The InstallVerifier
406 // will bootstrap itself once the ExtensionsSystem is ready.
407 outcome
= NO_SIGNATURE
;
408 } else if (!IsVerified(extension
->id())) {
409 if (signature_
.get() &&
410 !ContainsKey(signature_
->invalid_ids
, extension
->id())) {
411 outcome
= NOT_VERIFIED_BUT_UNKNOWN_ID
;
414 outcome
= NOT_VERIFIED
;
417 if (!verified
&& !ShouldEnforce()) {
419 outcome
= NOT_VERIFIED_BUT_NOT_ENFORCING
;
421 MustRemainDisabledHistogram(outcome
);
425 *reason
= Extension::DISABLE_NOT_VERIFIED
;
427 *error
= l10n_util::GetStringFUTF16(
428 IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE
,
429 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE
));
434 InstallVerifier::PendingOperation::PendingOperation(OperationType type
)
437 InstallVerifier::PendingOperation::~PendingOperation() {
440 ExtensionIdSet
InstallVerifier::GetExtensionsToVerify() const {
441 ExtensionIdSet result
;
442 scoped_ptr
<ExtensionSet
> extensions
=
443 ExtensionRegistry::Get(context_
)->GenerateInstalledExtensionsSet();
444 for (ExtensionSet::const_iterator iter
= extensions
->begin();
445 iter
!= extensions
->end();
447 if (NeedsVerification(*iter
->get()))
448 result
.insert((*iter
)->id());
453 void InstallVerifier::MaybeBootstrapSelf() {
454 bool needs_bootstrap
= false;
456 ExtensionIdSet extension_ids
= GetExtensionsToVerify();
457 if (signature_
.get() == NULL
&& ShouldFetchSignature()) {
458 needs_bootstrap
= true;
460 for (ExtensionIdSet::const_iterator iter
= extension_ids
.begin();
461 iter
!= extension_ids
.end();
463 if (!IsKnownId(*iter
)) {
464 needs_bootstrap
= true;
471 AddMany(extension_ids
, ADD_ALL_BOOTSTRAP
);
473 bootstrap_check_complete_
= true;
476 void InstallVerifier::OnVerificationComplete(bool success
, OperationType type
) {
479 LogAddVerifiedSuccess(success
);
482 case ADD_ALL_BOOTSTRAP
:
483 LogVerifyAllSuccessHistogram(type
== ADD_ALL_BOOTSTRAP
, success
);
484 bootstrap_check_complete_
= true;
486 // Iterate through the extensions and, if any are newly-verified and
487 // should have the DISABLE_NOT_VERIFIED reason lifted, do so.
488 const ExtensionSet
& disabled_extensions
=
489 ExtensionRegistry::Get(context_
)->disabled_extensions();
490 for (ExtensionSet::const_iterator iter
= disabled_extensions
.begin();
491 iter
!= disabled_extensions
.end();
493 int disable_reasons
= prefs_
->GetDisableReasons((*iter
)->id());
494 if (disable_reasons
& Extension::DISABLE_NOT_VERIFIED
&&
495 !MustRemainDisabled(iter
->get(), NULL
, NULL
)) {
496 prefs_
->RemoveDisableReason((*iter
)->id(),
497 Extension::DISABLE_NOT_VERIFIED
);
501 if (success
|| GetStatus() == ENFORCE_STRICT
) {
502 ExtensionSystem::Get(context_
)
503 ->extension_service()
504 ->CheckManagementPolicy();
507 // We don't need to check disable reasons or report UMA stats for
508 // provisional adds or removals.
509 case ADD_PROVISIONAL
:
515 void InstallVerifier::GarbageCollect() {
516 if (!ShouldFetchSignature()) {
519 CHECK(signature_
.get());
520 ExtensionIdSet leftovers
= signature_
->ids
;
521 leftovers
.insert(signature_
->invalid_ids
.begin(),
522 signature_
->invalid_ids
.end());
523 ExtensionIdList all_ids
;
524 prefs_
->GetExtensions(&all_ids
);
525 for (ExtensionIdList::const_iterator i
= all_ids
.begin();
526 i
!= all_ids
.end(); ++i
) {
527 ExtensionIdSet::iterator found
= leftovers
.find(*i
);
528 if (found
!= leftovers
.end())
529 leftovers
.erase(found
);
531 if (!leftovers
.empty()) {
532 RemoveMany(leftovers
);
536 bool InstallVerifier::IsVerified(const std::string
& id
) const {
537 return ((signature_
.get() && ContainsKey(signature_
->ids
, id
)) ||
538 ContainsKey(provisional_
, id
));
541 void InstallVerifier::BeginFetch() {
542 DCHECK(ShouldFetchSignature());
544 // TODO(asargent) - It would be possible to coalesce all operations in the
545 // queue into one fetch - we'd probably just need to change the queue to
546 // hold (set of ids, list of operation type) pairs.
547 CHECK(!operation_queue_
.empty());
548 const PendingOperation
& operation
= *operation_queue_
.front();
550 ExtensionIdSet ids_to_sign
;
551 if (signature_
.get()) {
552 ids_to_sign
.insert(signature_
->ids
.begin(), signature_
->ids
.end());
554 if (operation
.type
== InstallVerifier::REMOVE
) {
555 for (ExtensionIdSet::const_iterator i
= operation
.ids
.begin();
556 i
!= operation
.ids
.end(); ++i
) {
557 if (ContainsKey(ids_to_sign
, *i
))
558 ids_to_sign
.erase(*i
);
560 } else { // All other operation types are some form of "ADD".
561 ids_to_sign
.insert(operation
.ids
.begin(), operation
.ids
.end());
564 signer_
.reset(new InstallSigner(context_
->GetRequestContext(), ids_to_sign
));
565 signer_
->GetSignature(base::Bind(&InstallVerifier::SignatureCallback
,
566 weak_factory_
.GetWeakPtr()));
569 void InstallVerifier::SaveToPrefs() {
570 if (signature_
.get())
571 DCHECK(InstallSigner::VerifySignature(*signature_
));
573 if (!signature_
.get() || signature_
->ids
.empty()) {
574 DVLOG(1) << "SaveToPrefs - saving NULL";
575 prefs_
->SetInstallSignature(NULL
);
577 base::DictionaryValue pref
;
578 signature_
->ToValue(&pref
);
580 DVLOG(1) << "SaveToPrefs - saving";
582 DCHECK(InstallSigner::VerifySignature(*signature_
.get()));
583 scoped_ptr
<InstallSignature
> rehydrated
=
584 InstallSignature::FromValue(pref
);
585 DCHECK(InstallSigner::VerifySignature(*rehydrated
.get()));
587 prefs_
->SetInstallSignature(&pref
);
593 enum CallbackResult
{
594 CALLBACK_NO_SIGNATURE
= 0,
595 CALLBACK_INVALID_SIGNATURE
,
596 CALLBACK_VALID_SIGNATURE
,
598 // This is used in histograms - do not remove or reorder entries above! Also
599 // the "MAX" item below should always be the last element.
604 void GetSignatureResultHistogram(CallbackResult result
) {
605 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult",
606 result
, CALLBACK_RESULT_MAX
);
611 void InstallVerifier::SignatureCallback(
612 scoped_ptr
<InstallSignature
> signature
) {
614 linked_ptr
<PendingOperation
> operation
= operation_queue_
.front();
615 operation_queue_
.pop();
617 bool success
= false;
618 if (!signature
.get()) {
619 GetSignatureResultHistogram(CALLBACK_NO_SIGNATURE
);
620 } else if (!InstallSigner::VerifySignature(*signature
)) {
621 GetSignatureResultHistogram(CALLBACK_INVALID_SIGNATURE
);
623 GetSignatureResultHistogram(CALLBACK_VALID_SIGNATURE
);
628 OnVerificationComplete(false, operation
->type
);
630 // TODO(asargent) - if this was something like a network error, we need to
631 // do retries with exponential back off.
633 signature_
= signature
.Pass();
636 if (!provisional_
.empty()) {
637 // Update |provisional_| to remove ids that were successfully signed.
638 provisional_
= base::STLSetDifference
<ExtensionIdSet
>(
639 provisional_
, signature_
->ids
);
642 OnVerificationComplete(success
, operation
->type
);
645 if (!operation_queue_
.empty())
649 } // namespace extensions