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_prefs.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/common/content_switches.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.
35 #if defined(GOOGLE_CHROME_BUILD)
36 const char kExperimentName
[] = "ExtensionInstallVerification";
37 #endif // defined(GOOGLE_CHROME_BUILD)
39 VerifyStatus
GetExperimentStatus() {
40 #if defined(GOOGLE_CHROME_BUILD)
41 const std::string group
= base::FieldTrialList::FindFullName(
44 std::string forced_trials
= CommandLine::ForCurrentProcess()->
45 GetSwitchValueASCII(switches::kForceFieldTrials
);
46 if (forced_trials
.find(kExperimentName
) != std::string::npos
) {
47 // We don't want to allow turning off enforcement by forcing the field
48 // trial group to something other than enforcement.
52 VerifyStatus default_status
= BOOTSTRAP
;
54 if (group
== "Enforce")
56 else if (group
== "Bootstrap")
58 else if (group
== "None" || group
== "Control")
61 return default_status
;
62 #endif // defined(GOOGLE_CHROME_BUILD)
67 VerifyStatus
GetCommandLineStatus() {
68 const CommandLine
* cmdline
= CommandLine::ForCurrentProcess();
69 if (!extensions::InstallSigner::GetForcedNotFromWebstore().empty())
72 if (cmdline
->HasSwitch(switches::kExtensionsInstallVerification
)) {
73 std::string value
= cmdline
->GetSwitchValueASCII(
74 switches::kExtensionsInstallVerification
);
75 if (value
== "bootstrap")
84 VerifyStatus
GetStatus() {
85 return std::max(GetExperimentStatus(), GetCommandLineStatus());
88 bool ShouldFetchSignature() {
89 VerifyStatus status
= GetStatus();
90 return (status
== BOOTSTRAP
|| status
== ENFORCE
);
93 bool ShouldEnforce() {
94 return GetStatus() == ENFORCE
;
99 namespace extensions
{
101 InstallVerifier::InstallVerifier(ExtensionPrefs
* prefs
,
102 net::URLRequestContextGetter
* context_getter
)
103 : prefs_(prefs
), context_getter_(context_getter
) {
106 InstallVerifier::~InstallVerifier() {}
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 bool updates_from_store
= ManifestURL::UpdatesFromGallery(&extension
);
129 return extension
.from_webstore() || updates_from_store
;
132 bool CanUseExtensionApis(const Extension
& extension
) {
133 return extension
.is_extension() || extension
.is_legacy_packaged_app();
139 bool InstallVerifier::NeedsVerification(const Extension
& extension
) {
140 return FromStore(extension
) && CanUseExtensionApis(extension
);
143 void InstallVerifier::Init() {
144 const base::DictionaryValue
* pref
= prefs_
->GetInstallSignature();
146 scoped_ptr
<InstallSignature
> signature_from_prefs
=
147 InstallSignature::FromValue(*pref
);
148 if (!signature_from_prefs
.get()) {
149 LogInitResultHistogram(INIT_UNPARSEABLE_PREF
);
150 } else if (!InstallSigner::VerifySignature(*signature_from_prefs
.get())) {
151 LogInitResultHistogram(INIT_INVALID_SIGNATURE
);
152 DVLOG(1) << "Init - ignoring invalid signature";
154 signature_
= signature_from_prefs
.Pass();
155 LogInitResultHistogram(INIT_VALID_SIGNATURE
);
156 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallVerifier.InitSignatureCount",
157 signature_
->ids
.size());
161 LogInitResultHistogram(INIT_NO_PREF
);
165 bool InstallVerifier::NeedsBootstrap() {
166 return signature_
.get() == NULL
&& ShouldFetchSignature();
169 void InstallVerifier::Add(const std::string
& id
,
170 const AddResultCallback
& callback
) {
173 AddMany(ids
, callback
);
176 void InstallVerifier::AddMany(const ExtensionIdSet
& ids
,
177 const AddResultCallback
& callback
) {
178 if (!ShouldFetchSignature()) {
179 if (!callback
.is_null())
184 if (signature_
.get()) {
185 ExtensionIdSet not_allowed_yet
=
186 base::STLSetDifference
<ExtensionIdSet
>(ids
, signature_
->ids
);
187 if (not_allowed_yet
.empty()) {
188 if (!callback
.is_null())
194 InstallVerifier::PendingOperation
* operation
=
195 new InstallVerifier::PendingOperation();
196 operation
->type
= InstallVerifier::ADD
;
197 operation
->ids
.insert(ids
.begin(), ids
.end());
198 operation
->callback
= callback
;
200 operation_queue_
.push(linked_ptr
<PendingOperation
>(operation
));
202 // If there are no ongoing pending requests, we need to kick one off.
203 if (operation_queue_
.size() == 1)
207 void InstallVerifier::AddProvisional(const ExtensionIdSet
& ids
) {
208 provisional_
.insert(ids
.begin(), ids
.end());
209 AddMany(ids
, AddResultCallback());
212 void InstallVerifier::Remove(const std::string
& id
) {
218 void InstallVerifier::RemoveMany(const ExtensionIdSet
& ids
) {
219 if (!signature_
.get() || !ShouldFetchSignature())
222 bool found_any
= false;
223 for (ExtensionIdSet::const_iterator i
= ids
.begin(); i
!= ids
.end(); ++i
) {
224 if (ContainsKey(signature_
->ids
, *i
)) {
232 InstallVerifier::PendingOperation
* operation
=
233 new InstallVerifier::PendingOperation();
234 operation
->type
= InstallVerifier::REMOVE
;
235 operation
->ids
= ids
;
237 operation_queue_
.push(linked_ptr
<PendingOperation
>(operation
));
238 if (operation_queue_
.size() == 1)
242 std::string
InstallVerifier::GetDebugPolicyProviderName() const {
243 return std::string("InstallVerifier");
248 enum MustRemainDisabledOutcome
{
252 ENTERPRISE_POLICY_ALLOWED
,
256 NOT_VERIFIED_BUT_NOT_ENFORCING
,
259 // This is used in histograms - do not remove or reorder entries above! Also
260 // the "MAX" item below should always be the last element.
262 MUST_REMAIN_DISABLED_OUTCOME_MAX
265 void MustRemainDisabledHistogram(MustRemainDisabledOutcome outcome
) {
266 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.MustRemainDisabled",
267 outcome
, MUST_REMAIN_DISABLED_OUTCOME_MAX
);
272 bool InstallVerifier::MustRemainDisabled(const Extension
* extension
,
273 Extension::DisableReason
* reason
,
274 base::string16
* error
) const {
276 if (!CanUseExtensionApis(*extension
)) {
277 MustRemainDisabledHistogram(NOT_EXTENSION
);
280 if (Manifest::IsUnpackedLocation(extension
->location())) {
281 MustRemainDisabledHistogram(UNPACKED
);
284 if (AllowedByEnterprisePolicy(extension
->id())) {
285 MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED
);
289 bool verified
= true;
290 MustRemainDisabledOutcome outcome
= VERIFIED
;
291 if (ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension
->id())) {
293 outcome
= FORCED_NOT_VERIFIED
;
294 } else if (!FromStore(*extension
)) {
296 outcome
= NOT_FROM_STORE
;
297 } else if (signature_
.get() == NULL
) {
298 // If we don't have a signature yet, we'll temporarily consider every
299 // extension from the webstore verified to avoid false positives on existing
300 // profiles hitting this code for the first time, and rely on consumers of
301 // this class to check NeedsBootstrap() and schedule a first check so we can
303 outcome
= NO_SIGNATURE
;
304 } else if (!IsVerified(extension
->id())) {
306 outcome
= NOT_VERIFIED
;
308 if (!verified
&& !ShouldEnforce()) {
310 outcome
= NOT_VERIFIED_BUT_NOT_ENFORCING
;
312 MustRemainDisabledHistogram(outcome
);
316 *reason
= Extension::DISABLE_NOT_VERIFIED
;
318 *error
= l10n_util::GetStringFUTF16(
319 IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE
,
320 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE
));
325 InstallVerifier::PendingOperation::PendingOperation() {
326 type
= InstallVerifier::ADD
;
329 InstallVerifier::PendingOperation::~PendingOperation() {
332 void InstallVerifier::GarbageCollect() {
333 if (!ShouldFetchSignature()) {
336 CHECK(signature_
.get());
337 ExtensionIdSet leftovers
= signature_
->ids
;
338 ExtensionIdList all_ids
;
339 prefs_
->GetExtensions(&all_ids
);
340 for (ExtensionIdList::const_iterator i
= all_ids
.begin();
341 i
!= all_ids
.end(); ++i
) {
342 ExtensionIdSet::iterator found
= leftovers
.find(*i
);
343 if (found
!= leftovers
.end())
344 leftovers
.erase(found
);
346 if (!leftovers
.empty()) {
347 RemoveMany(leftovers
);
351 bool InstallVerifier::AllowedByEnterprisePolicy(const std::string
& id
) const {
352 PrefService
* pref_service
= prefs_
->pref_service();
353 if (pref_service
->IsManagedPreference(pref_names::kInstallAllowList
)) {
354 const base::ListValue
* whitelist
=
355 pref_service
->GetList(pref_names::kInstallAllowList
);
356 base::StringValue
id_value(id
);
357 if (whitelist
&& whitelist
->Find(id_value
) != whitelist
->end())
360 if (pref_service
->IsManagedPreference(pref_names::kInstallForceList
)) {
361 const base::DictionaryValue
* forcelist
=
362 pref_service
->GetDictionary(pref_names::kInstallForceList
);
363 if (forcelist
&& forcelist
->HasKey(id
))
369 bool InstallVerifier::IsVerified(const std::string
& id
) const {
370 return ((signature_
.get() && ContainsKey(signature_
->ids
, id
)) ||
371 ContainsKey(provisional_
, id
));
374 void InstallVerifier::BeginFetch() {
375 DCHECK(ShouldFetchSignature());
377 // TODO(asargent) - It would be possible to coalesce all operations in the
378 // queue into one fetch - we'd probably just need to change the queue to
379 // hold (set of ids, list of callbacks) pairs.
380 CHECK(!operation_queue_
.empty());
381 const PendingOperation
& operation
= *operation_queue_
.front();
383 ExtensionIdSet ids_to_sign
;
384 if (signature_
.get()) {
385 ids_to_sign
.insert(signature_
->ids
.begin(), signature_
->ids
.end());
387 if (operation
.type
== InstallVerifier::ADD
) {
388 ids_to_sign
.insert(operation
.ids
.begin(), operation
.ids
.end());
390 for (ExtensionIdSet::const_iterator i
= operation
.ids
.begin();
391 i
!= operation
.ids
.end(); ++i
) {
392 if (ContainsKey(ids_to_sign
, *i
))
393 ids_to_sign
.erase(*i
);
397 signer_
.reset(new InstallSigner(context_getter_
, ids_to_sign
));
398 signer_
->GetSignature(base::Bind(&InstallVerifier::SignatureCallback
,
399 base::Unretained(this)));
402 void InstallVerifier::SaveToPrefs() {
403 if (signature_
.get())
404 DCHECK(InstallSigner::VerifySignature(*signature_
));
406 if (!signature_
.get() || signature_
->ids
.empty()) {
407 DVLOG(1) << "SaveToPrefs - saving NULL";
408 prefs_
->SetInstallSignature(NULL
);
410 base::DictionaryValue pref
;
411 signature_
->ToValue(&pref
);
413 DVLOG(1) << "SaveToPrefs - saving";
415 DCHECK(InstallSigner::VerifySignature(*signature_
.get()));
416 scoped_ptr
<InstallSignature
> rehydrated
=
417 InstallSignature::FromValue(pref
);
418 DCHECK(InstallSigner::VerifySignature(*rehydrated
.get()));
420 prefs_
->SetInstallSignature(&pref
);
426 enum CallbackResult
{
427 CALLBACK_NO_SIGNATURE
= 0,
428 CALLBACK_INVALID_SIGNATURE
,
429 CALLBACK_VALID_SIGNATURE
,
431 // This is used in histograms - do not remove or reorder entries above! Also
432 // the "MAX" item below should always be the last element.
437 void GetSignatureResultHistogram(CallbackResult result
) {
438 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult",
439 result
, CALLBACK_RESULT_MAX
);
444 void InstallVerifier::SignatureCallback(
445 scoped_ptr
<InstallSignature
> signature
) {
447 linked_ptr
<PendingOperation
> operation
= operation_queue_
.front();
448 operation_queue_
.pop();
450 bool success
= false;
451 if (!signature
.get()) {
452 GetSignatureResultHistogram(CALLBACK_NO_SIGNATURE
);
453 } else if (!InstallSigner::VerifySignature(*signature
)) {
454 GetSignatureResultHistogram(CALLBACK_INVALID_SIGNATURE
);
456 GetSignatureResultHistogram(CALLBACK_VALID_SIGNATURE
);
461 if (!operation
->callback
.is_null())
462 operation
->callback
.Run(false);
464 // TODO(asargent) - if this was something like a network error, we need to
465 // do retries with exponential back off.
467 signature_
= signature
.Pass();
470 if (!provisional_
.empty()) {
471 // Update |provisional_| to remove ids that were successfully signed.
472 provisional_
= base::STLSetDifference
<ExtensionIdSet
>(
473 provisional_
, signature_
->ids
);
476 if (!operation
->callback
.is_null())
477 operation
->callback
.Run(success
);
480 if (!operation_queue_
.empty())
485 } // namespace extensions