Add ENABLE_MEDIA_ROUTER define to builds other than Android and iOS.
[chromium-blink-merge.git] / chrome / browser / extensions / install_verifier.cc
blobbc1d69009b3a0ddc4460749dc636335c994b0c67
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"
7 #include <algorithm>
8 #include <string>
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 {
36 namespace {
38 enum VerifyStatus {
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
43 // signatures.
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.
47 VERIFY_STATUS_MAX
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(
57 kExperimentName);
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")
73 return ENFORCE;
74 else if (group == "Bootstrap")
75 return BOOTSTRAP;
76 else if (group == "None" || group == "Control")
77 return NONE;
78 else
79 return default_status;
80 #endif // defined(GOOGLE_CHROME_BUILD)
82 return NONE;
85 VerifyStatus GetCommandLineStatus() {
86 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
87 if (!InstallSigner::GetForcedNotFromWebstore().empty())
88 return ENFORCE;
90 if (cmdline->HasSwitch(switches::kExtensionsInstallVerification)) {
91 std::string value = cmdline->GetSwitchValueASCII(
92 switches::kExtensionsInstallVerification);
93 if (value == "bootstrap")
94 return BOOTSTRAP;
95 else if (value == "enforce_strict")
96 return ENFORCE_STRICT;
97 else
98 return ENFORCE;
101 return NONE;
104 VerifyStatus GetStatus() {
105 return std::max(GetExperimentStatus(), GetCommandLineStatus());
108 bool ShouldFetchSignature() {
109 return GetStatus() >= BOOTSTRAP;
112 bool ShouldEnforce() {
113 return GetStatus() >= ENFORCE;
116 enum InitResult {
117 INIT_NO_PREF = 0,
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.
125 INIT_RESULT_MAX
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;
158 else
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);
174 } // namespace
176 InstallVerifier::InstallVerifier(ExtensionPrefs* prefs,
177 content::BrowserContext* context)
178 : prefs_(prefs),
179 context_(context),
180 bootstrap_check_complete_(false),
181 weak_factory_(this) {
184 InstallVerifier::~InstallVerifier() {}
186 // static
187 bool InstallVerifier::NeedsVerification(const Extension& extension) {
188 return IsFromStore(extension) && CanUseExtensionApis(extension);
193 // static
194 bool InstallVerifier::IsFromStore(const Extension& extension) {
195 if (extension.from_webstore() || ManifestURL::UpdatesFromGallery(&extension))
196 return true;
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();
215 if (pref) {
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";
223 } else {
224 signature_ = signature_from_prefs.Pass();
225 LogInitResultHistogram(INIT_VALID_SIGNATURE);
226 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallVerifier.InitSignatureCount",
227 signature_->ids.size());
228 GarbageCollect();
230 } else {
231 LogInitResultHistogram(INIT_NO_PREF);
234 ExtensionSystem::Get(context_)->ready().Post(
235 FROM_HERE,
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;
247 else
248 return base::Time();
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) {
261 ExtensionIdSet ids;
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.
269 return;
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.
277 return;
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)
289 BeginFetch();
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) {
298 ExtensionIdSet ids;
299 ids.insert(id);
300 RemoveMany(ids);
303 void InstallVerifier::RemoveMany(const ExtensionIdSet& ids) {
304 if (!signature_.get() || !ShouldFetchSignature())
305 return;
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)) {
311 found_any = true;
312 break;
315 if (!found_any)
316 return;
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)
324 BeginFetch();
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");
336 namespace {
338 enum MustRemainDisabledOutcome {
339 VERIFIED = 0,
340 NOT_EXTENSION,
341 UNPACKED,
342 ENTERPRISE_POLICY_ALLOWED,
343 FORCED_NOT_VERIFIED,
344 NOT_FROM_STORE,
345 NO_SIGNATURE,
346 NOT_VERIFIED_BUT_NOT_ENFORCING,
347 NOT_VERIFIED,
348 NOT_VERIFIED_BUT_INSTALL_TIME_NEWER_THAN_SIGNATURE,
349 NOT_VERIFIED_BUT_UNKNOWN_ID,
350 COMPONENT,
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);
362 } // namespace
364 bool InstallVerifier::MustRemainDisabled(const Extension* extension,
365 Extension::DisableReason* reason,
366 base::string16* error) const {
367 CHECK(extension);
368 if (!CanUseExtensionApis(*extension)) {
369 MustRemainDisabledHistogram(NOT_EXTENSION);
370 return false;
372 if (Manifest::IsUnpackedLocation(extension->location())) {
373 MustRemainDisabledHistogram(UNPACKED);
374 return false;
376 if (extension->location() == Manifest::COMPONENT) {
377 MustRemainDisabledHistogram(COMPONENT);
378 return false;
380 if (AllowedByEnterprisePolicy(extension->id())) {
381 MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED);
382 return false;
385 bool verified = true;
386 MustRemainDisabledOutcome outcome = VERIFIED;
387 if (ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension->id())) {
388 verified = false;
389 outcome = FORCED_NOT_VERIFIED;
390 } else if (!IsFromStore(*extension)) {
391 verified = false;
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;
404 } else {
405 verified = false;
406 outcome = NOT_VERIFIED;
409 if (!verified && !ShouldEnforce()) {
410 verified = true;
411 outcome = NOT_VERIFIED_BUT_NOT_ENFORCING;
413 MustRemainDisabledHistogram(outcome);
415 if (!verified) {
416 if (reason)
417 *reason = Extension::DISABLE_NOT_VERIFIED;
418 if (error)
419 *error = l10n_util::GetStringFUTF16(
420 IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
421 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE));
423 return !verified;
426 InstallVerifier::PendingOperation::PendingOperation(OperationType type)
427 : type(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();
438 ++iter) {
439 if (NeedsVerification(*iter->get()))
440 result.insert((*iter)->id());
442 return result;
445 void InstallVerifier::MaybeBootstrapSelf() {
446 bool needs_bootstrap = false;
448 ExtensionIdSet extension_ids = GetExtensionsToVerify();
449 if (signature_.get() == NULL && ShouldFetchSignature()) {
450 needs_bootstrap = true;
451 } else {
452 for (ExtensionIdSet::const_iterator iter = extension_ids.begin();
453 iter != extension_ids.end();
454 ++iter) {
455 if (!IsKnownId(*iter)) {
456 needs_bootstrap = true;
457 break;
462 if (needs_bootstrap)
463 AddMany(extension_ids, ADD_ALL_BOOTSTRAP);
464 else
465 bootstrap_check_complete_ = true;
468 void InstallVerifier::OnVerificationComplete(bool success, OperationType type) {
469 switch (type) {
470 case ADD_SINGLE:
471 LogAddVerifiedSuccess(success);
472 break;
473 case ADD_ALL:
474 case ADD_ALL_BOOTSTRAP:
475 LogVerifyAllSuccessHistogram(type == ADD_ALL_BOOTSTRAP, success);
476 bootstrap_check_complete_ = true;
477 if (success) {
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();
484 ++iter) {
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();
498 break;
499 // We don't need to check disable reasons or report UMA stats for
500 // provisional adds or removals.
501 case ADD_PROVISIONAL:
502 case REMOVE:
503 break;
507 void InstallVerifier::GarbageCollect() {
508 if (!ShouldFetchSignature()) {
509 return;
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);
568 } else {
569 base::DictionaryValue pref;
570 signature_->ToValue(&pref);
571 if (VLOG_IS_ON(1)) {
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);
583 namespace {
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.
593 CALLBACK_RESULT_MAX
596 void GetSignatureResultHistogram(CallbackResult result) {
597 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult",
598 result, CALLBACK_RESULT_MAX);
601 } // namespace
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);
614 } else {
615 GetSignatureResultHistogram(CALLBACK_VALID_SIGNATURE);
616 success = true;
619 if (!success) {
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.
624 } else {
625 signature_ = signature.Pass();
626 SaveToPrefs();
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())
638 BeginFetch();
641 } // namespace extensions