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/common/chrome_switches.h"
21 #include "chrome/grit/generated_resources.h"
22 #include "content/public/browser/browser_context.h"
23 #include "content/public/common/content_switches.h"
24 #include "extensions/browser/extension_prefs.h"
25 #include "extensions/browser/extension_registry.h"
26 #include "extensions/browser/extension_system.h"
27 #include "extensions/browser/pref_names.h"
28 #include "extensions/common/extension_set.h"
29 #include "extensions/common/manifest.h"
30 #include "extensions/common/manifest_url_handlers.h"
31 #include "extensions/common/one_shot_event.h"
32 #include "ui/base/l10n/l10n_util.h"
34 namespace extensions
{
39 NONE
= 0, // Do not request install signatures, and do not enforce them.
40 BOOTSTRAP
, // Request install signatures, but do not enforce them.
41 ENFORCE
, // Request install signatures, and enforce them.
42 ENFORCE_STRICT
, // Same as ENFORCE, but hard fail if we can't fetch
45 // This is used in histograms - do not remove or reorder entries above! Also
46 // the "MAX" item below should always be the last element.
50 #if defined(GOOGLE_CHROME_BUILD)
51 const char kExperimentName
[] = "ExtensionInstallVerification";
52 #endif // defined(GOOGLE_CHROME_BUILD)
54 VerifyStatus
GetExperimentStatus() {
55 #if defined(GOOGLE_CHROME_BUILD)
56 const std::string group
= base::FieldTrialList::FindFullName(
59 std::string forced_trials
=
60 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
61 switches::kForceFieldTrials
);
62 if (forced_trials
.find(kExperimentName
) != std::string::npos
) {
63 // We don't want to allow turning off enforcement by forcing the field
64 // trial group to something other than enforcement.
65 return ENFORCE_STRICT
;
68 VerifyStatus default_status
= NONE
;
70 if (group
== "EnforceStrict")
71 return ENFORCE_STRICT
;
72 else if (group
== "Enforce")
74 else if (group
== "Bootstrap")
76 else if (group
== "None" || group
== "Control")
79 return default_status
;
80 #endif // defined(GOOGLE_CHROME_BUILD)
85 VerifyStatus
GetCommandLineStatus() {
86 const base::CommandLine
* cmdline
= base::CommandLine::ForCurrentProcess();
87 if (!InstallSigner::GetForcedNotFromWebstore().empty())
90 if (cmdline
->HasSwitch(switches::kExtensionsInstallVerification
)) {
91 std::string value
= cmdline
->GetSwitchValueASCII(
92 switches::kExtensionsInstallVerification
);
93 if (value
== "bootstrap")
95 else if (value
== "enforce_strict")
96 return ENFORCE_STRICT
;
104 VerifyStatus
GetStatus() {
105 return std::max(GetExperimentStatus(), GetCommandLineStatus());
108 bool ShouldFetchSignature() {
109 return GetStatus() >= BOOTSTRAP
;
112 bool ShouldEnforce() {
113 return GetStatus() >= ENFORCE
;
118 INIT_UNPARSEABLE_PREF
,
119 INIT_INVALID_SIGNATURE
,
120 INIT_VALID_SIGNATURE
,
122 // This is used in histograms - do not remove or reorder entries above! Also
123 // the "MAX" item below should always be the last element.
128 void LogInitResultHistogram(InitResult result
) {
129 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.InitResult",
130 result
, INIT_RESULT_MAX
);
133 bool CanUseExtensionApis(const Extension
& extension
) {
134 return extension
.is_extension() || extension
.is_legacy_packaged_app();
137 enum VerifyAllSuccess
{
138 VERIFY_ALL_BOOTSTRAP_SUCCESS
= 0,
139 VERIFY_ALL_BOOTSTRAP_FAILURE
,
140 VERIFY_ALL_NON_BOOTSTRAP_SUCCESS
,
141 VERIFY_ALL_NON_BOOTSTRAP_FAILURE
,
143 // Used in histograms. Do not remove/reorder any entries above, and the below
144 // MAX entry should always come last.
145 VERIFY_ALL_SUCCESS_MAX
148 // Record the success or failure of verifying all extensions, and whether or
149 // not it was a bootstrapping.
150 void LogVerifyAllSuccessHistogram(bool bootstrap
, bool success
) {
151 VerifyAllSuccess result
;
152 if (bootstrap
&& success
)
153 result
= VERIFY_ALL_BOOTSTRAP_SUCCESS
;
154 else if (bootstrap
&& !success
)
155 result
= VERIFY_ALL_BOOTSTRAP_FAILURE
;
156 else if (!bootstrap
&& success
)
157 result
= VERIFY_ALL_NON_BOOTSTRAP_SUCCESS
;
159 result
= VERIFY_ALL_NON_BOOTSTRAP_FAILURE
;
161 // This used to be part of ExtensionService, but moved here. In order to keep
162 // our histograms accurate, the name is unchanged.
163 UMA_HISTOGRAM_ENUMERATION(
164 "ExtensionService.VerifyAllSuccess", result
, VERIFY_ALL_SUCCESS_MAX
);
167 // Record the success or failure of a single verification.
168 void LogAddVerifiedSuccess(bool success
) {
169 // This used to be part of ExtensionService, but moved here. In order to keep
170 // our histograms accurate, the name is unchanged.
171 UMA_HISTOGRAM_BOOLEAN("ExtensionService.AddVerified", success
);
176 InstallVerifier::InstallVerifier(ExtensionPrefs
* prefs
,
177 content::BrowserContext
* context
)
180 bootstrap_check_complete_(false),
181 weak_factory_(this) {
184 InstallVerifier::~InstallVerifier() {}
187 bool InstallVerifier::NeedsVerification(const Extension
& extension
) {
188 return IsFromStore(extension
) && CanUseExtensionApis(extension
);
194 bool InstallVerifier::IsFromStore(const Extension
& extension
) {
195 if (extension
.from_webstore() || ManifestURL::UpdatesFromGallery(&extension
))
198 // If an extension has no update url, our autoupdate code will ask the
199 // webstore about it (to aid in migrating to the webstore from self-hosting
200 // or sideloading based installs). So we want to do verification checks on
201 // such extensions too so that we don't accidentally disable old installs of
202 // extensions that did migrate to the webstore.
203 return (ManifestURL::GetUpdateURL(&extension
).is_empty() &&
204 Manifest::IsAutoUpdateableLocation(extension
.location()));
207 void InstallVerifier::Init() {
208 TRACE_EVENT0("browser,startup", "extensions::InstallVerifier::Init");
209 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ExperimentStatus",
210 GetExperimentStatus(), VERIFY_STATUS_MAX
);
211 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ActualStatus",
212 GetStatus(), VERIFY_STATUS_MAX
);
214 const base::DictionaryValue
* pref
= prefs_
->GetInstallSignature();
216 scoped_ptr
<InstallSignature
> signature_from_prefs
=
217 InstallSignature::FromValue(*pref
);
218 if (!signature_from_prefs
.get()) {
219 LogInitResultHistogram(INIT_UNPARSEABLE_PREF
);
220 } else if (!InstallSigner::VerifySignature(*signature_from_prefs
.get())) {
221 LogInitResultHistogram(INIT_INVALID_SIGNATURE
);
222 DVLOG(1) << "Init - ignoring invalid signature";
224 signature_
= signature_from_prefs
.Pass();
225 LogInitResultHistogram(INIT_VALID_SIGNATURE
);
226 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallVerifier.InitSignatureCount",
227 signature_
->ids
.size());
231 LogInitResultHistogram(INIT_NO_PREF
);
234 ExtensionSystem::Get(context_
)->ready().Post(
236 base::Bind(&InstallVerifier::MaybeBootstrapSelf
,
237 weak_factory_
.GetWeakPtr()));
240 void InstallVerifier::VerifyAllExtensions() {
241 AddMany(GetExtensionsToVerify(), ADD_ALL
);
244 base::Time
InstallVerifier::SignatureTimestamp() {
245 if (signature_
.get())
246 return signature_
->timestamp
;
251 bool InstallVerifier::IsKnownId(const std::string
& id
) const {
252 return signature_
.get() && (ContainsKey(signature_
->ids
, id
) ||
253 ContainsKey(signature_
->invalid_ids
, id
));
256 bool InstallVerifier::IsInvalid(const std::string
& id
) const {
257 return ((signature_
.get() && ContainsKey(signature_
->invalid_ids
, id
)));
260 void InstallVerifier::VerifyExtension(const std::string
& extension_id
) {
262 ids
.insert(extension_id
);
263 AddMany(ids
, ADD_SINGLE
);
266 void InstallVerifier::AddMany(const ExtensionIdSet
& ids
, OperationType type
) {
267 if (!ShouldFetchSignature()) {
268 OnVerificationComplete(true, type
); // considered successful.
272 if (signature_
.get()) {
273 ExtensionIdSet not_allowed_yet
=
274 base::STLSetDifference
<ExtensionIdSet
>(ids
, signature_
->ids
);
275 if (not_allowed_yet
.empty()) {
276 OnVerificationComplete(true, type
); // considered successful.
281 InstallVerifier::PendingOperation
* operation
=
282 new InstallVerifier::PendingOperation(type
);
283 operation
->ids
.insert(ids
.begin(), ids
.end());
285 operation_queue_
.push(linked_ptr
<PendingOperation
>(operation
));
287 // If there are no ongoing pending requests, we need to kick one off.
288 if (operation_queue_
.size() == 1)
292 void InstallVerifier::AddProvisional(const ExtensionIdSet
& ids
) {
293 provisional_
.insert(ids
.begin(), ids
.end());
294 AddMany(ids
, ADD_PROVISIONAL
);
297 void InstallVerifier::Remove(const std::string
& id
) {
303 void InstallVerifier::RemoveMany(const ExtensionIdSet
& ids
) {
304 if (!signature_
.get() || !ShouldFetchSignature())
307 bool found_any
= false;
308 for (ExtensionIdSet::const_iterator i
= ids
.begin(); i
!= ids
.end(); ++i
) {
309 if (ContainsKey(signature_
->ids
, *i
) ||
310 ContainsKey(signature_
->invalid_ids
, *i
)) {
318 InstallVerifier::PendingOperation
* operation
=
319 new InstallVerifier::PendingOperation(InstallVerifier::REMOVE
);
320 operation
->ids
= ids
;
322 operation_queue_
.push(linked_ptr
<PendingOperation
>(operation
));
323 if (operation_queue_
.size() == 1)
327 bool InstallVerifier::AllowedByEnterprisePolicy(const std::string
& id
) const {
328 return ExtensionManagementFactory::GetForBrowserContext(context_
)
329 ->IsInstallationExplicitlyAllowed(id
);
332 std::string
InstallVerifier::GetDebugPolicyProviderName() const {
333 return std::string("InstallVerifier");
338 enum MustRemainDisabledOutcome
{
342 ENTERPRISE_POLICY_ALLOWED
,
346 NOT_VERIFIED_BUT_NOT_ENFORCING
,
348 NOT_VERIFIED_BUT_INSTALL_TIME_NEWER_THAN_SIGNATURE
,
349 NOT_VERIFIED_BUT_UNKNOWN_ID
,
352 // This is used in histograms - do not remove or reorder entries above! Also
353 // the "MAX" item below should always be the last element.
354 MUST_REMAIN_DISABLED_OUTCOME_MAX
357 void MustRemainDisabledHistogram(MustRemainDisabledOutcome outcome
) {
358 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.MustRemainDisabled",
359 outcome
, MUST_REMAIN_DISABLED_OUTCOME_MAX
);
364 bool InstallVerifier::MustRemainDisabled(const Extension
* extension
,
365 Extension::DisableReason
* reason
,
366 base::string16
* error
) const {
368 if (!CanUseExtensionApis(*extension
)) {
369 MustRemainDisabledHistogram(NOT_EXTENSION
);
372 if (Manifest::IsUnpackedLocation(extension
->location())) {
373 MustRemainDisabledHistogram(UNPACKED
);
376 if (extension
->location() == Manifest::COMPONENT
) {
377 MustRemainDisabledHistogram(COMPONENT
);
380 if (AllowedByEnterprisePolicy(extension
->id())) {
381 MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED
);
385 bool verified
= true;
386 MustRemainDisabledOutcome outcome
= VERIFIED
;
387 if (ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension
->id())) {
389 outcome
= FORCED_NOT_VERIFIED
;
390 } else if (!IsFromStore(*extension
)) {
392 outcome
= NOT_FROM_STORE
;
393 } else if (signature_
.get() == NULL
&&
394 (!bootstrap_check_complete_
|| GetStatus() < ENFORCE_STRICT
)) {
395 // If we don't have a signature yet, we'll temporarily consider every
396 // extension from the webstore verified to avoid false positives on existing
397 // profiles hitting this code for the first time. The InstallVerifier
398 // will bootstrap itself once the ExtensionsSystem is ready.
399 outcome
= NO_SIGNATURE
;
400 } else if (!IsVerified(extension
->id())) {
401 if (signature_
.get() &&
402 !ContainsKey(signature_
->invalid_ids
, extension
->id())) {
403 outcome
= NOT_VERIFIED_BUT_UNKNOWN_ID
;
406 outcome
= NOT_VERIFIED
;
409 if (!verified
&& !ShouldEnforce()) {
411 outcome
= NOT_VERIFIED_BUT_NOT_ENFORCING
;
413 MustRemainDisabledHistogram(outcome
);
417 *reason
= Extension::DISABLE_NOT_VERIFIED
;
419 *error
= l10n_util::GetStringFUTF16(
420 IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE
,
421 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE
));
426 InstallVerifier::PendingOperation::PendingOperation(OperationType type
)
429 InstallVerifier::PendingOperation::~PendingOperation() {
432 ExtensionIdSet
InstallVerifier::GetExtensionsToVerify() const {
433 ExtensionIdSet result
;
434 scoped_ptr
<ExtensionSet
> extensions
=
435 ExtensionRegistry::Get(context_
)->GenerateInstalledExtensionsSet();
436 for (ExtensionSet::const_iterator iter
= extensions
->begin();
437 iter
!= extensions
->end();
439 if (NeedsVerification(*iter
->get()))
440 result
.insert((*iter
)->id());
445 void InstallVerifier::MaybeBootstrapSelf() {
446 bool needs_bootstrap
= false;
448 ExtensionIdSet extension_ids
= GetExtensionsToVerify();
449 if (signature_
.get() == NULL
&& ShouldFetchSignature()) {
450 needs_bootstrap
= true;
452 for (ExtensionIdSet::const_iterator iter
= extension_ids
.begin();
453 iter
!= extension_ids
.end();
455 if (!IsKnownId(*iter
)) {
456 needs_bootstrap
= true;
463 AddMany(extension_ids
, ADD_ALL_BOOTSTRAP
);
465 bootstrap_check_complete_
= true;
468 void InstallVerifier::OnVerificationComplete(bool success
, OperationType type
) {
471 LogAddVerifiedSuccess(success
);
474 case ADD_ALL_BOOTSTRAP
:
475 LogVerifyAllSuccessHistogram(type
== ADD_ALL_BOOTSTRAP
, success
);
476 bootstrap_check_complete_
= true;
478 // Iterate through the extensions and, if any are newly-verified and
479 // should have the DISABLE_NOT_VERIFIED reason lifted, do so.
480 const ExtensionSet
& disabled_extensions
=
481 ExtensionRegistry::Get(context_
)->disabled_extensions();
482 for (ExtensionSet::const_iterator iter
= disabled_extensions
.begin();
483 iter
!= disabled_extensions
.end();
485 int disable_reasons
= prefs_
->GetDisableReasons((*iter
)->id());
486 if (disable_reasons
& Extension::DISABLE_NOT_VERIFIED
&&
487 !MustRemainDisabled(iter
->get(), NULL
, NULL
)) {
488 prefs_
->RemoveDisableReason((*iter
)->id(),
489 Extension::DISABLE_NOT_VERIFIED
);
493 if (success
|| GetStatus() == ENFORCE_STRICT
) {
494 ExtensionSystem::Get(context_
)
495 ->extension_service()
496 ->CheckManagementPolicy();
499 // We don't need to check disable reasons or report UMA stats for
500 // provisional adds or removals.
501 case ADD_PROVISIONAL
:
507 void InstallVerifier::GarbageCollect() {
508 if (!ShouldFetchSignature()) {
511 CHECK(signature_
.get());
512 ExtensionIdSet leftovers
= signature_
->ids
;
513 leftovers
.insert(signature_
->invalid_ids
.begin(),
514 signature_
->invalid_ids
.end());
515 ExtensionIdList all_ids
;
516 prefs_
->GetExtensions(&all_ids
);
517 for (ExtensionIdList::const_iterator i
= all_ids
.begin();
518 i
!= all_ids
.end(); ++i
) {
519 ExtensionIdSet::iterator found
= leftovers
.find(*i
);
520 if (found
!= leftovers
.end())
521 leftovers
.erase(found
);
523 if (!leftovers
.empty()) {
524 RemoveMany(leftovers
);
528 bool InstallVerifier::IsVerified(const std::string
& id
) const {
529 return ((signature_
.get() && ContainsKey(signature_
->ids
, id
)) ||
530 ContainsKey(provisional_
, id
));
533 void InstallVerifier::BeginFetch() {
534 DCHECK(ShouldFetchSignature());
536 // TODO(asargent) - It would be possible to coalesce all operations in the
537 // queue into one fetch - we'd probably just need to change the queue to
538 // hold (set of ids, list of operation type) pairs.
539 CHECK(!operation_queue_
.empty());
540 const PendingOperation
& operation
= *operation_queue_
.front();
542 ExtensionIdSet ids_to_sign
;
543 if (signature_
.get()) {
544 ids_to_sign
.insert(signature_
->ids
.begin(), signature_
->ids
.end());
546 if (operation
.type
== InstallVerifier::REMOVE
) {
547 for (ExtensionIdSet::const_iterator i
= operation
.ids
.begin();
548 i
!= operation
.ids
.end(); ++i
) {
549 if (ContainsKey(ids_to_sign
, *i
))
550 ids_to_sign
.erase(*i
);
552 } else { // All other operation types are some form of "ADD".
553 ids_to_sign
.insert(operation
.ids
.begin(), operation
.ids
.end());
556 signer_
.reset(new InstallSigner(context_
->GetRequestContext(), ids_to_sign
));
557 signer_
->GetSignature(base::Bind(&InstallVerifier::SignatureCallback
,
558 weak_factory_
.GetWeakPtr()));
561 void InstallVerifier::SaveToPrefs() {
562 if (signature_
.get())
563 DCHECK(InstallSigner::VerifySignature(*signature_
));
565 if (!signature_
.get() || signature_
->ids
.empty()) {
566 DVLOG(1) << "SaveToPrefs - saving NULL";
567 prefs_
->SetInstallSignature(NULL
);
569 base::DictionaryValue pref
;
570 signature_
->ToValue(&pref
);
572 DVLOG(1) << "SaveToPrefs - saving";
574 DCHECK(InstallSigner::VerifySignature(*signature_
.get()));
575 scoped_ptr
<InstallSignature
> rehydrated
=
576 InstallSignature::FromValue(pref
);
577 DCHECK(InstallSigner::VerifySignature(*rehydrated
.get()));
579 prefs_
->SetInstallSignature(&pref
);
585 enum CallbackResult
{
586 CALLBACK_NO_SIGNATURE
= 0,
587 CALLBACK_INVALID_SIGNATURE
,
588 CALLBACK_VALID_SIGNATURE
,
590 // This is used in histograms - do not remove or reorder entries above! Also
591 // the "MAX" item below should always be the last element.
596 void GetSignatureResultHistogram(CallbackResult result
) {
597 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult",
598 result
, CALLBACK_RESULT_MAX
);
603 void InstallVerifier::SignatureCallback(
604 scoped_ptr
<InstallSignature
> signature
) {
606 linked_ptr
<PendingOperation
> operation
= operation_queue_
.front();
607 operation_queue_
.pop();
609 bool success
= false;
610 if (!signature
.get()) {
611 GetSignatureResultHistogram(CALLBACK_NO_SIGNATURE
);
612 } else if (!InstallSigner::VerifySignature(*signature
)) {
613 GetSignatureResultHistogram(CALLBACK_INVALID_SIGNATURE
);
615 GetSignatureResultHistogram(CALLBACK_VALID_SIGNATURE
);
620 OnVerificationComplete(false, operation
->type
);
622 // TODO(asargent) - if this was something like a network error, we need to
623 // do retries with exponential back off.
625 signature_
= signature
.Pass();
628 if (!provisional_
.empty()) {
629 // Update |provisional_| to remove ids that were successfully signed.
630 provisional_
= base::STLSetDifference
<ExtensionIdSet
>(
631 provisional_
, signature_
->ids
);
634 OnVerificationComplete(success
, operation
->type
);
637 if (!operation_queue_
.empty())
641 } // namespace extensions