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/install_signer.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/common/extensions/manifest_url_handler.h"
19 #include "chrome/common/pref_names.h"
20 #include "content/public/common/content_switches.h"
21 #include "extensions/browser/extension_prefs.h"
22 #include "extensions/browser/pref_names.h"
23 #include "extensions/common/manifest.h"
24 #include "grit/generated_resources.h"
25 #include "ui/base/l10n/l10n_util.h"
30 NONE
= 0, // Do not request install signatures, and do not enforce them.
31 BOOTSTRAP
, // Request install signatures, but do not enforce them.
32 ENFORCE
, // Request install signatures, and enforce them.
34 // This is used in histograms - do not remove or reorder entries above! Also
35 // the "MAX" item below should always be the last element.
40 #if defined(GOOGLE_CHROME_BUILD)
41 const char kExperimentName
[] = "ExtensionInstallVerification";
42 #endif // defined(GOOGLE_CHROME_BUILD)
44 VerifyStatus
GetExperimentStatus() {
45 #if defined(GOOGLE_CHROME_BUILD)
46 const std::string group
= base::FieldTrialList::FindFullName(
49 std::string forced_trials
= CommandLine::ForCurrentProcess()->
50 GetSwitchValueASCII(switches::kForceFieldTrials
);
51 if (forced_trials
.find(kExperimentName
) != std::string::npos
) {
52 // We don't want to allow turning off enforcement by forcing the field
53 // trial group to something other than enforcement.
57 VerifyStatus default_status
= NONE
;
59 if (group
== "Enforce")
61 else if (group
== "Bootstrap")
63 else if (group
== "None" || group
== "Control")
66 return default_status
;
67 #endif // defined(GOOGLE_CHROME_BUILD)
72 VerifyStatus
GetCommandLineStatus() {
73 const CommandLine
* cmdline
= CommandLine::ForCurrentProcess();
74 if (!extensions::InstallSigner::GetForcedNotFromWebstore().empty())
77 if (cmdline
->HasSwitch(switches::kExtensionsInstallVerification
)) {
78 std::string value
= cmdline
->GetSwitchValueASCII(
79 switches::kExtensionsInstallVerification
);
80 if (value
== "bootstrap")
89 VerifyStatus
GetStatus() {
90 return std::max(GetExperimentStatus(), GetCommandLineStatus());
93 bool ShouldFetchSignature() {
94 VerifyStatus status
= GetStatus();
95 return (status
== BOOTSTRAP
|| status
== ENFORCE
);
98 bool ShouldEnforce() {
99 return GetStatus() == ENFORCE
;
104 namespace extensions
{
106 InstallVerifier::InstallVerifier(ExtensionPrefs
* prefs
,
107 net::URLRequestContextGetter
* context_getter
)
108 : prefs_(prefs
), context_getter_(context_getter
) {
111 InstallVerifier::~InstallVerifier() {}
117 INIT_UNPARSEABLE_PREF
,
118 INIT_INVALID_SIGNATURE
,
119 INIT_VALID_SIGNATURE
,
121 // This is used in histograms - do not remove or reorder entries above! Also
122 // the "MAX" item below should always be the last element.
127 void LogInitResultHistogram(InitResult result
) {
128 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.InitResult",
129 result
, INIT_RESULT_MAX
);
132 bool FromStore(const Extension
& extension
) {
133 bool updates_from_store
= ManifestURL::UpdatesFromGallery(&extension
);
134 return extension
.from_webstore() || updates_from_store
;
137 bool CanUseExtensionApis(const Extension
& extension
) {
138 return extension
.is_extension() || extension
.is_legacy_packaged_app();
144 bool InstallVerifier::NeedsVerification(const Extension
& extension
) {
145 return FromStore(extension
) && CanUseExtensionApis(extension
);
148 void InstallVerifier::Init() {
149 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ExperimentStatus",
150 GetExperimentStatus(), VERIFY_STATUS_MAX
);
151 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ActualStatus",
152 GetStatus(), VERIFY_STATUS_MAX
);
154 const base::DictionaryValue
* pref
= prefs_
->GetInstallSignature();
156 scoped_ptr
<InstallSignature
> signature_from_prefs
=
157 InstallSignature::FromValue(*pref
);
158 if (!signature_from_prefs
.get()) {
159 LogInitResultHistogram(INIT_UNPARSEABLE_PREF
);
160 } else if (!InstallSigner::VerifySignature(*signature_from_prefs
.get())) {
161 LogInitResultHistogram(INIT_INVALID_SIGNATURE
);
162 DVLOG(1) << "Init - ignoring invalid signature";
164 signature_
= signature_from_prefs
.Pass();
165 LogInitResultHistogram(INIT_VALID_SIGNATURE
);
166 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallVerifier.InitSignatureCount",
167 signature_
->ids
.size());
171 LogInitResultHistogram(INIT_NO_PREF
);
175 bool InstallVerifier::NeedsBootstrap() {
176 return signature_
.get() == NULL
&& ShouldFetchSignature();
179 base::Time
InstallVerifier::SignatureTimestamp() {
180 if (signature_
.get())
181 return signature_
->timestamp
;
186 void InstallVerifier::Add(const std::string
& id
,
187 const AddResultCallback
& callback
) {
190 AddMany(ids
, callback
);
193 void InstallVerifier::AddMany(const ExtensionIdSet
& ids
,
194 const AddResultCallback
& callback
) {
195 if (!ShouldFetchSignature()) {
196 if (!callback
.is_null())
201 if (signature_
.get()) {
202 ExtensionIdSet not_allowed_yet
=
203 base::STLSetDifference
<ExtensionIdSet
>(ids
, signature_
->ids
);
204 if (not_allowed_yet
.empty()) {
205 if (!callback
.is_null())
211 InstallVerifier::PendingOperation
* operation
=
212 new InstallVerifier::PendingOperation();
213 operation
->type
= InstallVerifier::ADD
;
214 operation
->ids
.insert(ids
.begin(), ids
.end());
215 operation
->callback
= callback
;
217 operation_queue_
.push(linked_ptr
<PendingOperation
>(operation
));
219 // If there are no ongoing pending requests, we need to kick one off.
220 if (operation_queue_
.size() == 1)
224 void InstallVerifier::AddProvisional(const ExtensionIdSet
& ids
) {
225 provisional_
.insert(ids
.begin(), ids
.end());
226 AddMany(ids
, AddResultCallback());
229 void InstallVerifier::Remove(const std::string
& id
) {
235 void InstallVerifier::RemoveMany(const ExtensionIdSet
& ids
) {
236 if (!signature_
.get() || !ShouldFetchSignature())
239 bool found_any
= false;
240 for (ExtensionIdSet::const_iterator i
= ids
.begin(); i
!= ids
.end(); ++i
) {
241 if (ContainsKey(signature_
->ids
, *i
)) {
249 InstallVerifier::PendingOperation
* operation
=
250 new InstallVerifier::PendingOperation();
251 operation
->type
= InstallVerifier::REMOVE
;
252 operation
->ids
= ids
;
254 operation_queue_
.push(linked_ptr
<PendingOperation
>(operation
));
255 if (operation_queue_
.size() == 1)
259 std::string
InstallVerifier::GetDebugPolicyProviderName() const {
260 return std::string("InstallVerifier");
265 enum MustRemainDisabledOutcome
{
269 ENTERPRISE_POLICY_ALLOWED
,
273 NOT_VERIFIED_BUT_NOT_ENFORCING
,
275 NOT_VERIFIED_BUT_INSTALL_TIME_NEWER_THAN_SIGNATURE
,
277 // This is used in histograms - do not remove or reorder entries above! Also
278 // the "MAX" item below should always be the last element.
279 MUST_REMAIN_DISABLED_OUTCOME_MAX
282 void MustRemainDisabledHistogram(MustRemainDisabledOutcome outcome
) {
283 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.MustRemainDisabled",
284 outcome
, MUST_REMAIN_DISABLED_OUTCOME_MAX
);
289 bool InstallVerifier::MustRemainDisabled(const Extension
* extension
,
290 Extension::DisableReason
* reason
,
291 base::string16
* error
) const {
293 if (!CanUseExtensionApis(*extension
)) {
294 MustRemainDisabledHistogram(NOT_EXTENSION
);
297 if (Manifest::IsUnpackedLocation(extension
->location())) {
298 MustRemainDisabledHistogram(UNPACKED
);
301 if (AllowedByEnterprisePolicy(extension
->id())) {
302 MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED
);
306 bool verified
= true;
307 MustRemainDisabledOutcome outcome
= VERIFIED
;
308 if (ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension
->id())) {
310 outcome
= FORCED_NOT_VERIFIED
;
311 } else if (!FromStore(*extension
)) {
313 outcome
= NOT_FROM_STORE
;
314 } else if (signature_
.get() == NULL
) {
315 // If we don't have a signature yet, we'll temporarily consider every
316 // extension from the webstore verified to avoid false positives on existing
317 // profiles hitting this code for the first time, and rely on consumers of
318 // this class to check NeedsBootstrap() and schedule a first check so we can
320 outcome
= NO_SIGNATURE
;
321 } else if (!IsVerified(extension
->id())) {
322 if (WasInstalledAfterSignature(extension
->id())) {
323 outcome
= NOT_VERIFIED_BUT_INSTALL_TIME_NEWER_THAN_SIGNATURE
;
326 outcome
= NOT_VERIFIED
;
329 if (!verified
&& !ShouldEnforce()) {
331 outcome
= NOT_VERIFIED_BUT_NOT_ENFORCING
;
333 MustRemainDisabledHistogram(outcome
);
337 *reason
= Extension::DISABLE_NOT_VERIFIED
;
339 *error
= l10n_util::GetStringFUTF16(
340 IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE
,
341 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE
));
346 InstallVerifier::PendingOperation::PendingOperation() {
347 type
= InstallVerifier::ADD
;
350 InstallVerifier::PendingOperation::~PendingOperation() {
353 void InstallVerifier::GarbageCollect() {
354 if (!ShouldFetchSignature()) {
357 CHECK(signature_
.get());
358 ExtensionIdSet leftovers
= signature_
->ids
;
359 ExtensionIdList all_ids
;
360 prefs_
->GetExtensions(&all_ids
);
361 for (ExtensionIdList::const_iterator i
= all_ids
.begin();
362 i
!= all_ids
.end(); ++i
) {
363 ExtensionIdSet::iterator found
= leftovers
.find(*i
);
364 if (found
!= leftovers
.end())
365 leftovers
.erase(found
);
367 if (!leftovers
.empty()) {
368 RemoveMany(leftovers
);
372 bool InstallVerifier::AllowedByEnterprisePolicy(const std::string
& id
) const {
373 PrefService
* pref_service
= prefs_
->pref_service();
374 if (pref_service
->IsManagedPreference(pref_names::kInstallAllowList
)) {
375 const base::ListValue
* whitelist
=
376 pref_service
->GetList(pref_names::kInstallAllowList
);
377 base::StringValue
id_value(id
);
378 if (whitelist
&& whitelist
->Find(id_value
) != whitelist
->end())
381 if (pref_service
->IsManagedPreference(pref_names::kInstallForceList
)) {
382 const base::DictionaryValue
* forcelist
=
383 pref_service
->GetDictionary(pref_names::kInstallForceList
);
384 if (forcelist
&& forcelist
->HasKey(id
))
390 bool InstallVerifier::IsVerified(const std::string
& id
) const {
391 return ((signature_
.get() && ContainsKey(signature_
->ids
, id
)) ||
392 ContainsKey(provisional_
, id
));
395 bool InstallVerifier::WasInstalledAfterSignature(const std::string
& id
) const {
396 if (!signature_
.get() || signature_
->timestamp
.is_null())
399 base::Time install_time
= prefs_
->GetInstallTime(id
);
400 // If the extension install time is in the future, just assume it isn't
401 // newer than the signature. (Either the clock went backwards, or
402 // an attacker changed the install time in the preferences).
403 if (install_time
>= base::Time::Now())
405 return install_time
> signature_
->timestamp
;
408 void InstallVerifier::BeginFetch() {
409 DCHECK(ShouldFetchSignature());
411 // TODO(asargent) - It would be possible to coalesce all operations in the
412 // queue into one fetch - we'd probably just need to change the queue to
413 // hold (set of ids, list of callbacks) pairs.
414 CHECK(!operation_queue_
.empty());
415 const PendingOperation
& operation
= *operation_queue_
.front();
417 ExtensionIdSet ids_to_sign
;
418 if (signature_
.get()) {
419 ids_to_sign
.insert(signature_
->ids
.begin(), signature_
->ids
.end());
421 if (operation
.type
== InstallVerifier::ADD
) {
422 ids_to_sign
.insert(operation
.ids
.begin(), operation
.ids
.end());
424 for (ExtensionIdSet::const_iterator i
= operation
.ids
.begin();
425 i
!= operation
.ids
.end(); ++i
) {
426 if (ContainsKey(ids_to_sign
, *i
))
427 ids_to_sign
.erase(*i
);
431 signer_
.reset(new InstallSigner(context_getter_
, ids_to_sign
));
432 signer_
->GetSignature(base::Bind(&InstallVerifier::SignatureCallback
,
433 base::Unretained(this)));
436 void InstallVerifier::SaveToPrefs() {
437 if (signature_
.get())
438 DCHECK(InstallSigner::VerifySignature(*signature_
));
440 if (!signature_
.get() || signature_
->ids
.empty()) {
441 DVLOG(1) << "SaveToPrefs - saving NULL";
442 prefs_
->SetInstallSignature(NULL
);
444 base::DictionaryValue pref
;
445 signature_
->ToValue(&pref
);
447 DVLOG(1) << "SaveToPrefs - saving";
449 DCHECK(InstallSigner::VerifySignature(*signature_
.get()));
450 scoped_ptr
<InstallSignature
> rehydrated
=
451 InstallSignature::FromValue(pref
);
452 DCHECK(InstallSigner::VerifySignature(*rehydrated
.get()));
454 prefs_
->SetInstallSignature(&pref
);
460 enum CallbackResult
{
461 CALLBACK_NO_SIGNATURE
= 0,
462 CALLBACK_INVALID_SIGNATURE
,
463 CALLBACK_VALID_SIGNATURE
,
465 // This is used in histograms - do not remove or reorder entries above! Also
466 // the "MAX" item below should always be the last element.
471 void GetSignatureResultHistogram(CallbackResult result
) {
472 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult",
473 result
, CALLBACK_RESULT_MAX
);
478 void InstallVerifier::SignatureCallback(
479 scoped_ptr
<InstallSignature
> signature
) {
481 linked_ptr
<PendingOperation
> operation
= operation_queue_
.front();
482 operation_queue_
.pop();
484 bool success
= false;
485 if (!signature
.get()) {
486 GetSignatureResultHistogram(CALLBACK_NO_SIGNATURE
);
487 } else if (!InstallSigner::VerifySignature(*signature
)) {
488 GetSignatureResultHistogram(CALLBACK_INVALID_SIGNATURE
);
490 GetSignatureResultHistogram(CALLBACK_VALID_SIGNATURE
);
495 if (!operation
->callback
.is_null())
496 operation
->callback
.Run(false);
498 // TODO(asargent) - if this was something like a network error, we need to
499 // do retries with exponential back off.
501 signature_
= signature
.Pass();
504 if (!provisional_
.empty()) {
505 // Update |provisional_| to remove ids that were successfully signed.
506 provisional_
= base::STLSetDifference
<ExtensionIdSet
>(
507 provisional_
, signature_
->ids
);
510 if (!operation
->callback
.is_null())
511 operation
->callback
.Run(success
);
514 if (!operation_queue_
.empty())
519 } // namespace extensions