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.
42 // This is used in histograms - do not remove or reorder entries above! Also
43 // the "MAX" item below should always be the last element.
48 #if defined(GOOGLE_CHROME_BUILD)
49 const char kExperimentName
[] = "ExtensionInstallVerification";
50 #endif // defined(GOOGLE_CHROME_BUILD)
52 VerifyStatus
GetExperimentStatus() {
53 #if defined(GOOGLE_CHROME_BUILD)
54 const std::string group
= base::FieldTrialList::FindFullName(
57 std::string forced_trials
= CommandLine::ForCurrentProcess()->
58 GetSwitchValueASCII(switches::kForceFieldTrials
);
59 if (forced_trials
.find(kExperimentName
) != std::string::npos
) {
60 // We don't want to allow turning off enforcement by forcing the field
61 // trial group to something other than enforcement.
65 VerifyStatus default_status
= NONE
;
67 if (group
== "Enforce")
69 else if (group
== "Bootstrap")
71 else if (group
== "None" || group
== "Control")
74 return default_status
;
75 #endif // defined(GOOGLE_CHROME_BUILD)
80 VerifyStatus
GetCommandLineStatus() {
81 const CommandLine
* cmdline
= CommandLine::ForCurrentProcess();
82 if (!InstallSigner::GetForcedNotFromWebstore().empty())
85 if (cmdline
->HasSwitch(switches::kExtensionsInstallVerification
)) {
86 std::string value
= cmdline
->GetSwitchValueASCII(
87 switches::kExtensionsInstallVerification
);
88 if (value
== "bootstrap")
97 VerifyStatus
GetStatus() {
98 return std::max(GetExperimentStatus(), GetCommandLineStatus());
101 bool ShouldFetchSignature() {
102 VerifyStatus status
= GetStatus();
103 return (status
== BOOTSTRAP
|| status
== ENFORCE
);
106 bool ShouldEnforce() {
107 return GetStatus() == ENFORCE
;
112 INIT_UNPARSEABLE_PREF
,
113 INIT_INVALID_SIGNATURE
,
114 INIT_VALID_SIGNATURE
,
116 // This is used in histograms - do not remove or reorder entries above! Also
117 // the "MAX" item below should always be the last element.
122 void LogInitResultHistogram(InitResult result
) {
123 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.InitResult",
124 result
, INIT_RESULT_MAX
);
127 bool FromStore(const Extension
& extension
) {
128 if (extension
.from_webstore() || ManifestURL::UpdatesFromGallery(&extension
))
131 // If an extension has no update url, our autoupdate code will ask the
132 // webstore about it (to aid in migrating to the webstore from self-hosting
133 // or sideloading based installs). So we want to do verification checks on
134 // such extensions too so that we don't accidentally disable old installs of
135 // extensions that did migrate to the webstore.
136 return (ManifestURL::GetUpdateURL(&extension
).is_empty() &&
137 Manifest::IsAutoUpdateableLocation(extension
.location()));
140 bool CanUseExtensionApis(const Extension
& extension
) {
141 return extension
.is_extension() || extension
.is_legacy_packaged_app();
144 enum VerifyAllSuccess
{
145 VERIFY_ALL_BOOTSTRAP_SUCCESS
= 0,
146 VERIFY_ALL_BOOTSTRAP_FAILURE
,
147 VERIFY_ALL_NON_BOOTSTRAP_SUCCESS
,
148 VERIFY_ALL_NON_BOOTSTRAP_FAILURE
,
150 // Used in histograms. Do not remove/reorder any entries above, and the below
151 // MAX entry should always come last.
152 VERIFY_ALL_SUCCESS_MAX
155 // Record the success or failure of verifying all extensions, and whether or
156 // not it was a bootstrapping.
157 void LogVerifyAllSuccessHistogram(bool bootstrap
, bool success
) {
158 VerifyAllSuccess result
;
159 if (bootstrap
&& success
)
160 result
= VERIFY_ALL_BOOTSTRAP_SUCCESS
;
161 else if (bootstrap
&& !success
)
162 result
= VERIFY_ALL_BOOTSTRAP_FAILURE
;
163 else if (!bootstrap
&& success
)
164 result
= VERIFY_ALL_NON_BOOTSTRAP_SUCCESS
;
166 result
= VERIFY_ALL_NON_BOOTSTRAP_FAILURE
;
168 // This used to be part of ExtensionService, but moved here. In order to keep
169 // our histograms accurate, the name is unchanged.
170 UMA_HISTOGRAM_ENUMERATION(
171 "ExtensionService.VerifyAllSuccess", result
, VERIFY_ALL_SUCCESS_MAX
);
174 // Record the success or failure of a single verification.
175 void LogAddVerifiedSuccess(bool success
) {
176 // This used to be part of ExtensionService, but moved here. In order to keep
177 // our histograms accurate, the name is unchanged.
178 UMA_HISTOGRAM_BOOLEAN("ExtensionService.AddVerified", success
);
183 InstallVerifier::InstallVerifier(ExtensionPrefs
* prefs
,
184 content::BrowserContext
* context
)
185 : prefs_(prefs
), context_(context
), weak_factory_(this) {}
187 InstallVerifier::~InstallVerifier() {}
190 bool InstallVerifier::NeedsVerification(const Extension
& extension
) {
191 return FromStore(extension
) && CanUseExtensionApis(extension
);
194 void InstallVerifier::Init() {
195 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ExperimentStatus",
196 GetExperimentStatus(), VERIFY_STATUS_MAX
);
197 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ActualStatus",
198 GetStatus(), VERIFY_STATUS_MAX
);
200 const base::DictionaryValue
* pref
= prefs_
->GetInstallSignature();
202 scoped_ptr
<InstallSignature
> signature_from_prefs
=
203 InstallSignature::FromValue(*pref
);
204 if (!signature_from_prefs
.get()) {
205 LogInitResultHistogram(INIT_UNPARSEABLE_PREF
);
206 } else if (!InstallSigner::VerifySignature(*signature_from_prefs
.get())) {
207 LogInitResultHistogram(INIT_INVALID_SIGNATURE
);
208 DVLOG(1) << "Init - ignoring invalid signature";
210 signature_
= signature_from_prefs
.Pass();
211 LogInitResultHistogram(INIT_VALID_SIGNATURE
);
212 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallVerifier.InitSignatureCount",
213 signature_
->ids
.size());
217 LogInitResultHistogram(INIT_NO_PREF
);
220 ExtensionSystem::Get(context_
)->ready().Post(
222 base::Bind(&InstallVerifier::MaybeBootstrapSelf
,
223 weak_factory_
.GetWeakPtr()));
226 void InstallVerifier::VerifyAllExtensions() {
227 AddMany(GetExtensionsToVerify(), ADD_ALL
);
230 base::Time
InstallVerifier::SignatureTimestamp() {
231 if (signature_
.get())
232 return signature_
->timestamp
;
237 bool InstallVerifier::IsKnownId(const std::string
& id
) {
238 return signature_
.get() && (ContainsKey(signature_
->ids
, id
) ||
239 ContainsKey(signature_
->invalid_ids
, id
));
242 void InstallVerifier::VerifyExtension(const std::string
& extension_id
) {
244 ids
.insert(extension_id
);
245 AddMany(ids
, ADD_SINGLE
);
248 void InstallVerifier::AddMany(const ExtensionIdSet
& ids
, OperationType type
) {
249 if (!ShouldFetchSignature()) {
250 OnVerificationComplete(true, type
); // considered successful.
254 if (signature_
.get()) {
255 ExtensionIdSet not_allowed_yet
=
256 base::STLSetDifference
<ExtensionIdSet
>(ids
, signature_
->ids
);
257 if (not_allowed_yet
.empty()) {
258 OnVerificationComplete(true, type
); // considered successful.
263 InstallVerifier::PendingOperation
* operation
=
264 new InstallVerifier::PendingOperation(type
);
265 operation
->ids
.insert(ids
.begin(), ids
.end());
267 operation_queue_
.push(linked_ptr
<PendingOperation
>(operation
));
269 // If there are no ongoing pending requests, we need to kick one off.
270 if (operation_queue_
.size() == 1)
274 void InstallVerifier::AddProvisional(const ExtensionIdSet
& ids
) {
275 provisional_
.insert(ids
.begin(), ids
.end());
276 AddMany(ids
, ADD_PROVISIONAL
);
279 void InstallVerifier::Remove(const std::string
& id
) {
285 void InstallVerifier::RemoveMany(const ExtensionIdSet
& ids
) {
286 if (!signature_
.get() || !ShouldFetchSignature())
289 bool found_any
= false;
290 for (ExtensionIdSet::const_iterator i
= ids
.begin(); i
!= ids
.end(); ++i
) {
291 if (ContainsKey(signature_
->ids
, *i
) ||
292 ContainsKey(signature_
->invalid_ids
, *i
)) {
300 InstallVerifier::PendingOperation
* operation
=
301 new InstallVerifier::PendingOperation(InstallVerifier::REMOVE
);
302 operation
->ids
= ids
;
304 operation_queue_
.push(linked_ptr
<PendingOperation
>(operation
));
305 if (operation_queue_
.size() == 1)
309 std::string
InstallVerifier::GetDebugPolicyProviderName() const {
310 return std::string("InstallVerifier");
315 enum MustRemainDisabledOutcome
{
319 ENTERPRISE_POLICY_ALLOWED
,
323 NOT_VERIFIED_BUT_NOT_ENFORCING
,
325 NOT_VERIFIED_BUT_INSTALL_TIME_NEWER_THAN_SIGNATURE
,
326 NOT_VERIFIED_BUT_UNKNOWN_ID
,
329 // This is used in histograms - do not remove or reorder entries above! Also
330 // the "MAX" item below should always be the last element.
331 MUST_REMAIN_DISABLED_OUTCOME_MAX
334 void MustRemainDisabledHistogram(MustRemainDisabledOutcome outcome
) {
335 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.MustRemainDisabled",
336 outcome
, MUST_REMAIN_DISABLED_OUTCOME_MAX
);
341 bool InstallVerifier::MustRemainDisabled(const Extension
* extension
,
342 Extension::DisableReason
* reason
,
343 base::string16
* error
) const {
345 if (!CanUseExtensionApis(*extension
)) {
346 MustRemainDisabledHistogram(NOT_EXTENSION
);
349 if (Manifest::IsUnpackedLocation(extension
->location())) {
350 MustRemainDisabledHistogram(UNPACKED
);
353 if (extension
->location() == Manifest::COMPONENT
) {
354 MustRemainDisabledHistogram(COMPONENT
);
357 if (AllowedByEnterprisePolicy(extension
->id())) {
358 MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED
);
362 bool verified
= true;
363 MustRemainDisabledOutcome outcome
= VERIFIED
;
364 if (ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension
->id())) {
366 outcome
= FORCED_NOT_VERIFIED
;
367 } else if (!FromStore(*extension
)) {
369 outcome
= NOT_FROM_STORE
;
370 } else if (signature_
.get() == NULL
) {
371 // If we don't have a signature yet, we'll temporarily consider every
372 // extension from the webstore verified to avoid false positives on existing
373 // profiles hitting this code for the first time. The InstallVerifier
374 // will bootstrap itself once the ExtensionsSystem is ready.
375 outcome
= NO_SIGNATURE
;
376 } else if (!IsVerified(extension
->id())) {
377 if (!ContainsKey(signature_
->invalid_ids
, extension
->id())) {
378 outcome
= NOT_VERIFIED_BUT_UNKNOWN_ID
;
381 outcome
= NOT_VERIFIED
;
384 if (!verified
&& !ShouldEnforce()) {
386 outcome
= NOT_VERIFIED_BUT_NOT_ENFORCING
;
388 MustRemainDisabledHistogram(outcome
);
392 *reason
= Extension::DISABLE_NOT_VERIFIED
;
394 *error
= l10n_util::GetStringFUTF16(
395 IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE
,
396 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE
));
401 InstallVerifier::PendingOperation::PendingOperation(OperationType type
)
404 InstallVerifier::PendingOperation::~PendingOperation() {
407 ExtensionIdSet
InstallVerifier::GetExtensionsToVerify() const {
408 ExtensionIdSet result
;
409 scoped_ptr
<ExtensionSet
> extensions
=
410 ExtensionRegistry::Get(context_
)->GenerateInstalledExtensionsSet();
411 for (ExtensionSet::const_iterator iter
= extensions
->begin();
412 iter
!= extensions
->end();
414 if (NeedsVerification(**iter
))
415 result
.insert((*iter
)->id());
420 void InstallVerifier::MaybeBootstrapSelf() {
421 bool needs_bootstrap
= false;
423 ExtensionIdSet extension_ids
= GetExtensionsToVerify();
424 if (signature_
.get() == NULL
&& ShouldFetchSignature()) {
425 needs_bootstrap
= true;
427 for (ExtensionIdSet::const_iterator iter
= extension_ids
.begin();
428 iter
!= extension_ids
.end();
430 if (!IsKnownId(*iter
)) {
431 needs_bootstrap
= true;
438 AddMany(extension_ids
, ADD_ALL_BOOTSTRAP
);
441 void InstallVerifier::OnVerificationComplete(bool success
,
442 OperationType type
) const {
445 LogAddVerifiedSuccess(success
);
448 case ADD_ALL_BOOTSTRAP
:
449 LogVerifyAllSuccessHistogram(type
== ADD_ALL_BOOTSTRAP
, success
);
451 // Iterate through the extensions and, if any are newly-verified and
452 // should have the DISABLE_NOT_VERIFIED reason lifted, do so.
453 const ExtensionSet
& disabled_extensions
=
454 ExtensionRegistry::Get(context_
)->disabled_extensions();
455 for (ExtensionSet::const_iterator iter
= disabled_extensions
.begin();
456 iter
!= disabled_extensions
.end();
458 int disable_reasons
= prefs_
->GetDisableReasons((*iter
)->id());
459 if (disable_reasons
& Extension::DISABLE_NOT_VERIFIED
&&
460 !MustRemainDisabled(*iter
, NULL
, NULL
)) {
461 prefs_
->RemoveDisableReason((*iter
)->id(),
462 Extension::DISABLE_NOT_VERIFIED
);
466 ExtensionSystem::Get(context_
)
467 ->extension_service()
468 ->CheckManagementPolicy();
471 // We don't need to check disable reasons or report UMA stats for
472 // provisional adds or removals.
473 case ADD_PROVISIONAL
:
479 void InstallVerifier::GarbageCollect() {
480 if (!ShouldFetchSignature()) {
483 CHECK(signature_
.get());
484 ExtensionIdSet leftovers
= signature_
->ids
;
485 leftovers
.insert(signature_
->invalid_ids
.begin(),
486 signature_
->invalid_ids
.end());
487 ExtensionIdList all_ids
;
488 prefs_
->GetExtensions(&all_ids
);
489 for (ExtensionIdList::const_iterator i
= all_ids
.begin();
490 i
!= all_ids
.end(); ++i
) {
491 ExtensionIdSet::iterator found
= leftovers
.find(*i
);
492 if (found
!= leftovers
.end())
493 leftovers
.erase(found
);
495 if (!leftovers
.empty()) {
496 RemoveMany(leftovers
);
500 bool InstallVerifier::AllowedByEnterprisePolicy(const std::string
& id
) const {
501 PrefService
* pref_service
= prefs_
->pref_service();
502 if (pref_service
->IsManagedPreference(pref_names::kInstallAllowList
)) {
503 const base::ListValue
* whitelist
=
504 pref_service
->GetList(pref_names::kInstallAllowList
);
505 base::StringValue
id_value(id
);
506 if (whitelist
&& whitelist
->Find(id_value
) != whitelist
->end())
509 if (pref_service
->IsManagedPreference(pref_names::kInstallForceList
)) {
510 const base::DictionaryValue
* forcelist
=
511 pref_service
->GetDictionary(pref_names::kInstallForceList
);
512 if (forcelist
&& forcelist
->HasKey(id
))
518 bool InstallVerifier::IsVerified(const std::string
& id
) const {
519 return ((signature_
.get() && ContainsKey(signature_
->ids
, id
)) ||
520 ContainsKey(provisional_
, id
));
523 void InstallVerifier::BeginFetch() {
524 DCHECK(ShouldFetchSignature());
526 // TODO(asargent) - It would be possible to coalesce all operations in the
527 // queue into one fetch - we'd probably just need to change the queue to
528 // hold (set of ids, list of operation type) pairs.
529 CHECK(!operation_queue_
.empty());
530 const PendingOperation
& operation
= *operation_queue_
.front();
532 ExtensionIdSet ids_to_sign
;
533 if (signature_
.get()) {
534 ids_to_sign
.insert(signature_
->ids
.begin(), signature_
->ids
.end());
536 if (operation
.type
== InstallVerifier::REMOVE
) {
537 for (ExtensionIdSet::const_iterator i
= operation
.ids
.begin();
538 i
!= operation
.ids
.end(); ++i
) {
539 if (ContainsKey(ids_to_sign
, *i
))
540 ids_to_sign
.erase(*i
);
542 } else { // All other operation types are some form of "ADD".
543 ids_to_sign
.insert(operation
.ids
.begin(), operation
.ids
.end());
546 signer_
.reset(new InstallSigner(context_
->GetRequestContext(), ids_to_sign
));
547 signer_
->GetSignature(base::Bind(&InstallVerifier::SignatureCallback
,
548 weak_factory_
.GetWeakPtr()));
551 void InstallVerifier::SaveToPrefs() {
552 if (signature_
.get())
553 DCHECK(InstallSigner::VerifySignature(*signature_
));
555 if (!signature_
.get() || signature_
->ids
.empty()) {
556 DVLOG(1) << "SaveToPrefs - saving NULL";
557 prefs_
->SetInstallSignature(NULL
);
559 base::DictionaryValue pref
;
560 signature_
->ToValue(&pref
);
562 DVLOG(1) << "SaveToPrefs - saving";
564 DCHECK(InstallSigner::VerifySignature(*signature_
.get()));
565 scoped_ptr
<InstallSignature
> rehydrated
=
566 InstallSignature::FromValue(pref
);
567 DCHECK(InstallSigner::VerifySignature(*rehydrated
.get()));
569 prefs_
->SetInstallSignature(&pref
);
575 enum CallbackResult
{
576 CALLBACK_NO_SIGNATURE
= 0,
577 CALLBACK_INVALID_SIGNATURE
,
578 CALLBACK_VALID_SIGNATURE
,
580 // This is used in histograms - do not remove or reorder entries above! Also
581 // the "MAX" item below should always be the last element.
586 void GetSignatureResultHistogram(CallbackResult result
) {
587 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult",
588 result
, CALLBACK_RESULT_MAX
);
593 void InstallVerifier::SignatureCallback(
594 scoped_ptr
<InstallSignature
> signature
) {
596 linked_ptr
<PendingOperation
> operation
= operation_queue_
.front();
597 operation_queue_
.pop();
599 bool success
= false;
600 if (!signature
.get()) {
601 GetSignatureResultHistogram(CALLBACK_NO_SIGNATURE
);
602 } else if (!InstallSigner::VerifySignature(*signature
)) {
603 GetSignatureResultHistogram(CALLBACK_INVALID_SIGNATURE
);
605 GetSignatureResultHistogram(CALLBACK_VALID_SIGNATURE
);
610 OnVerificationComplete(false, operation
->type
);
612 // TODO(asargent) - if this was something like a network error, we need to
613 // do retries with exponential back off.
615 signature_
= signature
.Pass();
618 if (!provisional_
.empty()) {
619 // Update |provisional_| to remove ids that were successfully signed.
620 provisional_
= base::STLSetDifference
<ExtensionIdSet
>(
621 provisional_
, signature_
->ids
);
624 OnVerificationComplete(success
, operation
->type
);
627 if (!operation_queue_
.empty())
631 } // namespace extensions