Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / install_verifier.cc
blob073964779ba1de0e6362a4b8dff893cdeea2b1cd
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/browser/extensions/install_verifier_factory.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/grit/generated_resources.h"
23 #include "content/public/browser/browser_context.h"
24 #include "content/public/common/content_switches.h"
25 #include "extensions/browser/extension_prefs.h"
26 #include "extensions/browser/extension_registry.h"
27 #include "extensions/browser/extension_system.h"
28 #include "extensions/browser/pref_names.h"
29 #include "extensions/common/extension_set.h"
30 #include "extensions/common/manifest.h"
31 #include "extensions/common/manifest_url_handlers.h"
32 #include "extensions/common/one_shot_event.h"
33 #include "ui/base/l10n/l10n_util.h"
35 namespace extensions {
37 namespace {
39 enum VerifyStatus {
40 NONE = 0, // Do not request install signatures, and do not enforce them.
41 BOOTSTRAP, // Request install signatures, but do not enforce them.
42 ENFORCE, // Request install signatures, and enforce them.
43 ENFORCE_STRICT, // Same as ENFORCE, but hard fail if we can't fetch
44 // signatures.
46 // This is used in histograms - do not remove or reorder entries above! Also
47 // the "MAX" item below should always be the last element.
48 VERIFY_STATUS_MAX
51 #if defined(GOOGLE_CHROME_BUILD)
52 const char kExperimentName[] = "ExtensionInstallVerification";
53 #endif // defined(GOOGLE_CHROME_BUILD)
55 VerifyStatus GetExperimentStatus() {
56 #if defined(GOOGLE_CHROME_BUILD)
57 const std::string group = base::FieldTrialList::FindFullName(
58 kExperimentName);
60 std::string forced_trials =
61 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
62 switches::kForceFieldTrials);
63 if (forced_trials.find(kExperimentName) != std::string::npos) {
64 // We don't want to allow turning off enforcement by forcing the field
65 // trial group to something other than enforcement.
66 return ENFORCE_STRICT;
69 VerifyStatus default_status = NONE;
71 if (group == "EnforceStrict")
72 return ENFORCE_STRICT;
73 else if (group == "Enforce")
74 return ENFORCE;
75 else if (group == "Bootstrap")
76 return BOOTSTRAP;
77 else if (group == "None" || group == "Control")
78 return NONE;
79 else
80 return default_status;
81 #endif // defined(GOOGLE_CHROME_BUILD)
83 return NONE;
86 VerifyStatus GetCommandLineStatus() {
87 const base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess();
88 if (!InstallSigner::GetForcedNotFromWebstore().empty())
89 return ENFORCE;
91 if (cmdline->HasSwitch(switches::kExtensionsInstallVerification)) {
92 std::string value = cmdline->GetSwitchValueASCII(
93 switches::kExtensionsInstallVerification);
94 if (value == "bootstrap")
95 return BOOTSTRAP;
96 else if (value == "enforce_strict")
97 return ENFORCE_STRICT;
98 else
99 return ENFORCE;
102 return NONE;
105 VerifyStatus GetStatus() {
106 return std::max(GetExperimentStatus(), GetCommandLineStatus());
109 bool ShouldFetchSignature() {
110 return GetStatus() >= BOOTSTRAP;
113 enum InitResult {
114 INIT_NO_PREF = 0,
115 INIT_UNPARSEABLE_PREF,
116 INIT_INVALID_SIGNATURE,
117 INIT_VALID_SIGNATURE,
119 // This is used in histograms - do not remove or reorder entries above! Also
120 // the "MAX" item below should always be the last element.
122 INIT_RESULT_MAX
125 void LogInitResultHistogram(InitResult result) {
126 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.InitResult",
127 result, INIT_RESULT_MAX);
130 bool CanUseExtensionApis(const Extension& extension) {
131 return extension.is_extension() || extension.is_legacy_packaged_app();
134 enum VerifyAllSuccess {
135 VERIFY_ALL_BOOTSTRAP_SUCCESS = 0,
136 VERIFY_ALL_BOOTSTRAP_FAILURE,
137 VERIFY_ALL_NON_BOOTSTRAP_SUCCESS,
138 VERIFY_ALL_NON_BOOTSTRAP_FAILURE,
140 // Used in histograms. Do not remove/reorder any entries above, and the below
141 // MAX entry should always come last.
142 VERIFY_ALL_SUCCESS_MAX
145 // Record the success or failure of verifying all extensions, and whether or
146 // not it was a bootstrapping.
147 void LogVerifyAllSuccessHistogram(bool bootstrap, bool success) {
148 VerifyAllSuccess result;
149 if (bootstrap && success)
150 result = VERIFY_ALL_BOOTSTRAP_SUCCESS;
151 else if (bootstrap && !success)
152 result = VERIFY_ALL_BOOTSTRAP_FAILURE;
153 else if (!bootstrap && success)
154 result = VERIFY_ALL_NON_BOOTSTRAP_SUCCESS;
155 else
156 result = VERIFY_ALL_NON_BOOTSTRAP_FAILURE;
158 // This used to be part of ExtensionService, but moved here. In order to keep
159 // our histograms accurate, the name is unchanged.
160 UMA_HISTOGRAM_ENUMERATION(
161 "ExtensionService.VerifyAllSuccess", result, VERIFY_ALL_SUCCESS_MAX);
164 // Record the success or failure of a single verification.
165 void LogAddVerifiedSuccess(bool success) {
166 // This used to be part of ExtensionService, but moved here. In order to keep
167 // our histograms accurate, the name is unchanged.
168 UMA_HISTOGRAM_BOOLEAN("ExtensionService.AddVerified", success);
171 } // namespace
173 InstallVerifier::InstallVerifier(ExtensionPrefs* prefs,
174 content::BrowserContext* context)
175 : prefs_(prefs),
176 context_(context),
177 bootstrap_check_complete_(false),
178 weak_factory_(this) {
181 InstallVerifier::~InstallVerifier() {}
183 // static
184 InstallVerifier* InstallVerifier::Get(
185 content::BrowserContext* browser_context) {
186 return InstallVerifierFactory::GetForBrowserContext(browser_context);
189 // static
190 bool InstallVerifier::ShouldEnforce() {
191 return GetStatus() >= ENFORCE;
194 // static
195 bool InstallVerifier::NeedsVerification(const Extension& extension) {
196 return IsFromStore(extension) && CanUseExtensionApis(extension);
201 // static
202 bool InstallVerifier::IsFromStore(const Extension& extension) {
203 if (extension.from_webstore() || ManifestURL::UpdatesFromGallery(&extension))
204 return true;
206 // If an extension has no update url, our autoupdate code will ask the
207 // webstore about it (to aid in migrating to the webstore from self-hosting
208 // or sideloading based installs). So we want to do verification checks on
209 // such extensions too so that we don't accidentally disable old installs of
210 // extensions that did migrate to the webstore.
211 return (ManifestURL::GetUpdateURL(&extension).is_empty() &&
212 Manifest::IsAutoUpdateableLocation(extension.location()));
215 void InstallVerifier::Init() {
216 TRACE_EVENT0("browser,startup", "extensions::InstallVerifier::Init");
217 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ExperimentStatus",
218 GetExperimentStatus(), VERIFY_STATUS_MAX);
219 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ActualStatus",
220 GetStatus(), VERIFY_STATUS_MAX);
222 const base::DictionaryValue* pref = prefs_->GetInstallSignature();
223 if (pref) {
224 scoped_ptr<InstallSignature> signature_from_prefs =
225 InstallSignature::FromValue(*pref);
226 if (!signature_from_prefs.get()) {
227 LogInitResultHistogram(INIT_UNPARSEABLE_PREF);
228 } else if (!InstallSigner::VerifySignature(*signature_from_prefs.get())) {
229 LogInitResultHistogram(INIT_INVALID_SIGNATURE);
230 DVLOG(1) << "Init - ignoring invalid signature";
231 } else {
232 signature_ = signature_from_prefs.Pass();
233 LogInitResultHistogram(INIT_VALID_SIGNATURE);
234 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallVerifier.InitSignatureCount",
235 signature_->ids.size());
236 GarbageCollect();
238 } else {
239 LogInitResultHistogram(INIT_NO_PREF);
242 ExtensionSystem::Get(context_)->ready().Post(
243 FROM_HERE,
244 base::Bind(&InstallVerifier::MaybeBootstrapSelf,
245 weak_factory_.GetWeakPtr()));
248 void InstallVerifier::VerifyAllExtensions() {
249 AddMany(GetExtensionsToVerify(), ADD_ALL);
252 base::Time InstallVerifier::SignatureTimestamp() {
253 if (signature_.get())
254 return signature_->timestamp;
255 else
256 return base::Time();
259 bool InstallVerifier::IsKnownId(const std::string& id) const {
260 return signature_.get() && (ContainsKey(signature_->ids, id) ||
261 ContainsKey(signature_->invalid_ids, id));
264 bool InstallVerifier::IsInvalid(const std::string& id) const {
265 return ((signature_.get() && ContainsKey(signature_->invalid_ids, id)));
268 void InstallVerifier::VerifyExtension(const std::string& extension_id) {
269 ExtensionIdSet ids;
270 ids.insert(extension_id);
271 AddMany(ids, ADD_SINGLE);
274 void InstallVerifier::AddMany(const ExtensionIdSet& ids, OperationType type) {
275 if (!ShouldFetchSignature()) {
276 OnVerificationComplete(true, type); // considered successful.
277 return;
280 if (signature_.get()) {
281 ExtensionIdSet not_allowed_yet =
282 base::STLSetDifference<ExtensionIdSet>(ids, signature_->ids);
283 if (not_allowed_yet.empty()) {
284 OnVerificationComplete(true, type); // considered successful.
285 return;
289 InstallVerifier::PendingOperation* operation =
290 new InstallVerifier::PendingOperation(type);
291 operation->ids.insert(ids.begin(), ids.end());
293 operation_queue_.push(linked_ptr<PendingOperation>(operation));
295 // If there are no ongoing pending requests, we need to kick one off.
296 if (operation_queue_.size() == 1)
297 BeginFetch();
300 void InstallVerifier::AddProvisional(const ExtensionIdSet& ids) {
301 provisional_.insert(ids.begin(), ids.end());
302 AddMany(ids, ADD_PROVISIONAL);
305 void InstallVerifier::Remove(const std::string& id) {
306 ExtensionIdSet ids;
307 ids.insert(id);
308 RemoveMany(ids);
311 void InstallVerifier::RemoveMany(const ExtensionIdSet& ids) {
312 if (!signature_.get() || !ShouldFetchSignature())
313 return;
315 bool found_any = false;
316 for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); ++i) {
317 if (ContainsKey(signature_->ids, *i) ||
318 ContainsKey(signature_->invalid_ids, *i)) {
319 found_any = true;
320 break;
323 if (!found_any)
324 return;
326 InstallVerifier::PendingOperation* operation =
327 new InstallVerifier::PendingOperation(InstallVerifier::REMOVE);
328 operation->ids = ids;
330 operation_queue_.push(linked_ptr<PendingOperation>(operation));
331 if (operation_queue_.size() == 1)
332 BeginFetch();
335 bool InstallVerifier::AllowedByEnterprisePolicy(const std::string& id) const {
336 return ExtensionManagementFactory::GetForBrowserContext(context_)
337 ->IsInstallationExplicitlyAllowed(id);
340 std::string InstallVerifier::GetDebugPolicyProviderName() const {
341 return std::string("InstallVerifier");
344 namespace {
346 enum MustRemainDisabledOutcome {
347 VERIFIED = 0,
348 NOT_EXTENSION,
349 UNPACKED,
350 ENTERPRISE_POLICY_ALLOWED,
351 FORCED_NOT_VERIFIED,
352 NOT_FROM_STORE,
353 NO_SIGNATURE,
354 NOT_VERIFIED_BUT_NOT_ENFORCING,
355 NOT_VERIFIED,
356 NOT_VERIFIED_BUT_INSTALL_TIME_NEWER_THAN_SIGNATURE,
357 NOT_VERIFIED_BUT_UNKNOWN_ID,
358 COMPONENT,
360 // This is used in histograms - do not remove or reorder entries above! Also
361 // the "MAX" item below should always be the last element.
362 MUST_REMAIN_DISABLED_OUTCOME_MAX
365 void MustRemainDisabledHistogram(MustRemainDisabledOutcome outcome) {
366 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.MustRemainDisabled",
367 outcome, MUST_REMAIN_DISABLED_OUTCOME_MAX);
370 } // namespace
372 bool InstallVerifier::MustRemainDisabled(const Extension* extension,
373 Extension::DisableReason* reason,
374 base::string16* error) const {
375 CHECK(extension);
376 if (!CanUseExtensionApis(*extension)) {
377 MustRemainDisabledHistogram(NOT_EXTENSION);
378 return false;
380 if (Manifest::IsUnpackedLocation(extension->location())) {
381 MustRemainDisabledHistogram(UNPACKED);
382 return false;
384 if (extension->location() == Manifest::COMPONENT) {
385 MustRemainDisabledHistogram(COMPONENT);
386 return false;
388 if (AllowedByEnterprisePolicy(extension->id())) {
389 MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED);
390 return false;
393 bool verified = true;
394 MustRemainDisabledOutcome outcome = VERIFIED;
395 if (ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension->id())) {
396 verified = false;
397 outcome = FORCED_NOT_VERIFIED;
398 } else if (!IsFromStore(*extension)) {
399 verified = false;
400 outcome = NOT_FROM_STORE;
401 } else if (signature_.get() == NULL &&
402 (!bootstrap_check_complete_ || GetStatus() < ENFORCE_STRICT)) {
403 // If we don't have a signature yet, we'll temporarily consider every
404 // extension from the webstore verified to avoid false positives on existing
405 // profiles hitting this code for the first time. The InstallVerifier
406 // will bootstrap itself once the ExtensionsSystem is ready.
407 outcome = NO_SIGNATURE;
408 } else if (!IsVerified(extension->id())) {
409 if (signature_.get() &&
410 !ContainsKey(signature_->invalid_ids, extension->id())) {
411 outcome = NOT_VERIFIED_BUT_UNKNOWN_ID;
412 } else {
413 verified = false;
414 outcome = NOT_VERIFIED;
417 if (!verified && !ShouldEnforce()) {
418 verified = true;
419 outcome = NOT_VERIFIED_BUT_NOT_ENFORCING;
421 MustRemainDisabledHistogram(outcome);
423 if (!verified) {
424 if (reason)
425 *reason = Extension::DISABLE_NOT_VERIFIED;
426 if (error)
427 *error = l10n_util::GetStringFUTF16(
428 IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
429 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE));
431 return !verified;
434 InstallVerifier::PendingOperation::PendingOperation(OperationType type)
435 : type(type) {}
437 InstallVerifier::PendingOperation::~PendingOperation() {
440 ExtensionIdSet InstallVerifier::GetExtensionsToVerify() const {
441 ExtensionIdSet result;
442 scoped_ptr<ExtensionSet> extensions =
443 ExtensionRegistry::Get(context_)->GenerateInstalledExtensionsSet();
444 for (ExtensionSet::const_iterator iter = extensions->begin();
445 iter != extensions->end();
446 ++iter) {
447 if (NeedsVerification(*iter->get()))
448 result.insert((*iter)->id());
450 return result;
453 void InstallVerifier::MaybeBootstrapSelf() {
454 bool needs_bootstrap = false;
456 ExtensionIdSet extension_ids = GetExtensionsToVerify();
457 if (signature_.get() == NULL && ShouldFetchSignature()) {
458 needs_bootstrap = true;
459 } else {
460 for (ExtensionIdSet::const_iterator iter = extension_ids.begin();
461 iter != extension_ids.end();
462 ++iter) {
463 if (!IsKnownId(*iter)) {
464 needs_bootstrap = true;
465 break;
470 if (needs_bootstrap)
471 AddMany(extension_ids, ADD_ALL_BOOTSTRAP);
472 else
473 bootstrap_check_complete_ = true;
476 void InstallVerifier::OnVerificationComplete(bool success, OperationType type) {
477 switch (type) {
478 case ADD_SINGLE:
479 LogAddVerifiedSuccess(success);
480 break;
481 case ADD_ALL:
482 case ADD_ALL_BOOTSTRAP:
483 LogVerifyAllSuccessHistogram(type == ADD_ALL_BOOTSTRAP, success);
484 bootstrap_check_complete_ = true;
485 if (success) {
486 // Iterate through the extensions and, if any are newly-verified and
487 // should have the DISABLE_NOT_VERIFIED reason lifted, do so.
488 const ExtensionSet& disabled_extensions =
489 ExtensionRegistry::Get(context_)->disabled_extensions();
490 for (ExtensionSet::const_iterator iter = disabled_extensions.begin();
491 iter != disabled_extensions.end();
492 ++iter) {
493 int disable_reasons = prefs_->GetDisableReasons((*iter)->id());
494 if (disable_reasons & Extension::DISABLE_NOT_VERIFIED &&
495 !MustRemainDisabled(iter->get(), NULL, NULL)) {
496 prefs_->RemoveDisableReason((*iter)->id(),
497 Extension::DISABLE_NOT_VERIFIED);
501 if (success || GetStatus() == ENFORCE_STRICT) {
502 ExtensionSystem::Get(context_)
503 ->extension_service()
504 ->CheckManagementPolicy();
506 break;
507 // We don't need to check disable reasons or report UMA stats for
508 // provisional adds or removals.
509 case ADD_PROVISIONAL:
510 case REMOVE:
511 break;
515 void InstallVerifier::GarbageCollect() {
516 if (!ShouldFetchSignature()) {
517 return;
519 CHECK(signature_.get());
520 ExtensionIdSet leftovers = signature_->ids;
521 leftovers.insert(signature_->invalid_ids.begin(),
522 signature_->invalid_ids.end());
523 ExtensionIdList all_ids;
524 prefs_->GetExtensions(&all_ids);
525 for (ExtensionIdList::const_iterator i = all_ids.begin();
526 i != all_ids.end(); ++i) {
527 ExtensionIdSet::iterator found = leftovers.find(*i);
528 if (found != leftovers.end())
529 leftovers.erase(found);
531 if (!leftovers.empty()) {
532 RemoveMany(leftovers);
536 bool InstallVerifier::IsVerified(const std::string& id) const {
537 return ((signature_.get() && ContainsKey(signature_->ids, id)) ||
538 ContainsKey(provisional_, id));
541 void InstallVerifier::BeginFetch() {
542 DCHECK(ShouldFetchSignature());
544 // TODO(asargent) - It would be possible to coalesce all operations in the
545 // queue into one fetch - we'd probably just need to change the queue to
546 // hold (set of ids, list of operation type) pairs.
547 CHECK(!operation_queue_.empty());
548 const PendingOperation& operation = *operation_queue_.front();
550 ExtensionIdSet ids_to_sign;
551 if (signature_.get()) {
552 ids_to_sign.insert(signature_->ids.begin(), signature_->ids.end());
554 if (operation.type == InstallVerifier::REMOVE) {
555 for (ExtensionIdSet::const_iterator i = operation.ids.begin();
556 i != operation.ids.end(); ++i) {
557 if (ContainsKey(ids_to_sign, *i))
558 ids_to_sign.erase(*i);
560 } else { // All other operation types are some form of "ADD".
561 ids_to_sign.insert(operation.ids.begin(), operation.ids.end());
564 signer_.reset(new InstallSigner(context_->GetRequestContext(), ids_to_sign));
565 signer_->GetSignature(base::Bind(&InstallVerifier::SignatureCallback,
566 weak_factory_.GetWeakPtr()));
569 void InstallVerifier::SaveToPrefs() {
570 if (signature_.get())
571 DCHECK(InstallSigner::VerifySignature(*signature_));
573 if (!signature_.get() || signature_->ids.empty()) {
574 DVLOG(1) << "SaveToPrefs - saving NULL";
575 prefs_->SetInstallSignature(NULL);
576 } else {
577 base::DictionaryValue pref;
578 signature_->ToValue(&pref);
579 if (VLOG_IS_ON(1)) {
580 DVLOG(1) << "SaveToPrefs - saving";
582 DCHECK(InstallSigner::VerifySignature(*signature_.get()));
583 scoped_ptr<InstallSignature> rehydrated =
584 InstallSignature::FromValue(pref);
585 DCHECK(InstallSigner::VerifySignature(*rehydrated.get()));
587 prefs_->SetInstallSignature(&pref);
591 namespace {
593 enum CallbackResult {
594 CALLBACK_NO_SIGNATURE = 0,
595 CALLBACK_INVALID_SIGNATURE,
596 CALLBACK_VALID_SIGNATURE,
598 // This is used in histograms - do not remove or reorder entries above! Also
599 // the "MAX" item below should always be the last element.
601 CALLBACK_RESULT_MAX
604 void GetSignatureResultHistogram(CallbackResult result) {
605 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult",
606 result, CALLBACK_RESULT_MAX);
609 } // namespace
611 void InstallVerifier::SignatureCallback(
612 scoped_ptr<InstallSignature> signature) {
614 linked_ptr<PendingOperation> operation = operation_queue_.front();
615 operation_queue_.pop();
617 bool success = false;
618 if (!signature.get()) {
619 GetSignatureResultHistogram(CALLBACK_NO_SIGNATURE);
620 } else if (!InstallSigner::VerifySignature(*signature)) {
621 GetSignatureResultHistogram(CALLBACK_INVALID_SIGNATURE);
622 } else {
623 GetSignatureResultHistogram(CALLBACK_VALID_SIGNATURE);
624 success = true;
627 if (!success) {
628 OnVerificationComplete(false, operation->type);
630 // TODO(asargent) - if this was something like a network error, we need to
631 // do retries with exponential back off.
632 } else {
633 signature_ = signature.Pass();
634 SaveToPrefs();
636 if (!provisional_.empty()) {
637 // Update |provisional_| to remove ids that were successfully signed.
638 provisional_ = base::STLSetDifference<ExtensionIdSet>(
639 provisional_, signature_->ids);
642 OnVerificationComplete(success, operation->type);
645 if (!operation_queue_.empty())
646 BeginFetch();
649 } // namespace extensions