• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/extension_prefs.h"
17 #include "chrome/browser/extensions/install_signer.h"
18 #include "chrome/common/chrome_switches.h"
19 #include "chrome/common/extensions/manifest_url_handler.h"
20 #include "chrome/common/pref_names.h"
21 #include "content/public/common/content_switches.h"
22 #include "extensions/common/manifest.h"
23 #include "grit/generated_resources.h"
24 #include "ui/base/l10n/l10n_util.h"
25 
26 namespace {
27 
28 enum VerifyStatus {
29   NONE = 0,   // Do not request install signatures, and do not enforce them.
30   BOOTSTRAP,  // Request install signatures, but do not enforce them.
31   ENFORCE,    // Request install signatures, and enforce them.
32 
33   // This is used in histograms - do not remove or reorder entries above! Also
34   // the "MAX" item below should always be the last element.
35 
36   VERIFY_STATUS_MAX
37 };
38 
39 #if defined(GOOGLE_CHROME_BUILD)
40 const char kExperimentName[] = "ExtensionInstallVerification";
41 #endif  // defined(GOOGLE_CHROME_BUILD)
42 
GetExperimentStatus()43 VerifyStatus GetExperimentStatus() {
44 #if defined(GOOGLE_CHROME_BUILD)
45   const std::string group = base::FieldTrialList::FindFullName(
46       kExperimentName);
47 
48   std::string forced_trials = CommandLine::ForCurrentProcess()->
49       GetSwitchValueASCII(switches::kForceFieldTrials);
50   if (forced_trials.find(kExperimentName) != std::string::npos) {
51     // We don't want to allow turning off enforcement by forcing the field
52     // trial group to something other than enforcement.
53     return ENFORCE;
54   }
55 
56   VerifyStatus default_status = NONE;
57 
58   if (group == "Enforce")
59     return ENFORCE;
60   else if (group == "Bootstrap")
61     return BOOTSTRAP;
62   else if (group == "None" || group == "Control")
63     return NONE;
64   else
65     return default_status;
66 #endif  // defined(GOOGLE_CHROME_BUILD)
67 
68   return NONE;
69 }
70 
GetCommandLineStatus()71 VerifyStatus GetCommandLineStatus() {
72   const CommandLine* cmdline = CommandLine::ForCurrentProcess();
73   if (!extensions::InstallSigner::GetForcedNotFromWebstore().empty())
74     return ENFORCE;
75 
76   if (cmdline->HasSwitch(switches::kExtensionsInstallVerification)) {
77     std::string value = cmdline->GetSwitchValueASCII(
78         switches::kExtensionsInstallVerification);
79     if (value == "bootstrap")
80       return BOOTSTRAP;
81     else
82       return ENFORCE;
83   }
84 
85   return NONE;
86 }
87 
GetStatus()88 VerifyStatus GetStatus() {
89   return std::max(GetExperimentStatus(), GetCommandLineStatus());
90 }
91 
ShouldFetchSignature()92 bool ShouldFetchSignature() {
93   VerifyStatus status = GetStatus();
94   return (status == BOOTSTRAP || status == ENFORCE);
95 }
96 
ShouldEnforce()97 bool ShouldEnforce() {
98   return GetStatus() == ENFORCE;
99 }
100 
101 }  // namespace
102 
103 namespace extensions {
104 
InstallVerifier(ExtensionPrefs * prefs,net::URLRequestContextGetter * context_getter)105 InstallVerifier::InstallVerifier(ExtensionPrefs* prefs,
106                                  net::URLRequestContextGetter* context_getter)
107     : prefs_(prefs), context_getter_(context_getter) {
108 }
109 
~InstallVerifier()110 InstallVerifier::~InstallVerifier() {}
111 
112 namespace {
113 
114 enum InitResult {
115   INIT_NO_PREF = 0,
116   INIT_UNPARSEABLE_PREF,
117   INIT_INVALID_SIGNATURE,
118   INIT_VALID_SIGNATURE,
119 
120   // This is used in histograms - do not remove or reorder entries above! Also
121   // the "MAX" item below should always be the last element.
122 
123   INIT_RESULT_MAX
124 };
125 
LogInitResultHistogram(InitResult result)126 void LogInitResultHistogram(InitResult result) {
127   UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.InitResult",
128                             result, INIT_RESULT_MAX);
129 }
130 
FromStore(const Extension & extension)131 bool FromStore(const Extension& extension) {
132   bool updates_from_store = ManifestURL::UpdatesFromGallery(&extension);
133   return extension.from_webstore() || updates_from_store;
134 }
135 
CanUseExtensionApis(const Extension & extension)136 bool CanUseExtensionApis(const Extension& extension) {
137   return extension.is_extension() || extension.is_legacy_packaged_app();
138 }
139 
140 }  // namespace
141 
142 // static
NeedsVerification(const Extension & extension)143 bool InstallVerifier::NeedsVerification(const Extension& extension) {
144   return FromStore(extension) && CanUseExtensionApis(extension);
145 }
146 
Init()147 void InstallVerifier::Init() {
148   UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ExperimentStatus",
149                             GetExperimentStatus(), VERIFY_STATUS_MAX);
150   UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.ActualStatus",
151                             GetStatus(), VERIFY_STATUS_MAX);
152 
153   const DictionaryValue* pref = prefs_->GetInstallSignature();
154   if (pref) {
155     scoped_ptr<InstallSignature> signature_from_prefs =
156         InstallSignature::FromValue(*pref);
157     if (!signature_from_prefs.get()) {
158       LogInitResultHistogram(INIT_UNPARSEABLE_PREF);
159     } else if (!InstallSigner::VerifySignature(*signature_from_prefs.get())) {
160       LogInitResultHistogram(INIT_INVALID_SIGNATURE);
161       DVLOG(1) << "Init - ignoring invalid signature";
162     } else {
163       signature_ = signature_from_prefs.Pass();
164       LogInitResultHistogram(INIT_VALID_SIGNATURE);
165       UMA_HISTOGRAM_COUNTS_100("ExtensionInstallVerifier.InitSignatureCount",
166                                signature_->ids.size());
167       GarbageCollect();
168     }
169   } else {
170     LogInitResultHistogram(INIT_NO_PREF);
171   }
172 }
173 
NeedsBootstrap()174 bool InstallVerifier::NeedsBootstrap() {
175   return signature_.get() == NULL && ShouldFetchSignature();
176 }
177 
SignatureTimestamp()178 base::Time InstallVerifier::SignatureTimestamp() {
179   if (signature_.get())
180     return signature_->timestamp;
181   else
182     return base::Time();
183 }
184 
Add(const std::string & id,const AddResultCallback & callback)185 void InstallVerifier::Add(const std::string& id,
186                           const AddResultCallback& callback) {
187   ExtensionIdSet ids;
188   ids.insert(id);
189   AddMany(ids, callback);
190 }
191 
AddMany(const ExtensionIdSet & ids,const AddResultCallback & callback)192 void InstallVerifier::AddMany(const ExtensionIdSet& ids,
193                               const AddResultCallback& callback) {
194   if (!ShouldFetchSignature()) {
195     if (!callback.is_null())
196       callback.Run(true);
197     return;
198   }
199 
200   if (signature_.get()) {
201     ExtensionIdSet not_allowed_yet =
202         base::STLSetDifference<ExtensionIdSet>(ids, signature_->ids);
203     if (not_allowed_yet.empty()) {
204       if (!callback.is_null())
205         callback.Run(true);
206       return;
207     }
208   }
209 
210   InstallVerifier::PendingOperation* operation =
211     new InstallVerifier::PendingOperation();
212   operation->type = InstallVerifier::ADD;
213   operation->ids.insert(ids.begin(), ids.end());
214   operation->callback = callback;
215 
216   operation_queue_.push(linked_ptr<PendingOperation>(operation));
217 
218   // If there are no ongoing pending requests, we need to kick one off.
219   if (operation_queue_.size() == 1)
220     BeginFetch();
221 }
222 
AddProvisional(const ExtensionIdSet & ids)223 void InstallVerifier::AddProvisional(const ExtensionIdSet& ids) {
224   provisional_.insert(ids.begin(), ids.end());
225   AddMany(ids, AddResultCallback());
226 }
227 
Remove(const std::string & id)228 void InstallVerifier::Remove(const std::string& id) {
229   ExtensionIdSet ids;
230   ids.insert(id);
231   RemoveMany(ids);
232 }
233 
RemoveMany(const ExtensionIdSet & ids)234 void InstallVerifier::RemoveMany(const ExtensionIdSet& ids) {
235   if (!signature_.get() || !ShouldFetchSignature())
236     return;
237 
238   bool found_any = false;
239   for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); ++i) {
240     if (ContainsKey(signature_->ids, *i)) {
241       found_any = true;
242       break;
243     }
244   }
245   if (!found_any)
246     return;
247 
248   InstallVerifier::PendingOperation* operation =
249     new InstallVerifier::PendingOperation();
250   operation->type = InstallVerifier::REMOVE;
251   operation->ids = ids;
252 
253   operation_queue_.push(linked_ptr<PendingOperation>(operation));
254   if (operation_queue_.size() == 1)
255     BeginFetch();
256 }
257 
GetDebugPolicyProviderName() const258 std::string InstallVerifier::GetDebugPolicyProviderName() const {
259   return std::string("InstallVerifier");
260 }
261 
262 namespace {
263 
264 enum MustRemainDisabledOutcome {
265   VERIFIED = 0,
266   NOT_EXTENSION,
267   UNPACKED,
268   ENTERPRISE_POLICY_ALLOWED,
269   FORCED_NOT_VERIFIED,
270   NOT_FROM_STORE,
271   NO_SIGNATURE,
272   NOT_VERIFIED_BUT_NOT_ENFORCING,
273   NOT_VERIFIED,
274   NOT_VERIFIED_BUT_INSTALL_TIME_NEWER_THAN_SIGNATURE,
275 
276   // This is used in histograms - do not remove or reorder entries above! Also
277   // the "MAX" item below should always be the last element.
278   MUST_REMAIN_DISABLED_OUTCOME_MAX
279 };
280 
MustRemainDisabledHistogram(MustRemainDisabledOutcome outcome)281 void MustRemainDisabledHistogram(MustRemainDisabledOutcome outcome) {
282   UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.MustRemainDisabled",
283                             outcome, MUST_REMAIN_DISABLED_OUTCOME_MAX);
284 }
285 
286 }  // namespace
287 
MustRemainDisabled(const Extension * extension,Extension::DisableReason * reason,base::string16 * error) const288 bool InstallVerifier::MustRemainDisabled(const Extension* extension,
289                                          Extension::DisableReason* reason,
290                                          base::string16* error) const {
291   if (!CanUseExtensionApis(*extension)) {
292     MustRemainDisabledHistogram(NOT_EXTENSION);
293     return false;
294   }
295   if (Manifest::IsUnpackedLocation(extension->location())) {
296     MustRemainDisabledHistogram(UNPACKED);
297     return false;
298   }
299   if (AllowedByEnterprisePolicy(extension->id())) {
300     MustRemainDisabledHistogram(ENTERPRISE_POLICY_ALLOWED);
301     return false;
302   }
303 
304   bool verified = true;
305   MustRemainDisabledOutcome outcome = VERIFIED;
306   if (ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension->id())) {
307     verified = false;
308     outcome = FORCED_NOT_VERIFIED;
309   } else if (!FromStore(*extension)) {
310     verified = false;
311     outcome = NOT_FROM_STORE;
312   } else if (signature_.get() == NULL) {
313     // If we don't have a signature yet, we'll temporarily consider every
314     // extension from the webstore verified to avoid false positives on existing
315     // profiles hitting this code for the first time, and rely on consumers of
316     // this class to check NeedsBootstrap() and schedule a first check so we can
317     // get a signature.
318     outcome = NO_SIGNATURE;
319   } else if (!IsVerified(extension->id())) {
320     if (WasInstalledAfterSignature(extension->id())) {
321       outcome = NOT_VERIFIED_BUT_INSTALL_TIME_NEWER_THAN_SIGNATURE;
322     } else {
323       verified = false;
324       outcome = NOT_VERIFIED;
325     }
326   }
327   if (!verified && !ShouldEnforce()) {
328     verified = true;
329     outcome = NOT_VERIFIED_BUT_NOT_ENFORCING;
330   }
331   MustRemainDisabledHistogram(outcome);
332 
333   if (!verified) {
334     if (reason)
335       *reason = Extension::DISABLE_NOT_VERIFIED;
336     if (error)
337       *error = l10n_util::GetStringFUTF16(
338           IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
339           l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE));
340   }
341   return !verified;
342 }
343 
PendingOperation()344 InstallVerifier::PendingOperation::PendingOperation() {
345   type = InstallVerifier::ADD;
346 }
347 
~PendingOperation()348 InstallVerifier::PendingOperation::~PendingOperation() {
349 }
350 
GarbageCollect()351 void InstallVerifier::GarbageCollect() {
352   if (!ShouldFetchSignature()) {
353     return;
354   }
355   CHECK(signature_.get());
356   ExtensionIdSet leftovers = signature_->ids;
357   ExtensionIdList all_ids;
358   prefs_->GetExtensions(&all_ids);
359   for (ExtensionIdList::const_iterator i = all_ids.begin();
360        i != all_ids.end(); ++i) {
361     ExtensionIdSet::iterator found = leftovers.find(*i);
362     if (found != leftovers.end())
363       leftovers.erase(found);
364   }
365   if (!leftovers.empty()) {
366     RemoveMany(leftovers);
367   }
368 }
369 
AllowedByEnterprisePolicy(const std::string & id) const370 bool InstallVerifier::AllowedByEnterprisePolicy(const std::string& id) const {
371   PrefService* pref_service = prefs_->pref_service();
372   if (pref_service->IsManagedPreference(prefs::kExtensionInstallAllowList)) {
373     const base::ListValue* whitelist =
374         pref_service->GetList(prefs::kExtensionInstallAllowList);
375     base::StringValue id_value(id);
376     if (whitelist && whitelist->Find(id_value) != whitelist->end())
377       return true;
378   }
379   if (pref_service->IsManagedPreference(prefs::kExtensionInstallForceList)) {
380     const base::DictionaryValue* forcelist =
381         pref_service->GetDictionary(prefs::kExtensionInstallForceList);
382     if (forcelist && forcelist->HasKey(id))
383       return true;
384   }
385   return false;
386 }
387 
IsVerified(const std::string & id) const388 bool InstallVerifier::IsVerified(const std::string& id) const {
389   return ((signature_.get() && ContainsKey(signature_->ids, id)) ||
390           ContainsKey(provisional_, id));
391 }
392 
WasInstalledAfterSignature(const std::string & id) const393 bool InstallVerifier::WasInstalledAfterSignature(const std::string& id) const {
394   if (!signature_.get() || signature_->timestamp.is_null())
395     return true;
396 
397   base::Time install_time = prefs_->GetInstallTime(id);
398   // If the extension install time is in the future, just assume it isn't
399   // newer than the signature. (Either the clock went backwards, or
400   // an attacker changed the install time in the preferences).
401   if (install_time >= base::Time::Now())
402     return false;
403   return install_time > signature_->timestamp;
404 }
405 
BeginFetch()406 void InstallVerifier::BeginFetch() {
407   DCHECK(ShouldFetchSignature());
408 
409   // TODO(asargent) - It would be possible to coalesce all operations in the
410   // queue into one fetch - we'd probably just need to change the queue to
411   // hold (set of ids, list of callbacks) pairs.
412   CHECK(!operation_queue_.empty());
413   const PendingOperation& operation = *operation_queue_.front();
414 
415   ExtensionIdSet ids_to_sign;
416   if (signature_.get()) {
417     ids_to_sign.insert(signature_->ids.begin(), signature_->ids.end());
418   }
419   if (operation.type == InstallVerifier::ADD) {
420     ids_to_sign.insert(operation.ids.begin(), operation.ids.end());
421   } else {
422     for (ExtensionIdSet::const_iterator i = operation.ids.begin();
423          i != operation.ids.end(); ++i) {
424       if (ContainsKey(ids_to_sign, *i))
425         ids_to_sign.erase(*i);
426     }
427   }
428 
429   signer_.reset(new InstallSigner(context_getter_, ids_to_sign));
430   signer_->GetSignature(base::Bind(&InstallVerifier::SignatureCallback,
431                                    base::Unretained(this)));
432 }
433 
SaveToPrefs()434 void InstallVerifier::SaveToPrefs() {
435   if (signature_.get())
436     DCHECK(InstallSigner::VerifySignature(*signature_));
437 
438   if (!signature_.get() || signature_->ids.empty()) {
439     DVLOG(1) << "SaveToPrefs - saving NULL";
440     prefs_->SetInstallSignature(NULL);
441   } else {
442     DictionaryValue pref;
443     signature_->ToValue(&pref);
444     if (VLOG_IS_ON(1)) {
445       DVLOG(1) << "SaveToPrefs - saving";
446 
447       DCHECK(InstallSigner::VerifySignature(*signature_.get()));
448       scoped_ptr<InstallSignature> rehydrated =
449           InstallSignature::FromValue(pref);
450       DCHECK(InstallSigner::VerifySignature(*rehydrated.get()));
451     }
452     prefs_->SetInstallSignature(&pref);
453   }
454 }
455 
456 namespace {
457 
458 enum CallbackResult {
459   CALLBACK_NO_SIGNATURE = 0,
460   CALLBACK_INVALID_SIGNATURE,
461   CALLBACK_VALID_SIGNATURE,
462 
463   // This is used in histograms - do not remove or reorder entries above! Also
464   // the "MAX" item below should always be the last element.
465 
466   CALLBACK_RESULT_MAX
467 };
468 
GetSignatureResultHistogram(CallbackResult result)469 void GetSignatureResultHistogram(CallbackResult result) {
470   UMA_HISTOGRAM_ENUMERATION("ExtensionInstallVerifier.GetSignatureResult",
471                             result, CALLBACK_RESULT_MAX);
472 }
473 
474 }  // namespace
475 
SignatureCallback(scoped_ptr<InstallSignature> signature)476 void InstallVerifier::SignatureCallback(
477     scoped_ptr<InstallSignature> signature) {
478 
479   linked_ptr<PendingOperation> operation = operation_queue_.front();
480   operation_queue_.pop();
481 
482   bool success = false;
483   if (!signature.get()) {
484     GetSignatureResultHistogram(CALLBACK_NO_SIGNATURE);
485   } else if (!InstallSigner::VerifySignature(*signature)) {
486     GetSignatureResultHistogram(CALLBACK_INVALID_SIGNATURE);
487   } else {
488     GetSignatureResultHistogram(CALLBACK_VALID_SIGNATURE);
489     success = true;
490   }
491 
492   if (!success) {
493     if (!operation->callback.is_null())
494       operation->callback.Run(false);
495 
496     // TODO(asargent) - if this was something like a network error, we need to
497     // do retries with exponential back off.
498   } else {
499     signature_ = signature.Pass();
500     SaveToPrefs();
501 
502     if (!provisional_.empty()) {
503       // Update |provisional_| to remove ids that were successfully signed.
504       provisional_ = base::STLSetDifference<ExtensionIdSet>(
505           provisional_, signature_->ids);
506     }
507 
508     if (!operation->callback.is_null())
509       operation->callback.Run(success);
510   }
511 
512   if (!operation_queue_.empty())
513     BeginFetch();
514 }
515 
516 
517 }  // namespace extensions
518