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