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