NaCl: Update revision in DEPS, r12770 -> r12773
[chromium-blink-merge.git] / chrome / browser / extensions / install_verifier.cc
blob1f6e824d3987771e9f3bc36468aa1df750ec6373
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 "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"
27 namespace {
29 enum VerifyStatus {
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.
37 VERIFY_STATUS_MAX
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(
47 kExperimentName);
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.
54 return ENFORCE;
57 VerifyStatus default_status = NONE;
59 if (group == "Enforce")
60 return ENFORCE;
61 else if (group == "Bootstrap")
62 return BOOTSTRAP;
63 else if (group == "None" || group == "Control")
64 return NONE;
65 else
66 return default_status;
67 #endif // defined(GOOGLE_CHROME_BUILD)
69 return NONE;
72 VerifyStatus GetCommandLineStatus() {
73 const CommandLine* cmdline = CommandLine::ForCurrentProcess();
74 if (!extensions::InstallSigner::GetForcedNotFromWebstore().empty())
75 return ENFORCE;
77 if (cmdline->HasSwitch(switches::kExtensionsInstallVerification)) {
78 std::string value = cmdline->GetSwitchValueASCII(
79 switches::kExtensionsInstallVerification);
80 if (value == "bootstrap")
81 return BOOTSTRAP;
82 else
83 return ENFORCE;
86 return NONE;
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;
102 } // namespace
104 namespace extensions {
106 InstallVerifier::InstallVerifier(ExtensionPrefs* prefs,
107 net::URLRequestContextGetter* context_getter)
108 : prefs_(prefs), context_getter_(context_getter) {
111 InstallVerifier::~InstallVerifier() {}
113 namespace {
115 enum InitResult {
116 INIT_NO_PREF = 0,
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.
124 INIT_RESULT_MAX
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();
141 } // namespace
143 // static
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();
155 if (pref) {
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";
163 } else {
164 signature_ = signature_from_prefs.Pass();
165 LogInitResultHistogram(INIT_VALID_SIGNATURE);
166 UMA_HISTOGRAM_COUNTS_100("ExtensionInstallVerifier.InitSignatureCount",
167 signature_->ids.size());
168 GarbageCollect();
170 } else {
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;
182 else
183 return base::Time();
186 void InstallVerifier::Add(const std::string& id,
187 const AddResultCallback& callback) {
188 ExtensionIdSet ids;
189 ids.insert(id);
190 AddMany(ids, callback);
193 void InstallVerifier::AddMany(const ExtensionIdSet& ids,
194 const AddResultCallback& callback) {
195 if (!ShouldFetchSignature()) {
196 if (!callback.is_null())
197 callback.Run(true);
198 return;
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())
206 callback.Run(true);
207 return;
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)
221 BeginFetch();
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) {
230 ExtensionIdSet ids;
231 ids.insert(id);
232 RemoveMany(ids);
235 void InstallVerifier::RemoveMany(const ExtensionIdSet& ids) {
236 if (!signature_.get() || !ShouldFetchSignature())
237 return;
239 bool found_any = false;
240 for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); ++i) {
241 if (ContainsKey(signature_->ids, *i)) {
242 found_any = true;
243 break;
246 if (!found_any)
247 return;
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)
256 BeginFetch();
259 std::string InstallVerifier::GetDebugPolicyProviderName() const {
260 return std::string("InstallVerifier");
263 namespace {
265 enum MustRemainDisabledOutcome {
266 VERIFIED = 0,
267 NOT_EXTENSION,
268 UNPACKED,
269 ENTERPRISE_POLICY_ALLOWED,
270 FORCED_NOT_VERIFIED,
271 NOT_FROM_STORE,
272 NO_SIGNATURE,
273 NOT_VERIFIED_BUT_NOT_ENFORCING,
274 NOT_VERIFIED,
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);
287 } // namespace
289 bool InstallVerifier::MustRemainDisabled(const Extension* extension,
290 Extension::DisableReason* reason,
291 base::string16* error) const {
292 CHECK(extension);
293 if (!CanUseExtensionApis(*extension)) {
294 MustRemainDisabledHistogram(NOT_EXTENSION);
295 return false;
297 if (Manifest::IsUnpackedLocation(extension->location())) {
298 MustRemainDisabledHistogram(UNPACKED);
299 return false;
301 if (AllowedByEnterprisePolicy(extension->id())) {
302 MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED);
303 return false;
306 bool verified = true;
307 MustRemainDisabledOutcome outcome = VERIFIED;
308 if (ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension->id())) {
309 verified = false;
310 outcome = FORCED_NOT_VERIFIED;
311 } else if (!FromStore(*extension)) {
312 verified = false;
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
319 // get a signature.
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;
324 } else {
325 verified = false;
326 outcome = NOT_VERIFIED;
329 if (!verified && !ShouldEnforce()) {
330 verified = true;
331 outcome = NOT_VERIFIED_BUT_NOT_ENFORCING;
333 MustRemainDisabledHistogram(outcome);
335 if (!verified) {
336 if (reason)
337 *reason = Extension::DISABLE_NOT_VERIFIED;
338 if (error)
339 *error = l10n_util::GetStringFUTF16(
340 IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
341 l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE));
343 return !verified;
346 InstallVerifier::PendingOperation::PendingOperation() {
347 type = InstallVerifier::ADD;
350 InstallVerifier::PendingOperation::~PendingOperation() {
353 void InstallVerifier::GarbageCollect() {
354 if (!ShouldFetchSignature()) {
355 return;
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())
379 return true;
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))
385 return true;
387 return false;
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())
397 return true;
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())
404 return false;
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());
423 } else {
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);
443 } else {
444 base::DictionaryValue pref;
445 signature_->ToValue(&pref);
446 if (VLOG_IS_ON(1)) {
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);
458 namespace {
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.
468 CALLBACK_RESULT_MAX
471 void GetSignatureResultHistogram(CallbackResult result) {
472 UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult",
473 result, CALLBACK_RESULT_MAX);
476 } // namespace
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);
489 } else {
490 GetSignatureResultHistogram(CALLBACK_VALID_SIGNATURE);
491 success = true;
494 if (!success) {
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.
500 } else {
501 signature_ = signature.Pass();
502 SaveToPrefs();
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())
515 BeginFetch();
519 } // namespace extensions