install_verifier.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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  if (extension.from_webstore() || ManifestURL::UpdatesFromGallery(&extension))
134    return true;
135
136  // If an extension has no update url, our autoupdate code will ask the
137  // webstore about it (to aid in migrating to the webstore from self-hosting
138  // or sideloading based installs). So we want to do verification checks on
139  // such extensions too so that we don't accidentally disable old installs of
140  // extensions that did migrate to the webstore.
141  return (ManifestURL::GetUpdateURL(&extension).is_empty() &&
142          Manifest::IsAutoUpdateableLocation(extension.location()));
143}
144
145bool CanUseExtensionApis(const Extension& extension) {
146  return extension.is_extension() || extension.is_legacy_packaged_app();
147}
148
149}  // namespace
150
151// static
152bool InstallVerifier::NeedsVerification(const Extension& extension) {
153  return FromStore(extension) && CanUseExtensionApis(extension);
154}
155
156void InstallVerifier::Init() {
157  UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ExperimentStatus",
158                            GetExperimentStatus(), VERIFY_STATUS_MAX);
159  UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ActualStatus",
160                            GetStatus(), VERIFY_STATUS_MAX);
161
162  const base::DictionaryValue* pref = prefs_->GetInstallSignature();
163  if (pref) {
164    scoped_ptr<InstallSignature> signature_from_prefs =
165        InstallSignature::FromValue(*pref);
166    if (!signature_from_prefs.get()) {
167      LogInitResultHistogram(INIT_UNPARSEABLE_PREF);
168    } else if (!InstallSigner::VerifySignature(*signature_from_prefs.get())) {
169      LogInitResultHistogram(INIT_INVALID_SIGNATURE);
170      DVLOG(1) << "Init - ignoring invalid signature";
171    } else {
172      signature_ = signature_from_prefs.Pass();
173      LogInitResultHistogram(INIT_VALID_SIGNATURE);
174      UMA_HISTOGRAM_COUNTS_100("ExtensionInstallVerifier.InitSignatureCount",
175                               signature_->ids.size());
176      GarbageCollect();
177    }
178  } else {
179    LogInitResultHistogram(INIT_NO_PREF);
180  }
181}
182
183bool InstallVerifier::NeedsBootstrap() {
184  return signature_.get() == NULL && ShouldFetchSignature();
185}
186
187base::Time InstallVerifier::SignatureTimestamp() {
188  if (signature_.get())
189    return signature_->timestamp;
190  else
191    return base::Time();
192}
193
194bool InstallVerifier::IsKnownId(const std::string& id) {
195  return signature_.get() && (ContainsKey(signature_->ids, id) ||
196                              ContainsKey(signature_->invalid_ids, id));
197}
198
199void InstallVerifier::Add(const std::string& id,
200                          const AddResultCallback& callback) {
201  ExtensionIdSet ids;
202  ids.insert(id);
203  AddMany(ids, callback);
204}
205
206void InstallVerifier::AddMany(const ExtensionIdSet& ids,
207                              const AddResultCallback& callback) {
208  if (!ShouldFetchSignature()) {
209    if (!callback.is_null())
210      callback.Run(true);
211    return;
212  }
213
214  if (signature_.get()) {
215    ExtensionIdSet not_allowed_yet =
216        base::STLSetDifference<ExtensionIdSet>(ids, signature_->ids);
217    if (not_allowed_yet.empty()) {
218      if (!callback.is_null())
219        callback.Run(true);
220      return;
221    }
222  }
223
224  InstallVerifier::PendingOperation* operation =
225    new InstallVerifier::PendingOperation();
226  operation->type = InstallVerifier::ADD;
227  operation->ids.insert(ids.begin(), ids.end());
228  operation->callback = callback;
229
230  operation_queue_.push(linked_ptr<PendingOperation>(operation));
231
232  // If there are no ongoing pending requests, we need to kick one off.
233  if (operation_queue_.size() == 1)
234    BeginFetch();
235}
236
237void InstallVerifier::AddProvisional(const ExtensionIdSet& ids) {
238  provisional_.insert(ids.begin(), ids.end());
239  AddMany(ids, AddResultCallback());
240}
241
242void InstallVerifier::Remove(const std::string& id) {
243  ExtensionIdSet ids;
244  ids.insert(id);
245  RemoveMany(ids);
246}
247
248void InstallVerifier::RemoveMany(const ExtensionIdSet& ids) {
249  if (!signature_.get() || !ShouldFetchSignature())
250    return;
251
252  bool found_any = false;
253  for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); ++i) {
254    if (ContainsKey(signature_->ids, *i) ||
255        ContainsKey(signature_->invalid_ids, *i)) {
256      found_any = true;
257      break;
258    }
259  }
260  if (!found_any)
261    return;
262
263  InstallVerifier::PendingOperation* operation =
264    new InstallVerifier::PendingOperation();
265  operation->type = InstallVerifier::REMOVE;
266  operation->ids = ids;
267
268  operation_queue_.push(linked_ptr<PendingOperation>(operation));
269  if (operation_queue_.size() == 1)
270    BeginFetch();
271}
272
273std::string InstallVerifier::GetDebugPolicyProviderName() const {
274  return std::string("InstallVerifier");
275}
276
277namespace {
278
279enum MustRemainDisabledOutcome {
280  VERIFIED = 0,
281  NOT_EXTENSION,
282  UNPACKED,
283  ENTERPRISE_POLICY_ALLOWED,
284  FORCED_NOT_VERIFIED,
285  NOT_FROM_STORE,
286  NO_SIGNATURE,
287  NOT_VERIFIED_BUT_NOT_ENFORCING,
288  NOT_VERIFIED,
289  NOT_VERIFIED_BUT_INSTALL_TIME_NEWER_THAN_SIGNATURE,
290  NOT_VERIFIED_BUT_UNKNOWN_ID,
291  COMPONENT,
292
293  // This is used in histograms - do not remove or reorder entries above! Also
294  // the "MAX" item below should always be the last element.
295  MUST_REMAIN_DISABLED_OUTCOME_MAX
296};
297
298void MustRemainDisabledHistogram(MustRemainDisabledOutcome outcome) {
299  UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.MustRemainDisabled",
300                            outcome, MUST_REMAIN_DISABLED_OUTCOME_MAX);
301}
302
303}  // namespace
304
305bool InstallVerifier::MustRemainDisabled(const Extension* extension,
306                                         Extension::DisableReason* reason,
307                                         base::string16* error) const {
308  CHECK(extension);
309  if (!CanUseExtensionApis(*extension)) {
310    MustRemainDisabledHistogram(NOT_EXTENSION);
311    return false;
312  }
313  if (Manifest::IsUnpackedLocation(extension->location())) {
314    MustRemainDisabledHistogram(UNPACKED);
315    return false;
316  }
317  if (extension->location() == Manifest::COMPONENT) {
318    MustRemainDisabledHistogram(COMPONENT);
319    return false;
320  }
321  if (AllowedByEnterprisePolicy(extension->id())) {
322    MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED);
323    return false;
324  }
325
326  bool verified = true;
327  MustRemainDisabledOutcome outcome = VERIFIED;
328  if (ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension->id())) {
329    verified = false;
330    outcome = FORCED_NOT_VERIFIED;
331  } else if (!FromStore(*extension)) {
332    verified = false;
333    outcome = NOT_FROM_STORE;
334  } else if (signature_.get() == NULL) {
335    // If we don't have a signature yet, we'll temporarily consider every
336    // extension from the webstore verified to avoid false positives on existing
337    // profiles hitting this code for the first time, and rely on consumers of
338    // this class to check NeedsBootstrap() and schedule a first check so we can
339    // get a signature.
340    outcome = NO_SIGNATURE;
341  } else if (!IsVerified(extension->id())) {
342    if (!ContainsKey(signature_->invalid_ids, extension->id())) {
343      outcome = NOT_VERIFIED_BUT_UNKNOWN_ID;
344    } else {
345      verified = false;
346      outcome = NOT_VERIFIED;
347    }
348  }
349  if (!verified && !ShouldEnforce()) {
350    verified = true;
351    outcome = NOT_VERIFIED_BUT_NOT_ENFORCING;
352  }
353  MustRemainDisabledHistogram(outcome);
354
355  if (!verified) {
356    if (reason)
357      *reason = Extension::DISABLE_NOT_VERIFIED;
358    if (error)
359      *error = l10n_util::GetStringFUTF16(
360          IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
361          l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE));
362  }
363  return !verified;
364}
365
366InstallVerifier::PendingOperation::PendingOperation() {
367  type = InstallVerifier::ADD;
368}
369
370InstallVerifier::PendingOperation::~PendingOperation() {
371}
372
373void InstallVerifier::GarbageCollect() {
374  if (!ShouldFetchSignature()) {
375    return;
376  }
377  CHECK(signature_.get());
378  ExtensionIdSet leftovers = signature_->ids;
379  leftovers.insert(signature_->invalid_ids.begin(),
380                   signature_->invalid_ids.end());
381  ExtensionIdList all_ids;
382  prefs_->GetExtensions(&all_ids);
383  for (ExtensionIdList::const_iterator i = all_ids.begin();
384       i != all_ids.end(); ++i) {
385    ExtensionIdSet::iterator found = leftovers.find(*i);
386    if (found != leftovers.end())
387      leftovers.erase(found);
388  }
389  if (!leftovers.empty()) {
390    RemoveMany(leftovers);
391  }
392}
393
394bool InstallVerifier::AllowedByEnterprisePolicy(const std::string& id) const {
395  PrefService* pref_service = prefs_->pref_service();
396  if (pref_service->IsManagedPreference(pref_names::kInstallAllowList)) {
397    const base::ListValue* whitelist =
398        pref_service->GetList(pref_names::kInstallAllowList);
399    base::StringValue id_value(id);
400    if (whitelist && whitelist->Find(id_value) != whitelist->end())
401      return true;
402  }
403  if (pref_service->IsManagedPreference(pref_names::kInstallForceList)) {
404    const base::DictionaryValue* forcelist =
405        pref_service->GetDictionary(pref_names::kInstallForceList);
406    if (forcelist && forcelist->HasKey(id))
407      return true;
408  }
409  return false;
410}
411
412bool InstallVerifier::IsVerified(const std::string& id) const {
413  return ((signature_.get() && ContainsKey(signature_->ids, id)) ||
414          ContainsKey(provisional_, id));
415}
416
417void InstallVerifier::BeginFetch() {
418  DCHECK(ShouldFetchSignature());
419
420  // TODO(asargent) - It would be possible to coalesce all operations in the
421  // queue into one fetch - we'd probably just need to change the queue to
422  // hold (set of ids, list of callbacks) pairs.
423  CHECK(!operation_queue_.empty());
424  const PendingOperation& operation = *operation_queue_.front();
425
426  ExtensionIdSet ids_to_sign;
427  if (signature_.get()) {
428    ids_to_sign.insert(signature_->ids.begin(), signature_->ids.end());
429  }
430  if (operation.type == InstallVerifier::ADD) {
431    ids_to_sign.insert(operation.ids.begin(), operation.ids.end());
432  } else {
433    for (ExtensionIdSet::const_iterator i = operation.ids.begin();
434         i != operation.ids.end(); ++i) {
435      if (ContainsKey(ids_to_sign, *i))
436        ids_to_sign.erase(*i);
437    }
438  }
439
440  signer_.reset(new InstallSigner(context_getter_, ids_to_sign));
441  signer_->GetSignature(base::Bind(&InstallVerifier::SignatureCallback,
442                                   base::Unretained(this)));
443}
444
445void InstallVerifier::SaveToPrefs() {
446  if (signature_.get())
447    DCHECK(InstallSigner::VerifySignature(*signature_));
448
449  if (!signature_.get() || signature_->ids.empty()) {
450    DVLOG(1) << "SaveToPrefs - saving NULL";
451    prefs_->SetInstallSignature(NULL);
452  } else {
453    base::DictionaryValue pref;
454    signature_->ToValue(&pref);
455    if (VLOG_IS_ON(1)) {
456      DVLOG(1) << "SaveToPrefs - saving";
457
458      DCHECK(InstallSigner::VerifySignature(*signature_.get()));
459      scoped_ptr<InstallSignature> rehydrated =
460          InstallSignature::FromValue(pref);
461      DCHECK(InstallSigner::VerifySignature(*rehydrated.get()));
462    }
463    prefs_->SetInstallSignature(&pref);
464  }
465}
466
467namespace {
468
469enum CallbackResult {
470  CALLBACK_NO_SIGNATURE = 0,
471  CALLBACK_INVALID_SIGNATURE,
472  CALLBACK_VALID_SIGNATURE,
473
474  // This is used in histograms - do not remove or reorder entries above! Also
475  // the "MAX" item below should always be the last element.
476
477  CALLBACK_RESULT_MAX
478};
479
480void GetSignatureResultHistogram(CallbackResult result) {
481  UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult",
482                            result, CALLBACK_RESULT_MAX);
483}
484
485}  // namespace
486
487void InstallVerifier::SignatureCallback(
488    scoped_ptr<InstallSignature> signature) {
489
490  linked_ptr<PendingOperation> operation = operation_queue_.front();
491  operation_queue_.pop();
492
493  bool success = false;
494  if (!signature.get()) {
495    GetSignatureResultHistogram(CALLBACK_NO_SIGNATURE);
496  } else if (!InstallSigner::VerifySignature(*signature)) {
497    GetSignatureResultHistogram(CALLBACK_INVALID_SIGNATURE);
498  } else {
499    GetSignatureResultHistogram(CALLBACK_VALID_SIGNATURE);
500    success = true;
501  }
502
503  if (!success) {
504    if (!operation->callback.is_null())
505      operation->callback.Run(false);
506
507    // TODO(asargent) - if this was something like a network error, we need to
508    // do retries with exponential back off.
509  } else {
510    signature_ = signature.Pass();
511    SaveToPrefs();
512
513    if (!provisional_.empty()) {
514      // Update |provisional_| to remove ids that were successfully signed.
515      provisional_ = base::STLSetDifference<ExtensionIdSet>(
516          provisional_, signature_->ids);
517    }
518
519    if (!operation->callback.is_null())
520      operation->callback.Run(success);
521  }
522
523  if (!operation_queue_.empty())
524    BeginFetch();
525}
526
527
528}  // namespace extensions
529