install_verifier.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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 "base/bind.h"
8#include "base/command_line.h"
9#include "base/metrics/histogram.h"
10#include "base/prefs/pref_service.h"
11#include "base/stl_util.h"
12#include "chrome/browser/extensions/extension_prefs.h"
13#include "chrome/browser/extensions/install_signer.h"
14#include "chrome/common/chrome_switches.h"
15#include "chrome/common/extensions/manifest_url_handler.h"
16#include "chrome/common/pref_names.h"
17#include "extensions/common/manifest.h"
18#include "grit/generated_resources.h"
19#include "ui/base/l10n/l10n_util.h"
20
21namespace {
22
23enum VerifyStatus {
24  NONE,       // Do not request install signatures, and do not enforce them.
25  BOOTSTRAP,  // Request install signatures, but do not enforce them.
26  ENFORCE     // Request install signatures, and enforce them.
27};
28
29VerifyStatus GetStatus() {
30  const CommandLine* cmdline = CommandLine::ForCurrentProcess();
31  if (!extensions::InstallSigner::GetForcedNotFromWebstore().empty())
32    return ENFORCE;
33
34  if (cmdline->HasSwitch(switches::kExtensionsInstallVerification)) {
35    std::string value = cmdline->GetSwitchValueASCII(
36        switches::kExtensionsInstallVerification);
37    if (value == "bootstrap")
38      return BOOTSTRAP;
39    else
40      return ENFORCE;
41  }
42
43  return NONE;
44}
45
46bool ShouldFetchSignature() {
47  VerifyStatus status = GetStatus();
48  return (status == BOOTSTRAP || status == ENFORCE);
49}
50
51bool ShouldEnforce() {
52  return GetStatus() == ENFORCE;
53}
54
55}  // namespace
56
57namespace extensions {
58
59InstallVerifier::InstallVerifier(ExtensionPrefs* prefs,
60                                 net::URLRequestContextGetter* context_getter)
61    : prefs_(prefs), context_getter_(context_getter) {
62}
63
64InstallVerifier::~InstallVerifier() {}
65
66void InstallVerifier::Init() {
67  const DictionaryValue* pref = prefs_->GetInstallSignature();
68  if (pref) {
69    scoped_ptr<InstallSignature> signature_from_prefs =
70        InstallSignature::FromValue(*pref);
71    if (!signature_from_prefs.get()) {
72      UMA_HISTOGRAM_BOOLEAN("InstallVerifier.InitUnparseablePref", true);
73    } else if (!InstallSigner::VerifySignature(*signature_from_prefs.get())) {
74      UMA_HISTOGRAM_BOOLEAN("InstallVerifier.InitInvalidSignature", true);
75      DVLOG(1) << "Init - ignoring invalid signature";
76    } else {
77      signature_ = signature_from_prefs.Pass();
78      GarbageCollect();
79    }
80  }
81
82  if (!signature_.get() && ShouldFetchSignature()) {
83    // We didn't have any signature but are in fetch mode, so do a request for
84    // a signature if needed.
85    scoped_ptr<ExtensionPrefs::ExtensionsInfo> all_info =
86        prefs_->GetInstalledExtensionsInfo();
87    ExtensionIdSet to_add;
88    if (all_info.get()) {
89      for (ExtensionPrefs::ExtensionsInfo::const_iterator i = all_info->begin();
90           i != all_info->end(); ++i) {
91        const ExtensionInfo& info = **i;
92        const base::DictionaryValue* manifest = info.extension_manifest.get();
93        if (manifest && ManifestURL::UpdatesFromGallery(manifest))
94          to_add.insert(info.extension_id);
95      }
96    }
97    if (!to_add.empty())
98      AddMany(to_add, AddResultCallback());
99  }
100}
101
102void InstallVerifier::Add(const std::string& id,
103                          const AddResultCallback& callback) {
104  ExtensionIdSet ids;
105  ids.insert(id);
106  AddMany(ids, callback);
107}
108
109void InstallVerifier::AddMany(const ExtensionIdSet& ids,
110                              const AddResultCallback& callback) {
111  if (!ShouldFetchSignature())
112    return;
113
114  if (signature_.get()) {
115    ExtensionIdSet not_allowed_yet =
116        base::STLSetDifference<ExtensionIdSet>(ids, signature_->ids);
117    if (not_allowed_yet.empty()) {
118      if (!callback.is_null())
119        callback.Run(true);
120      return;
121    }
122  }
123
124  InstallVerifier::PendingOperation* operation =
125    new InstallVerifier::PendingOperation();
126  operation->type = InstallVerifier::ADD;
127  operation->ids.insert(ids.begin(), ids.end());
128  operation->callback = callback;
129
130  operation_queue_.push(linked_ptr<PendingOperation>(operation));
131
132  // If there are no ongoing pending requests, we need to kick one off.
133  if (operation_queue_.size() == 1)
134    BeginFetch();
135}
136
137void InstallVerifier::AddProvisional(const ExtensionIdSet& ids) {
138  provisional_.insert(ids.begin(), ids.end());
139  AddMany(ids, AddResultCallback());
140}
141
142void InstallVerifier::Remove(const std::string& id) {
143  ExtensionIdSet ids;
144  ids.insert(id);
145  RemoveMany(ids);
146}
147
148void InstallVerifier::RemoveMany(const ExtensionIdSet& ids) {
149  if (!signature_.get() || !ShouldFetchSignature())
150    return;
151
152  bool found_any = false;
153  for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); ++i) {
154    if (ContainsKey(signature_->ids, *i)) {
155      found_any = true;
156      break;
157    }
158  }
159  if (!found_any)
160    return;
161
162  InstallVerifier::PendingOperation* operation =
163    new InstallVerifier::PendingOperation();
164  operation->type = InstallVerifier::REMOVE;
165  operation->ids = ids;
166
167  operation_queue_.push(linked_ptr<PendingOperation>(operation));
168  if (operation_queue_.size() == 1)
169    BeginFetch();
170}
171
172std::string InstallVerifier::GetDebugPolicyProviderName() const {
173  return std::string("InstallVerifier");
174}
175
176static bool FromStore(const Extension* extension) {
177  bool updates_from_store = ManifestURL::UpdatesFromGallery(extension);
178  return extension->from_webstore() || updates_from_store;
179}
180
181bool InstallVerifier::MustRemainDisabled(const Extension* extension,
182                                         Extension::DisableReason* reason,
183                                         base::string16* error) const {
184  if (!ShouldEnforce() || !extension->is_extension() ||
185      Manifest::IsUnpackedLocation(extension->location()) ||
186      AllowedByEnterprisePolicy(extension->id()))
187    return false;
188
189  bool verified =
190      FromStore(extension) &&
191      IsVerified(extension->id()) &&
192      !ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension->id());
193
194  if (!verified) {
195    if (reason)
196      *reason = Extension::DISABLE_NOT_VERIFIED;
197    if (error)
198      *error = l10n_util::GetStringFUTF16(
199          IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
200          l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE));
201  }
202  return !verified;
203}
204
205InstallVerifier::PendingOperation::PendingOperation() {
206  type = InstallVerifier::ADD;
207}
208
209InstallVerifier::PendingOperation::~PendingOperation() {
210}
211
212void InstallVerifier::GarbageCollect() {
213  if (!ShouldFetchSignature()) {
214    return;
215  }
216  CHECK(signature_.get());
217  ExtensionIdSet leftovers = signature_->ids;
218  ExtensionIdList all_ids;
219  prefs_->GetExtensions(&all_ids);
220  for (ExtensionIdList::const_iterator i = all_ids.begin();
221       i != all_ids.end(); ++i) {
222    ExtensionIdSet::iterator found = leftovers.find(*i);
223    if (found != leftovers.end())
224      leftovers.erase(found);
225  }
226  if (!leftovers.empty()) {
227    RemoveMany(leftovers);
228  }
229}
230
231bool InstallVerifier::AllowedByEnterprisePolicy(const std::string& id) const {
232  PrefService* pref_service = prefs_->pref_service();
233  if (pref_service->IsManagedPreference(prefs::kExtensionInstallAllowList)) {
234    const base::ListValue* whitelist =
235        pref_service->GetList(prefs::kExtensionInstallAllowList);
236    base::StringValue id_value(id);
237    if (whitelist && whitelist->Find(id_value) != whitelist->end())
238      return true;
239  }
240  if (pref_service->IsManagedPreference(prefs::kExtensionInstallForceList)) {
241    const base::DictionaryValue* forcelist =
242        pref_service->GetDictionary(prefs::kExtensionInstallForceList);
243    if (forcelist && forcelist->HasKey(id))
244      return true;
245  }
246  return false;
247}
248
249bool InstallVerifier::IsVerified(const std::string& id) const {
250  return ((signature_.get() && ContainsKey(signature_->ids, id)) ||
251          ContainsKey(provisional_, id));
252}
253
254void InstallVerifier::BeginFetch() {
255  DCHECK(ShouldFetchSignature());
256
257  // TODO(asargent) - It would be possible to coalesce all operations in the
258  // queue into one fetch - we'd probably just need to change the queue to
259  // hold (set of ids, list of callbacks) pairs.
260  CHECK(!operation_queue_.empty());
261  const PendingOperation& operation = *operation_queue_.front();
262
263  ExtensionIdSet ids_to_sign;
264  if (signature_.get()) {
265    ids_to_sign.insert(signature_->ids.begin(), signature_->ids.end());
266  }
267  if (operation.type == InstallVerifier::ADD) {
268    ids_to_sign.insert(operation.ids.begin(), operation.ids.end());
269  } else {
270    for (ExtensionIdSet::const_iterator i = operation.ids.begin();
271         i != operation.ids.end(); ++i) {
272      if (ContainsKey(ids_to_sign, *i))
273        ids_to_sign.erase(*i);
274    }
275  }
276
277  signer_.reset(new InstallSigner(context_getter_, ids_to_sign));
278  signer_->GetSignature(base::Bind(&InstallVerifier::SignatureCallback,
279                                   base::Unretained(this)));
280}
281
282void InstallVerifier::SaveToPrefs() {
283  if (signature_.get())
284    DCHECK(InstallSigner::VerifySignature(*signature_));
285
286  if (!signature_.get() || signature_->ids.empty()) {
287    DVLOG(1) << "SaveToPrefs - saving NULL";
288    prefs_->SetInstallSignature(NULL);
289  } else {
290    DictionaryValue pref;
291    signature_->ToValue(&pref);
292    if (VLOG_IS_ON(1)) {
293      DVLOG(1) << "SaveToPrefs - saving";
294
295      DCHECK(InstallSigner::VerifySignature(*signature_.get()));
296      scoped_ptr<InstallSignature> rehydrated =
297          InstallSignature::FromValue(pref);
298      DCHECK(InstallSigner::VerifySignature(*rehydrated.get()));
299    }
300    prefs_->SetInstallSignature(&pref);
301  }
302}
303
304void InstallVerifier::SignatureCallback(
305    scoped_ptr<InstallSignature> signature) {
306
307  linked_ptr<PendingOperation> operation = operation_queue_.front();
308  operation_queue_.pop();
309
310  bool success = false;
311  if (!signature.get()) {
312    UMA_HISTOGRAM_BOOLEAN("InstallVerifier.CallbackNoSignature", true);
313  } else if (!InstallSigner::VerifySignature(*signature)) {
314    UMA_HISTOGRAM_BOOLEAN("InstallVerifier.CallbackInvalidSignature", true);
315  } else {
316    UMA_HISTOGRAM_BOOLEAN("InstallVerifier.CallbackValidSignature", true);
317    success = true;
318  }
319
320  if (!success) {
321    if (!operation->callback.is_null())
322      operation->callback.Run(false);
323
324    // TODO(asargent) - if this was something like a network error, we need to
325    // do retries with exponential back off.
326  } else {
327    signature_ = signature.Pass();
328    SaveToPrefs();
329
330    if (!provisional_.empty()) {
331      // Update |provisional_| to remove ids that were successfully signed.
332      provisional_ = base::STLSetDifference<ExtensionIdSet>(
333          provisional_, signature_->ids);
334    }
335
336    // See if we were able to sign all of |ids|.
337    ExtensionIdSet not_allowed =
338        base::STLSetDifference<ExtensionIdSet>(operation->ids,
339                                               signature_->ids);
340
341    UMA_HISTOGRAM_COUNTS_100("InstallVerifier.CallbackNotAllowed",
342                             not_allowed.size());
343
344    if (!operation->callback.is_null())
345      operation->callback.Run(not_allowed.empty());
346  }
347
348  if (!operation_queue_.empty())
349    BeginFetch();
350}
351
352
353}  // namespace extensions
354