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