install_verifier.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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.
4
5#include "chrome/browser/extensions/install_verifier.h"
6
7#include <algorithm>
8#include <string>
9
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"
26
27namespace {
28
29enum 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.
33
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.
36
37  VERIFY_STATUS_MAX
38};
39
40#if defined(GOOGLE_CHROME_BUILD)
41const char kExperimentName[] = "ExtensionInstallVerification";
42#endif  // defined(GOOGLE_CHROME_BUILD)
43
44VerifyStatus GetExperimentStatus() {
45#if defined(GOOGLE_CHROME_BUILD)
46  const std::string group = base::FieldTrialList::FindFullName(
47      kExperimentName);
48
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;
55  }
56
57  VerifyStatus default_status = NONE;
58
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)
68
69  return NONE;
70}
71
72VerifyStatus GetCommandLineStatus() {
73  const CommandLine* cmdline = CommandLine::ForCurrentProcess();
74  if (!extensions::InstallSigner::GetForcedNotFromWebstore().empty())
75    return ENFORCE;
76
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;
84  }
85
86  return NONE;
87}
88
89VerifyStatus GetStatus() {
90  return std::max(GetExperimentStatus(), GetCommandLineStatus());
91}
92
93bool ShouldFetchSignature() {
94  VerifyStatus status = GetStatus();
95  return (status == BOOTSTRAP || status == ENFORCE);
96}
97
98bool ShouldEnforce() {
99  return GetStatus() == ENFORCE;
100}
101
102}  // namespace
103
104namespace extensions {
105
106InstallVerifier::InstallVerifier(ExtensionPrefs* prefs,
107                                 net::URLRequestContextGetter* context_getter)
108    : prefs_(prefs), context_getter_(context_getter) {
109}
110
111InstallVerifier::~InstallVerifier() {}
112
113namespace {
114
115enum InitResult {
116  INIT_NO_PREF = 0,
117  INIT_UNPARSEABLE_PREF,
118  INIT_INVALID_SIGNATURE,
119  INIT_VALID_SIGNATURE,
120
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.
123
124  INIT_RESULT_MAX
125};
126
127void LogInitResultHistogram(InitResult result) {
128  UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.InitResult",
129                            result, INIT_RESULT_MAX);
130}
131
132bool FromStore(const Extension& extension) {
133  bool updates_from_store = ManifestURL::UpdatesFromGallery(&extension);
134  return extension.from_webstore() || updates_from_store;
135}
136
137bool CanUseExtensionApis(const Extension& extension) {
138  return extension.is_extension() || extension.is_legacy_packaged_app();
139}
140
141}  // namespace
142
143// static
144bool InstallVerifier::NeedsVerification(const Extension& extension) {
145  return FromStore(extension) && CanUseExtensionApis(extension);
146}
147
148void InstallVerifier::Init() {
149  UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ExperimentStatus",
150                            GetExperimentStatus(), VERIFY_STATUS_MAX);
151  UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ActualStatus",
152                            GetStatus(), VERIFY_STATUS_MAX);
153
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();
169    }
170  } else {
171    LogInitResultHistogram(INIT_NO_PREF);
172  }
173}
174
175bool InstallVerifier::NeedsBootstrap() {
176  return signature_.get() == NULL && ShouldFetchSignature();
177}
178
179base::Time InstallVerifier::SignatureTimestamp() {
180  if (signature_.get())
181    return signature_->timestamp;
182  else
183    return base::Time();
184}
185
186void InstallVerifier::Add(const std::string& id,
187                          const AddResultCallback& callback) {
188  ExtensionIdSet ids;
189  ids.insert(id);
190  AddMany(ids, callback);
191}
192
193void InstallVerifier::AddMany(const ExtensionIdSet& ids,
194                              const AddResultCallback& callback) {
195  if (!ShouldFetchSignature()) {
196    if (!callback.is_null())
197      callback.Run(true);
198    return;
199  }
200
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;
208    }
209  }
210
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;
216
217  operation_queue_.push(linked_ptr<PendingOperation>(operation));
218
219  // If there are no ongoing pending requests, we need to kick one off.
220  if (operation_queue_.size() == 1)
221    BeginFetch();
222}
223
224void InstallVerifier::AddProvisional(const ExtensionIdSet& ids) {
225  provisional_.insert(ids.begin(), ids.end());
226  AddMany(ids, AddResultCallback());
227}
228
229void InstallVerifier::Remove(const std::string& id) {
230  ExtensionIdSet ids;
231  ids.insert(id);
232  RemoveMany(ids);
233}
234
235void InstallVerifier::RemoveMany(const ExtensionIdSet& ids) {
236  if (!signature_.get() || !ShouldFetchSignature())
237    return;
238
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;
244    }
245  }
246  if (!found_any)
247    return;
248
249  InstallVerifier::PendingOperation* operation =
250    new InstallVerifier::PendingOperation();
251  operation->type = InstallVerifier::REMOVE;
252  operation->ids = ids;
253
254  operation_queue_.push(linked_ptr<PendingOperation>(operation));
255  if (operation_queue_.size() == 1)
256    BeginFetch();
257}
258
259std::string InstallVerifier::GetDebugPolicyProviderName() const {
260  return std::string("InstallVerifier");
261}
262
263namespace {
264
265enum 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,
276
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
280};
281
282void MustRemainDisabledHistogram(MustRemainDisabledOutcome outcome) {
283  UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.MustRemainDisabled",
284                            outcome, MUST_REMAIN_DISABLED_OUTCOME_MAX);
285}
286
287}  // namespace
288
289bool 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;
296  }
297  if (Manifest::IsUnpackedLocation(extension->location())) {
298    MustRemainDisabledHistogram(UNPACKED);
299    return false;
300  }
301  if (AllowedByEnterprisePolicy(extension->id())) {
302    MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED);
303    return false;
304  }
305
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;
327    }
328  }
329  if (!verified && !ShouldEnforce()) {
330    verified = true;
331    outcome = NOT_VERIFIED_BUT_NOT_ENFORCING;
332  }
333  MustRemainDisabledHistogram(outcome);
334
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));
342  }
343  return !verified;
344}
345
346InstallVerifier::PendingOperation::PendingOperation() {
347  type = InstallVerifier::ADD;
348}
349
350InstallVerifier::PendingOperation::~PendingOperation() {
351}
352
353void InstallVerifier::GarbageCollect() {
354  if (!ShouldFetchSignature()) {
355    return;
356  }
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);
366  }
367  if (!leftovers.empty()) {
368    RemoveMany(leftovers);
369  }
370}
371
372bool 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;
380  }
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;
386  }
387  return false;
388}
389
390bool InstallVerifier::IsVerified(const std::string& id) const {
391  return ((signature_.get() && ContainsKey(signature_->ids, id)) ||
392          ContainsKey(provisional_, id));
393}
394
395bool InstallVerifier::WasInstalledAfterSignature(const std::string& id) const {
396  if (!signature_.get() || signature_->timestamp.is_null())
397    return true;
398
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;
406}
407
408void InstallVerifier::BeginFetch() {
409  DCHECK(ShouldFetchSignature());
410
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();
416
417  ExtensionIdSet ids_to_sign;
418  if (signature_.get()) {
419    ids_to_sign.insert(signature_->ids.begin(), signature_->ids.end());
420  }
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);
428    }
429  }
430
431  signer_.reset(new InstallSigner(context_getter_, ids_to_sign));
432  signer_->GetSignature(base::Bind(&InstallVerifier::SignatureCallback,
433                                   base::Unretained(this)));
434}
435
436void InstallVerifier::SaveToPrefs() {
437  if (signature_.get())
438    DCHECK(InstallSigner::VerifySignature(*signature_));
439
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";
448
449      DCHECK(InstallSigner::VerifySignature(*signature_.get()));
450      scoped_ptr<InstallSignature> rehydrated =
451          InstallSignature::FromValue(pref);
452      DCHECK(InstallSigner::VerifySignature(*rehydrated.get()));
453    }
454    prefs_->SetInstallSignature(&pref);
455  }
456}
457
458namespace {
459
460enum CallbackResult {
461  CALLBACK_NO_SIGNATURE = 0,
462  CALLBACK_INVALID_SIGNATURE,
463  CALLBACK_VALID_SIGNATURE,
464
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.
467
468  CALLBACK_RESULT_MAX
469};
470
471void GetSignatureResultHistogram(CallbackResult result) {
472  UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult",
473                            result, CALLBACK_RESULT_MAX);
474}
475
476}  // namespace
477
478void InstallVerifier::SignatureCallback(
479    scoped_ptr<InstallSignature> signature) {
480
481  linked_ptr<PendingOperation> operation = operation_queue_.front();
482  operation_queue_.pop();
483
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;
492  }
493
494  if (!success) {
495    if (!operation->callback.is_null())
496      operation->callback.Run(false);
497
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();
503
504    if (!provisional_.empty()) {
505      // Update |provisional_| to remove ids that were successfully signed.
506      provisional_ = base::STLSetDifference<ExtensionIdSet>(
507          provisional_, signature_->ids);
508    }
509
510    if (!operation->callback.is_null())
511      operation->callback.Run(success);
512  }
513
514  if (!operation_queue_.empty())
515    BeginFetch();
516}
517
518
519}  // namespace extensions
520