summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorasargent@chromium.org <asargent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-14 00:11:46 +0000
committerasargent@chromium.org <asargent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-14 00:11:46 +0000
commitffd2f79ed3968c9aff2ea1c29e7ef6e3815f6f7d (patch)
tree6264db35a93f49dd871ead88734a1532461ac7f6
parent09feedf8a47a2430847e566984b137a572969e0c (diff)
downloadchromium_src-ffd2f79ed3968c9aff2ea1c29e7ef6e3815f6f7d.zip
chromium_src-ffd2f79ed3968c9aff2ea1c29e7ef6e3815f6f7d.tar.gz
chromium_src-ffd2f79ed3968c9aff2ea1c29e7ef6e3815f6f7d.tar.bz2
Base support for verifying extension installs
This code is the start of providing a mechanism for verifying that extensions are installed from the webstore or via enterprise policy, and not force-installed by malicious third-parties. BUG=318479 Review URL: https://codereview.chromium.org/70103003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@234970 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/extension_prefs.cc23
-rw-r--r--chrome/browser/extensions/extension_prefs.h5
-rw-r--r--chrome/browser/extensions/extension_service.cc9
-rw-r--r--chrome/browser/extensions/extension_system.cc15
-rw-r--r--chrome/browser/extensions/extension_system.h7
-rw-r--r--chrome/browser/extensions/install_signer.cc292
-rw-r--r--chrome/browser/extensions/install_signer.h109
-rw-r--r--chrome/browser/extensions/install_verifier.cc353
-rw-r--r--chrome/browser/extensions/install_verifier.h136
-rw-r--r--chrome/browser/extensions/test_extension_system.cc7
-rw-r--r--chrome/browser/extensions/test_extension_system.h2
-rw-r--r--chrome/browser/extensions/webstore_installer.cc8
-rw-r--r--chrome/chrome_browser_extensions.gypi4
-rw-r--r--chrome/common/chrome_switches.cc8
-rw-r--r--chrome/common/chrome_switches.h2
-rw-r--r--chrome/common/extensions/manifest_url_handler.cc8
-rw-r--r--chrome/common/extensions/manifest_url_handler.h5
17 files changed, 992 insertions, 1 deletions
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index e10ef94..f021de4 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -189,6 +189,9 @@ const char kPrefGeometryCache[] = "geometry_cache";
// A preference that indicates when an extension is last launched.
const char kPrefLastLaunchTime[] = "last_launch_time";
+// A list of installed ids and a signature.
+const char kInstallSignature[] = "extensions.install_signature";
+
// Provider of write access to a dictionary storing extension prefs.
class ScopedExtensionPrefUpdate : public DictionaryPrefUpdate {
public:
@@ -1714,6 +1717,21 @@ void ExtensionPrefs::SetGeometryCache(
UpdateExtensionPref(extension_id, kPrefGeometryCache, cache.release());
}
+const DictionaryValue* ExtensionPrefs::GetInstallSignature() {
+ return prefs_->GetDictionary(kInstallSignature);
+}
+
+void ExtensionPrefs::SetInstallSignature(const DictionaryValue* signature) {
+ if (signature) {
+ prefs_->Set(kInstallSignature, *signature);
+ DVLOG(1) << "SetInstallSignature - saving";
+ } else {
+ DVLOG(1) << "SetInstallSignature - clearing";
+ prefs_->ClearPref(kInstallSignature);
+ }
+}
+
+
ExtensionPrefs::ExtensionPrefs(
PrefService* prefs,
const base::FilePath& root_dir,
@@ -1787,13 +1805,16 @@ void ExtensionPrefs::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
registry->RegisterListPref(prefs::kExtensionKnownDisabled,
user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
-
#if defined(TOOLKIT_VIEWS)
registry->RegisterIntegerPref(
prefs::kBrowserActionContainerWidth,
0,
user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
#endif
+ registry->RegisterDictionaryPref(
+ kInstallSignature,
+ user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
+
}
template <class ExtensionIdContainer>
diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h
index edc98fd..081eb66 100644
--- a/chrome/browser/extensions/extension_prefs.h
+++ b/chrome/browser/extensions/extension_prefs.h
@@ -510,6 +510,11 @@ class ExtensionPrefs : public ExtensionScopedPrefs,
void SetGeometryCache(const std::string& extension_id,
scoped_ptr<base::DictionaryValue> cache);
+ // Used for verification of installed extension ids. For the Set method, pass
+ // null to remove the preference.
+ const base::DictionaryValue* GetInstallSignature();
+ void SetInstallSignature(const DictionaryValue* signature);
+
private:
friend class ExtensionPrefsBlacklistedExtensions; // Unit test.
friend class ExtensionPrefsUninstallExtension; // Unit test.
diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc
index 9fdefc0..2b39df1 100644
--- a/chrome/browser/extensions/extension_service.cc
+++ b/chrome/browser/extensions/extension_service.cc
@@ -52,6 +52,7 @@
#include "chrome/browser/extensions/external_install_ui.h"
#include "chrome/browser/extensions/external_provider_impl.h"
#include "chrome/browser/extensions/external_provider_interface.h"
+#include "chrome/browser/extensions/install_verifier.h"
#include "chrome/browser/extensions/installed_loader.h"
#include "chrome/browser/extensions/management_policy.h"
#include "chrome/browser/extensions/pending_extension_manager.h"
@@ -120,6 +121,7 @@ using extensions::Extension;
using extensions::ExtensionIdSet;
using extensions::ExtensionInfo;
using extensions::FeatureSwitch;
+using extensions::InstallVerifier;
using extensions::ManagementPolicy;
using extensions::Manifest;
using extensions::PermissionMessage;
@@ -774,6 +776,9 @@ bool ExtensionService::UninstallExtension(
extension.get(), is_ready());
}
+ extensions::ExtensionSystem::Get(profile_)->install_verifier()->Remove(
+ extension->id());
+
if (IsUnacknowledgedExternalExtension(extension.get())) {
UMA_HISTOGRAM_ENUMERATION("Extensions.ExternalExtensionEvent",
EXTERNAL_EXTENSION_UNINSTALLED,
@@ -2175,6 +2180,10 @@ void ExtensionService::AddNewOrUpdatedExtension(
blacklisted_for_malware,
page_ordinal);
delayed_installs_.Remove(extension->id());
+ if (extensions::ManifestURL::UpdatesFromGallery(extension)) {
+ extensions::ExtensionSystem::Get(profile_)->install_verifier()->Add(
+ extension->id(), InstallVerifier::AddResultCallback());
+ }
FinishInstallation(extension);
}
diff --git a/chrome/browser/extensions/extension_system.cc b/chrome/browser/extensions/extension_system.cc
index 007b1f4..bb18b07 100644
--- a/chrome/browser/extensions/extension_system.cc
+++ b/chrome/browser/extensions/extension_system.cc
@@ -25,6 +25,7 @@
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/extensions/extension_warning_badge_service.h"
#include "chrome/browser/extensions/extension_warning_set.h"
+#include "chrome/browser/extensions/install_verifier.h"
#include "chrome/browser/extensions/management_policy.h"
#include "chrome/browser/extensions/navigation_observer.h"
#include "chrome/browser/extensions/standard_management_policy_provider.h"
@@ -152,6 +153,8 @@ void ExtensionSystemImpl::Shared::RegisterManagementPolicyProviders() {
}
#endif // defined (OS_CHROMEOS)
+ management_policy_->RegisterProvider(install_verifier_.get());
+
#endif // defined(ENABLE_EXTENSIONS)
}
@@ -186,6 +189,10 @@ void ExtensionSystemImpl::Shared::Init(bool extensions_enabled) {
// These services must be registered before the ExtensionService tries to
// load any extensions.
{
+ install_verifier_.reset(new InstallVerifier(ExtensionPrefs::Get(profile_),
+ profile_->GetRequestContext()));
+ install_verifier_->Init();
+
management_policy_.reset(new ManagementPolicy);
RegisterManagementPolicyProviders();
}
@@ -304,6 +311,10 @@ ErrorConsole* ExtensionSystemImpl::Shared::error_console() {
return error_console_.get();
}
+InstallVerifier* ExtensionSystemImpl::Shared::install_verifier() {
+ return install_verifier_.get();
+}
+
//
// ExtensionSystemImpl
//
@@ -394,6 +405,10 @@ ErrorConsole* ExtensionSystemImpl::error_console() {
return shared_->error_console();
}
+InstallVerifier* ExtensionSystemImpl::install_verifier() {
+ return shared_->install_verifier();
+}
+
void ExtensionSystemImpl::RegisterExtensionWithRequestContexts(
const Extension* extension) {
base::Time install_time;
diff --git a/chrome/browser/extensions/extension_system.h b/chrome/browser/extensions/extension_system.h
index bf2522c..a96cfeb 100644
--- a/chrome/browser/extensions/extension_system.h
+++ b/chrome/browser/extensions/extension_system.h
@@ -35,6 +35,7 @@ class ExtensionSystemSharedFactory;
class ExtensionWarningBadgeService;
class ExtensionWarningService;
class InfoMap;
+class InstallVerifier;
class LazyBackgroundTaskQueue;
class ManagementPolicy;
class NavigationObserver;
@@ -111,6 +112,9 @@ class ExtensionSystem : public BrowserContextKeyedService {
// The ErrorConsole is created at startup.
virtual ErrorConsole* error_console() = 0;
+ // The InstallVerifier is created at startup.
+ virtual InstallVerifier* install_verifier() = 0;
+
// Called by the ExtensionService that lives in this system. Gives the
// info map a chance to react to the load event before the EXTENSION_LOADED
// notification has fired. The purpose for handling this event first is to
@@ -159,6 +163,7 @@ class ExtensionSystemImpl : public ExtensionSystem {
virtual ExtensionWarningService* warning_service() OVERRIDE;
virtual Blacklist* blacklist() OVERRIDE; // shared
virtual ErrorConsole* error_console() OVERRIDE;
+ virtual InstallVerifier* install_verifier() OVERRIDE;
virtual void RegisterExtensionWithRequestContexts(
const Extension* extension) OVERRIDE;
@@ -199,6 +204,7 @@ class ExtensionSystemImpl : public ExtensionSystem {
EventRouter* event_router();
ExtensionWarningService* warning_service();
ErrorConsole* error_console();
+ InstallVerifier* install_verifier();
const OneShotEvent& ready() const { return ready_; }
private:
@@ -226,6 +232,7 @@ class ExtensionSystemImpl : public ExtensionSystem {
scoped_ptr<ExtensionWarningService> extension_warning_service_;
scoped_ptr<ExtensionWarningBadgeService> extension_warning_badge_service_;
scoped_ptr<ErrorConsole> error_console_;
+ scoped_ptr<InstallVerifier> install_verifier_;
#if defined(OS_CHROMEOS)
scoped_ptr<chromeos::DeviceLocalAccountManagementPolicyProvider>
diff --git a/chrome/browser/extensions/install_signer.cc b/chrome/browser/extensions/install_signer.cc
new file mode 100644
index 0000000..a144b99
--- /dev/null
+++ b/chrome/browser/extensions/install_signer.cc
@@ -0,0 +1,292 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/install_signer.h"
+
+#include "base/base64.h"
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/values.h"
+#include "chrome/common/chrome_switches.h"
+#include "crypto/random.h"
+#include "crypto/secure_hash.h"
+#include "crypto/sha2.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "url/gurl.h"
+
+#if defined(ENABLE_RLZ)
+#include "rlz/lib/machine_id.h"
+#endif
+
+namespace {
+
+using extensions::ExtensionIdSet;
+
+const char kExpireDateKey[] = "expire_date";
+const char kIdsKey[] = "ids";
+const char kSaltKey[] = "salt";
+const char kSignatureKey[] = "signature";
+const size_t kSaltBytes = 32;
+
+// Returns a date 12 weeks from now in YYYY-MM-DD format.
+std::string GetExpiryString() {
+ base::Time later = base::Time::Now() + base::TimeDelta::FromDays(7*12);
+ base::Time::Exploded exploded;
+ later.LocalExplode(&exploded);
+ return base::StringPrintf("%d-%02d-%02d",
+ exploded.year,
+ exploded.month,
+ exploded.day_of_month);
+}
+
+void FakeSignData(const ExtensionIdSet& ids,
+ const std::string& hash_base64,
+ const std::string& expire_date,
+ std::string* signature,
+ ExtensionIdSet* invalid_ids) {
+ DCHECK(invalid_ids && invalid_ids->empty());
+ DCHECK(IsStringASCII(hash_base64));
+
+ ExtensionIdSet invalid =
+ extensions::InstallSigner::GetForcedNotFromWebstore();
+
+ std::string data_to_sign;
+ for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); ++i) {
+ if (ContainsKey(invalid, (*i)))
+ invalid_ids->insert(*i);
+ else
+ data_to_sign.append(*i);
+ }
+ data_to_sign.append(hash_base64);
+ data_to_sign.append(expire_date);
+
+ *signature = std::string("FakeSignature:") +
+ crypto::SHA256HashString(data_to_sign);
+}
+
+void FakeSignDataWithCallback(
+ const ExtensionIdSet& ids,
+ const std::string& hash_base64,
+ const base::Callback<void(const std::string&,
+ const std::string&,
+ const ExtensionIdSet&)>& callback) {
+ std::string signature;
+ std::string expire_date = GetExpiryString();
+ ExtensionIdSet invalid_ids;
+ FakeSignData(ids, hash_base64, expire_date, &signature, &invalid_ids);
+ callback.Run(signature, expire_date, invalid_ids);
+}
+
+bool FakeCheckSignature(const extensions::ExtensionIdSet& ids,
+ const std::string& hash_base64,
+ const std::string& signature,
+ const std::string& expire_date) {
+ std::string computed_signature;
+ extensions::ExtensionIdSet invalid_ids;
+ FakeSignData(ids, hash_base64, expire_date, &computed_signature,
+ &invalid_ids);
+ return invalid_ids.empty() && computed_signature == signature;
+}
+
+// Hashes |salt| with the machine id, base64-encodes it and returns it in
+// |result|.
+bool HashWithMachineId(const std::string& salt, std::string* result) {
+ std::string machine_id;
+#if defined(ENABLE_RLZ)
+ if (!rlz_lib::GetMachineId(&machine_id))
+ return false;
+#endif
+
+ scoped_ptr<crypto::SecureHash> hash(
+ crypto::SecureHash::Create(crypto::SecureHash::SHA256));
+
+ hash->Update(machine_id.data(), machine_id.size());
+ hash->Update(salt.data(), salt.size());
+
+ std::string result_bytes(crypto::kSHA256Length, 0);
+ hash->Finish(string_as_array(&result_bytes), result_bytes.size());
+
+ return base::Base64Encode(result_bytes, result);
+}
+
+} // namespace
+
+namespace extensions {
+
+InstallSignature::InstallSignature() {
+}
+InstallSignature::~InstallSignature() {
+}
+
+void InstallSignature::ToValue(base::DictionaryValue* value) const {
+ CHECK(value);
+ DCHECK(!signature.empty());
+ DCHECK(!salt.empty());
+
+ base::ListValue* id_list = new base::ListValue();
+ for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end();
+ ++i)
+ id_list->AppendString(*i);
+
+ value->Set(kIdsKey, id_list);
+ value->SetString(kExpireDateKey, expire_date);
+ std::string salt_base64;
+ std::string signature_base64;
+ base::Base64Encode(salt, &salt_base64);
+ base::Base64Encode(signature, &signature_base64);
+ value->SetString(kSaltKey, salt_base64);
+ value->SetString(kSignatureKey, signature_base64);
+}
+
+// static
+scoped_ptr<InstallSignature> InstallSignature::FromValue(
+ const base::DictionaryValue& value) {
+
+ scoped_ptr<InstallSignature> result(new InstallSignature);
+
+ std::string salt_base64;
+ std::string signature_base64;
+ if (!value.GetString(kExpireDateKey, &result->expire_date) ||
+ !value.GetString(kSaltKey, &salt_base64) ||
+ !value.GetString(kSignatureKey, &signature_base64) ||
+ !base::Base64Decode(salt_base64, &result->salt) ||
+ !base::Base64Decode(signature_base64, &result->signature)) {
+ result.reset();
+ return result.Pass();
+ }
+
+ const base::ListValue* ids = NULL;
+ if (!value.GetList(kIdsKey, &ids)) {
+ result.reset();
+ return result.Pass();
+ }
+
+ for (ListValue::const_iterator i = ids->begin(); i != ids->end(); ++i) {
+ std::string id;
+ if (!(*i)->GetAsString(&id)) {
+ result.reset();
+ return result.Pass();
+ }
+ result->ids.insert(id);
+ }
+
+ return result.Pass();
+}
+
+
+InstallSigner::InstallSigner(net::URLRequestContextGetter* context_getter,
+ const ExtensionIdSet& ids)
+ : ids_(ids), context_getter_(context_getter) {
+}
+
+InstallSigner::~InstallSigner() {
+}
+
+// static
+bool InstallSigner::VerifySignature(const InstallSignature& signature) {
+ if (signature.ids.empty())
+ return true;
+
+ std::string hash_base64;
+ if (!HashWithMachineId(signature.salt, &hash_base64))
+ return false;
+
+ return FakeCheckSignature(signature.ids, hash_base64, signature.signature,
+ signature.expire_date);
+}
+
+
+class InstallSigner::FetcherDelegate : public net::URLFetcherDelegate {
+ public:
+ explicit FetcherDelegate(const base::Closure& callback)
+ : callback_(callback) {
+ }
+
+ virtual ~FetcherDelegate() {
+ }
+
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
+ callback_.Run();
+ }
+
+ private:
+ base::Closure callback_;
+ DISALLOW_COPY_AND_ASSIGN(FetcherDelegate);
+};
+
+// static
+ExtensionIdSet InstallSigner::GetForcedNotFromWebstore() {
+ std::string value = CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kExtensionsNotWebstore);
+ if (value.empty())
+ return ExtensionIdSet();
+
+ std::vector<std::string> ids;
+ base::SplitString(value, ',', &ids);
+ return ExtensionIdSet(ids.begin(), ids.end());
+}
+
+void InstallSigner::GetSignature(const SignatureCallback& callback) {
+ CHECK(!url_fetcher_.get());
+ CHECK(callback_.is_null());
+ CHECK(salt_.empty());
+ callback_ = callback;
+
+ // If the set of ids is empty, just return an empty signature and skip the
+ // call to the server.
+ if (ids_.empty()) {
+ callback_.Run(scoped_ptr<InstallSignature>(new InstallSignature()));
+ return;
+ }
+
+ salt_ = std::string(kSaltBytes, 0);
+ DCHECK_EQ(kSaltBytes, salt_.size());
+ crypto::RandBytes(string_as_array(&salt_), salt_.size());
+
+ std::string hash_base64;
+ if (!HashWithMachineId(salt_, &hash_base64)) {
+ if (!callback_.is_null())
+ callback_.Run(scoped_ptr<InstallSignature>());
+ return;
+ }
+
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&FakeSignDataWithCallback,
+ ids_,
+ hash_base64,
+ base::Bind(&InstallSigner::HandleSignatureResult,
+ base::Unretained(this))));
+}
+
+void InstallSigner::HandleSignatureResult(const std::string& signature,
+ const std::string& expire_date,
+ const ExtensionIdSet& invalid_ids) {
+ ExtensionIdSet valid_ids =
+ base::STLSetDifference<ExtensionIdSet>(ids_, invalid_ids);
+
+ scoped_ptr<InstallSignature> result;
+ if (!signature.empty()) {
+ result.reset(new InstallSignature);
+ result->ids = valid_ids;
+ result->salt = salt_;
+ result->signature = signature;
+ result->expire_date = expire_date;
+ DCHECK(VerifySignature(*result));
+ }
+
+ if (!callback_.is_null())
+ callback_.Run(result.Pass());
+}
+
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/install_signer.h b/chrome/browser/extensions/install_signer.h
new file mode 100644
index 0000000..b110bef
--- /dev/null
+++ b/chrome/browser/extensions/install_signer.h
@@ -0,0 +1,109 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_INSTALL_SIGNER_H_
+#define CHROME_BROWSER_EXTENSIONS_INSTALL_SIGNER_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/common/extensions/extension.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace net {
+class URLFetcher;
+class URLRequestContextGetter;
+}
+
+namespace extensions {
+
+// This represents a list of ids signed with a private key using an algorithm
+// that includes some salt bytes.
+struct InstallSignature {
+ // The set of ids that have been signed.
+ ExtensionIdSet ids;
+
+ // Both of these are just arrays of bytes, NOT base64-encoded.
+ std::string salt;
+ std::string signature;
+
+ // The date that the signature should expire, in YYYY-MM-DD format.
+ std::string expire_date;
+
+ InstallSignature();
+ ~InstallSignature();
+
+ // Helper methods for serialization to/from a base::DictionaryValue.
+ void ToValue(base::DictionaryValue* value) const;
+
+ static scoped_ptr<InstallSignature> FromValue(
+ const base::DictionaryValue& value);
+};
+
+// Objects of this class encapsulate an operation to get a signature proving
+// that a set of ids are hosted in the webstore.
+class InstallSigner {
+ public:
+ typedef base::Callback<void(scoped_ptr<InstallSignature>)> SignatureCallback;
+
+ // IMPORTANT NOTE: It is possible that only some, but not all, of the entries
+ // in |ids| will be successfully signed by the backend. Callers should always
+ // check the set of ids in the InstallSignature passed to their callback, as
+ // it may contain only a subset of the ids they passed in.
+ InstallSigner(net::URLRequestContextGetter* context_getter,
+ const ExtensionIdSet& ids);
+ ~InstallSigner();
+
+ // Returns a set of ids that are forced to be considered not from webstore,
+ // e.g. by a command line flag used for testing.
+ static ExtensionIdSet GetForcedNotFromWebstore();
+
+ // Begins the process of fetching a signature from the backend. This should
+ // only be called once! If you want to get another signature, make another
+ // instance of this class.
+ void GetSignature(const SignatureCallback& callback);
+
+ // Returns whether the signature in InstallSignature is properly signed with a
+ // known public key.
+ static bool VerifySignature(const InstallSignature& signature);
+
+ private:
+ // A very simple delegate just used to call ourself back when a url fetch is
+ // complete.
+ class FetcherDelegate;
+
+ // Handles the result from a backend fetch.
+ void HandleSignatureResult(const std::string& signature,
+ const std::string& expire_date,
+ const ExtensionIdSet& invalid_ids);
+
+ // The final callback for when we're done.
+ SignatureCallback callback_;
+
+ // The current set of ids we're trying to verify. This may contain fewer ids
+ // than we started with.
+ ExtensionIdSet ids_;
+
+ // An array of random bytes used as an input to hash with the machine id,
+ // which will need to be persisted in the eventual InstallSignature we get.
+ std::string salt_;
+
+ // These are used to make the call to a backend server for a signature.
+ net::URLRequestContextGetter* context_getter_;
+ scoped_ptr<net::URLFetcher> url_fetcher_;
+ scoped_ptr<FetcherDelegate> delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(InstallSigner);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_INSTALL_SIGNER_H_
diff --git a/chrome/browser/extensions/install_verifier.cc b/chrome/browser/extensions/install_verifier.cc
new file mode 100644
index 0000000..4b7f347
--- /dev/null
+++ b/chrome/browser/extensions/install_verifier.cc
@@ -0,0 +1,353 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/extensions/install_verifier.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/metrics/histogram.h"
+#include "base/prefs/pref_service.h"
+#include "base/stl_util.h"
+#include "chrome/browser/extensions/extension_prefs.h"
+#include "chrome/browser/extensions/install_signer.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/manifest_url_handler.h"
+#include "chrome/common/pref_names.h"
+#include "extensions/common/manifest.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+enum VerifyStatus {
+ NONE, // Do not request install signatures, and do not enforce them.
+ BOOTSTRAP, // Request install signatures, but do not enforce them.
+ ENFORCE // Request install signatures, and enforce them.
+};
+
+VerifyStatus GetStatus() {
+ const CommandLine* cmdline = CommandLine::ForCurrentProcess();
+ if (!extensions::InstallSigner::GetForcedNotFromWebstore().empty())
+ return ENFORCE;
+
+ if (cmdline->HasSwitch(switches::kExtensionsInstallVerification)) {
+ std::string value = cmdline->GetSwitchValueASCII(
+ switches::kExtensionsInstallVerification);
+ if (value == "bootstrap")
+ return BOOTSTRAP;
+ else
+ return ENFORCE;
+ }
+
+ return NONE;
+}
+
+bool ShouldFetchSignature() {
+ VerifyStatus status = GetStatus();
+ return (status == BOOTSTRAP || status == ENFORCE);
+}
+
+bool ShouldEnforce() {
+ return GetStatus() == ENFORCE;
+}
+
+} // namespace
+
+namespace extensions {
+
+InstallVerifier::InstallVerifier(ExtensionPrefs* prefs,
+ net::URLRequestContextGetter* context_getter)
+ : prefs_(prefs), context_getter_(context_getter) {
+}
+
+InstallVerifier::~InstallVerifier() {}
+
+void InstallVerifier::Init() {
+ const DictionaryValue* pref = prefs_->GetInstallSignature();
+ if (pref) {
+ scoped_ptr<InstallSignature> signature_from_prefs =
+ InstallSignature::FromValue(*pref);
+ if (!signature_from_prefs.get()) {
+ UMA_HISTOGRAM_BOOLEAN("InstallVerifier.InitUnparseablePref", true);
+ } else if (!InstallSigner::VerifySignature(*signature_from_prefs.get())) {
+ UMA_HISTOGRAM_BOOLEAN("InstallVerifier.InitInvalidSignature", true);
+ DVLOG(1) << "Init - ignoring invalid signature";
+ } else {
+ signature_ = signature_from_prefs.Pass();
+ GarbageCollect();
+ }
+ }
+
+ if (!signature_.get() && ShouldFetchSignature()) {
+ // We didn't have any signature but are in fetch mode, so do a request for
+ // a signature if needed.
+ scoped_ptr<ExtensionPrefs::ExtensionsInfo> all_info =
+ prefs_->GetInstalledExtensionsInfo();
+ ExtensionIdSet to_add;
+ if (all_info.get()) {
+ for (ExtensionPrefs::ExtensionsInfo::const_iterator i = all_info->begin();
+ i != all_info->end(); ++i) {
+ const ExtensionInfo& info = **i;
+ const base::DictionaryValue* manifest = info.extension_manifest.get();
+ if (manifest && ManifestURL::UpdatesFromGallery(manifest))
+ to_add.insert(info.extension_id);
+ }
+ }
+ if (!to_add.empty())
+ AddMany(to_add, AddResultCallback());
+ }
+}
+
+void InstallVerifier::Add(const std::string& id,
+ const AddResultCallback& callback) {
+ ExtensionIdSet ids;
+ ids.insert(id);
+ AddMany(ids, callback);
+}
+
+void InstallVerifier::AddMany(const ExtensionIdSet& ids,
+ const AddResultCallback& callback) {
+ if (!ShouldFetchSignature())
+ return;
+
+ if (signature_.get()) {
+ ExtensionIdSet not_allowed_yet =
+ base::STLSetDifference<ExtensionIdSet>(ids, signature_->ids);
+ if (not_allowed_yet.empty()) {
+ if (!callback.is_null())
+ callback.Run(true);
+ return;
+ }
+ }
+
+ InstallVerifier::PendingOperation* operation =
+ new InstallVerifier::PendingOperation();
+ operation->type = InstallVerifier::ADD;
+ operation->ids.insert(ids.begin(), ids.end());
+ operation->callback = callback;
+
+ operation_queue_.push(linked_ptr<PendingOperation>(operation));
+
+ // If there are no ongoing pending requests, we need to kick one off.
+ if (operation_queue_.size() == 1)
+ BeginFetch();
+}
+
+void InstallVerifier::AddProvisional(const ExtensionIdSet& ids) {
+ provisional_.insert(ids.begin(), ids.end());
+ AddMany(ids, AddResultCallback());
+}
+
+void InstallVerifier::Remove(const std::string& id) {
+ ExtensionIdSet ids;
+ ids.insert(id);
+ RemoveMany(ids);
+}
+
+void InstallVerifier::RemoveMany(const ExtensionIdSet& ids) {
+ if (!signature_.get() || !ShouldFetchSignature())
+ return;
+
+ bool found_any = false;
+ for (ExtensionIdSet::const_iterator i = ids.begin(); i != ids.end(); ++i) {
+ if (ContainsKey(signature_->ids, *i)) {
+ found_any = true;
+ break;
+ }
+ }
+ if (!found_any)
+ return;
+
+ InstallVerifier::PendingOperation* operation =
+ new InstallVerifier::PendingOperation();
+ operation->type = InstallVerifier::REMOVE;
+ operation->ids = ids;
+
+ operation_queue_.push(linked_ptr<PendingOperation>(operation));
+ if (operation_queue_.size() == 1)
+ BeginFetch();
+}
+
+std::string InstallVerifier::GetDebugPolicyProviderName() const {
+ return std::string("InstallVerifier");
+}
+
+static bool FromStore(const Extension* extension) {
+ bool updates_from_store = ManifestURL::UpdatesFromGallery(extension);
+ return extension->from_webstore() || updates_from_store;
+}
+
+bool InstallVerifier::MustRemainDisabled(const Extension* extension,
+ Extension::DisableReason* reason,
+ string16* error) const {
+ if (!ShouldEnforce() || !extension->is_extension() ||
+ Manifest::IsUnpackedLocation(extension->location()) ||
+ AllowedByEnterprisePolicy(extension->id()))
+ return false;
+
+ bool verified =
+ FromStore(extension) &&
+ IsVerified(extension->id()) &&
+ !ContainsKey(InstallSigner::GetForcedNotFromWebstore(), extension->id());
+
+ if (!verified) {
+ if (reason)
+ *reason = Extension::DISABLE_NOT_VERIFIED;
+ if (error)
+ *error = l10n_util::GetStringFUTF16(
+ IDS_EXTENSIONS_ADDED_WITHOUT_KNOWLEDGE,
+ l10n_util::GetStringUTF16(IDS_EXTENSION_WEB_STORE_TITLE));
+ }
+ return !verified;
+}
+
+InstallVerifier::PendingOperation::PendingOperation() {
+ type = InstallVerifier::ADD;
+}
+
+InstallVerifier::PendingOperation::~PendingOperation() {
+}
+
+void InstallVerifier::GarbageCollect() {
+ if (!ShouldFetchSignature()) {
+ return;
+ }
+ CHECK(signature_.get());
+ ExtensionIdSet leftovers = signature_->ids;
+ ExtensionIdList all_ids;
+ prefs_->GetExtensions(&all_ids);
+ for (ExtensionIdList::const_iterator i = all_ids.begin();
+ i != all_ids.end(); ++i) {
+ ExtensionIdSet::iterator found = leftovers.find(*i);
+ if (found != leftovers.end())
+ leftovers.erase(found);
+ }
+ if (!leftovers.empty()) {
+ RemoveMany(leftovers);
+ }
+}
+
+bool InstallVerifier::AllowedByEnterprisePolicy(const std::string& id) const {
+ PrefService* pref_service = prefs_->pref_service();
+ if (pref_service->IsManagedPreference(prefs::kExtensionInstallAllowList)) {
+ const base::ListValue* whitelist =
+ pref_service->GetList(prefs::kExtensionInstallAllowList);
+ base::StringValue id_value(id);
+ if (whitelist && whitelist->Find(id_value) != whitelist->end())
+ return true;
+ }
+ if (pref_service->IsManagedPreference(prefs::kExtensionInstallForceList)) {
+ const base::DictionaryValue* forcelist =
+ pref_service->GetDictionary(prefs::kExtensionInstallForceList);
+ if (forcelist && forcelist->HasKey(id))
+ return true;
+ }
+ return false;
+}
+
+bool InstallVerifier::IsVerified(const std::string& id) const {
+ return ((signature_.get() && ContainsKey(signature_->ids, id)) ||
+ ContainsKey(provisional_, id));
+}
+
+void InstallVerifier::BeginFetch() {
+ DCHECK(ShouldFetchSignature());
+
+ // TODO(asargent) - It would be possible to coalesce all operations in the
+ // queue into one fetch - we'd probably just need to change the queue to
+ // hold (set of ids, list of callbacks) pairs.
+ CHECK(!operation_queue_.empty());
+ const PendingOperation& operation = *operation_queue_.front();
+
+ ExtensionIdSet ids_to_sign;
+ if (signature_.get()) {
+ ids_to_sign.insert(signature_->ids.begin(), signature_->ids.end());
+ }
+ if (operation.type == InstallVerifier::ADD) {
+ ids_to_sign.insert(operation.ids.begin(), operation.ids.end());
+ } else {
+ for (ExtensionIdSet::const_iterator i = operation.ids.begin();
+ i != operation.ids.end(); ++i) {
+ if (ContainsKey(ids_to_sign, *i))
+ ids_to_sign.erase(*i);
+ }
+ }
+
+ signer_.reset(new InstallSigner(context_getter_, ids_to_sign));
+ signer_->GetSignature(base::Bind(&InstallVerifier::SignatureCallback,
+ base::Unretained(this)));
+}
+
+void InstallVerifier::SaveToPrefs() {
+ if (signature_.get())
+ DCHECK(InstallSigner::VerifySignature(*signature_));
+
+ if (!signature_.get() || signature_->ids.empty()) {
+ DVLOG(1) << "SaveToPrefs - saving NULL";
+ prefs_->SetInstallSignature(NULL);
+ } else {
+ DictionaryValue pref;
+ signature_->ToValue(&pref);
+ if (VLOG_IS_ON(1)) {
+ DVLOG(1) << "SaveToPrefs - saving";
+
+ DCHECK(InstallSigner::VerifySignature(*signature_.get()));
+ scoped_ptr<InstallSignature> rehydrated =
+ InstallSignature::FromValue(pref);
+ DCHECK(InstallSigner::VerifySignature(*rehydrated.get()));
+ }
+ prefs_->SetInstallSignature(&pref);
+ }
+}
+
+void InstallVerifier::SignatureCallback(
+ scoped_ptr<InstallSignature> signature) {
+
+ linked_ptr<PendingOperation> operation = operation_queue_.front();
+ operation_queue_.pop();
+
+ bool success = false;
+ if (!signature.get()) {
+ UMA_HISTOGRAM_BOOLEAN("InstallVerifier.CallbackNoSignature", true);
+ } else if (!InstallSigner::VerifySignature(*signature)) {
+ UMA_HISTOGRAM_BOOLEAN("InstallVerifier.CallbackInvalidSignature", true);
+ } else {
+ UMA_HISTOGRAM_BOOLEAN("InstallVerifier.CallbackValidSignature", true);
+ success = true;
+ }
+
+ if (!success) {
+ if (!operation->callback.is_null())
+ operation->callback.Run(false);
+
+ // TODO(asargent) - if this was something like a network error, we need to
+ // do retries with exponential back off.
+ } else {
+ signature_ = signature.Pass();
+ SaveToPrefs();
+
+ if (!provisional_.empty()) {
+ // Update |provisional_| to remove ids that were successfully signed.
+ provisional_ = base::STLSetDifference<ExtensionIdSet>(
+ provisional_, signature_->ids);
+ }
+
+ // See if we were able to sign all of |ids|.
+ ExtensionIdSet not_allowed =
+ base::STLSetDifference<ExtensionIdSet>(operation->ids,
+ signature_->ids);
+
+ UMA_HISTOGRAM_COUNTS_100("InstallVerifier.CallbackNotAllowed",
+ not_allowed.size());
+
+ if (!operation->callback.is_null())
+ operation->callback.Run(not_allowed.empty());
+ }
+
+ if (!operation_queue_.empty())
+ BeginFetch();
+}
+
+
+} // namespace extensions
diff --git a/chrome/browser/extensions/install_verifier.h b/chrome/browser/extensions/install_verifier.h
new file mode 100644
index 0000000..eb5d17c
--- /dev/null
+++ b/chrome/browser/extensions/install_verifier.h
@@ -0,0 +1,136 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_EXTENSIONS_INSTALL_VERIFIER_H_
+#define CHROME_BROWSER_EXTENSIONS_INSTALL_VERIFIER_H_
+
+#include <queue>
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/callback.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/extensions/management_policy.h"
+#include "chrome/common/extensions/extension.h"
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace extensions {
+
+class ExtensionPrefs;
+class InstallSigner;
+struct InstallSignature;
+
+// This class implements verification that a set of extensions are either from
+// the webstore or are whitelisted by enterprise policy. The webstore
+// verification process works by sending a request to a backend server to get a
+// signature proving that a set of extensions are verified. This signature is
+// written into the extension preferences and is checked for validity when
+// being read back again.
+//
+// This class should be kept notified of runtime changes to the set of
+// extensions installed from the webstore.
+class InstallVerifier : public ManagementPolicy::Provider {
+ public:
+ InstallVerifier(ExtensionPrefs* prefs,
+ net::URLRequestContextGetter* context_getter);
+ virtual ~InstallVerifier();
+
+ // Initializes this object for use, including reading preferences and
+ // validating the stored signature.
+ void Init();
+
+ // A callback for indicating success/failure of adding new ids.
+ typedef base::Callback<void(bool)> AddResultCallback;
+
+ // Try adding a new |id| (or set of ids) to the list of verified ids. When
+ // this process is finished |callback| will be run with success/failure. In
+ // case of success, subsequent calls to IsVerified will begin returning true
+ // for |id|.
+ void Add(const std::string& id, const AddResultCallback& callback);
+ void AddMany(const ExtensionIdSet& ids,
+ const AddResultCallback& callback);
+
+ // Call this to add a set of ids that will immediately be considered allowed,
+ // and kick off an aysnchronous request to Add.
+ void AddProvisional(const ExtensionIdSet& ids);
+
+ // Removes an id or set of ids from the verified list.
+ void Remove(const std::string& id);
+ void RemoveMany(const ExtensionIdSet& ids);
+
+ // ManagementPolicy::Provider interface.
+ virtual std::string GetDebugPolicyProviderName() const OVERRIDE;
+ virtual bool MustRemainDisabled(const Extension* extension,
+ Extension::DisableReason* reason,
+ string16* error) const OVERRIDE;
+
+ private:
+ // We keep a list of operations to the current set of extensions - either
+ // additions or removals.
+ enum OperationType {
+ ADD,
+ REMOVE
+ };
+
+ // This is an operation we want to apply to the current set of verified ids.
+ struct PendingOperation {
+ OperationType type;
+
+ // This is the set of ids being either added or removed.
+ ExtensionIdSet ids;
+
+ AddResultCallback callback;
+
+ explicit PendingOperation();
+ ~PendingOperation();
+ };
+
+ // Removes any no-longer-installed ids, requesting a new signature if needed.
+ void GarbageCollect();
+
+ // Returns whether an extension id is allowed by policy.
+ bool AllowedByEnterprisePolicy(const std::string& id) const;
+
+ // Returns whether the given |id| is included in our verified signature.
+ bool IsVerified(const std::string& id) const;
+
+ // Begins the process of fetching a new signature, based on applying the
+ // operation at the head of the queue to the current set of ids in
+ // |signature_| (if any) and then sending a request to sign that.
+ void BeginFetch();
+
+ // Saves the current value of |signature_| to the prefs;
+ void SaveToPrefs();
+
+ // Called with the result of a signature request, or NULL on failure.
+ void SignatureCallback(scoped_ptr<InstallSignature> signature);
+
+ ExtensionPrefs* prefs_;
+ net::URLRequestContextGetter* context_getter_;
+
+ // This is the most up-to-date signature, read out of |prefs_| during
+ // initialization and updated anytime we get new id's added.
+ scoped_ptr<InstallSignature> signature_;
+
+ // The current InstallSigner, if we have a signature request running.
+ scoped_ptr<InstallSigner> signer_;
+
+ // A queue of operations to apply to the current set of allowed ids.
+ std::queue<linked_ptr<PendingOperation> > operation_queue_;
+
+ // A set of ids that have been provisionally added, which we're willing to
+ // consider allowed until we hear back from the server signature request.
+ ExtensionIdSet provisional_;
+
+ DISALLOW_COPY_AND_ASSIGN(InstallVerifier);
+};
+
+} // namespace extensions
+
+#endif // CHROME_BROWSER_EXTENSIONS_INSTALL_VERIFIER_H_
diff --git a/chrome/browser/extensions/test_extension_system.cc b/chrome/browser/extensions/test_extension_system.cc
index 3a9cad7..33838b8 100644
--- a/chrome/browser/extensions/test_extension_system.cc
+++ b/chrome/browser/extensions/test_extension_system.cc
@@ -15,6 +15,7 @@
#include "chrome/browser/extensions/extension_prefs_factory.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
+#include "chrome/browser/extensions/install_verifier.h"
#include "chrome/browser/extensions/management_policy.h"
#include "chrome/browser/extensions/standard_management_policy_provider.h"
#include "chrome/browser/extensions/state_store.h"
@@ -79,6 +80,8 @@ ExtensionService* TestExtensionSystem::CreateExtensionService(
bool autoupdate_enabled) {
if (!ExtensionPrefs::Get(profile_))
CreateExtensionPrefs(command_line, install_directory);
+ install_verifier_.reset(new InstallVerifier(ExtensionPrefs::Get(profile_),
+ NULL));
// The ownership of |value_store_| is immediately transferred to state_store_,
// but we keep a naked pointer to the TestingValueStore.
scoped_ptr<TestingValueStore> value_store(new TestingValueStore());
@@ -158,6 +161,10 @@ ErrorConsole* TestExtensionSystem::error_console() {
return error_console_.get();
}
+InstallVerifier* TestExtensionSystem::install_verifier() {
+ return install_verifier_.get();
+}
+
// static
BrowserContextKeyedService* TestExtensionSystem::Build(
content::BrowserContext* profile) {
diff --git a/chrome/browser/extensions/test_extension_system.h b/chrome/browser/extensions/test_extension_system.h
index 4bf61e8..4f67311 100644
--- a/chrome/browser/extensions/test_extension_system.h
+++ b/chrome/browser/extensions/test_extension_system.h
@@ -70,6 +70,7 @@ class TestExtensionSystem : public ExtensionSystem {
virtual Blacklist* blacklist() OVERRIDE;
virtual const OneShotEvent& ready() const OVERRIDE;
virtual ErrorConsole* error_console() OVERRIDE;
+ virtual InstallVerifier* install_verifier() OVERRIDE;
void SetReady() {
LOG(INFO) << "SetReady()";
@@ -94,6 +95,7 @@ class TestExtensionSystem : public ExtensionSystem {
scoped_ptr<ProcessManager> process_manager_;
scoped_refptr<InfoMap> info_map_;
scoped_ptr<ErrorConsole> error_console_;
+ scoped_ptr<InstallVerifier> install_verifier_;
OneShotEvent ready_;
};
diff --git a/chrome/browser/extensions/webstore_installer.cc b/chrome/browser/extensions/webstore_installer.cc
index de53ca9..d4c9c2f 100644
--- a/chrome/browser/extensions/webstore_installer.cc
+++ b/chrome/browser/extensions/webstore_installer.cc
@@ -24,6 +24,7 @@
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/extensions/install_tracker.h"
#include "chrome/browser/extensions/install_tracker_factory.h"
+#include "chrome/browser/extensions/install_verifier.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -283,6 +284,13 @@ void WebstoreInstaller::Start() {
total_modules_ = pending_modules_.size();
+ std::set<std::string> ids;
+ std::list<SharedModuleInfo::ImportInfo>::const_iterator i;
+ for (i = pending_modules_.begin(); i != pending_modules_.end(); ++i) {
+ ids.insert(i->extension_id);
+ }
+ ExtensionSystem::Get(profile_)->install_verifier()->AddProvisional(ids);
+
// TODO(crbug.com/305343): Query manifest of dependencises before
// downloading & installing those dependencies.
DownloadNextPendingModule();
diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi
index c701630..cdf3be2 100644
--- a/chrome/chrome_browser_extensions.gypi
+++ b/chrome/chrome_browser_extensions.gypi
@@ -806,10 +806,14 @@
'browser/extensions/installed_loader.cc',
'browser/extensions/installed_loader.h',
'browser/extensions/install_observer.h',
+ 'browser/extensions/install_signer.cc',
+ 'browser/extensions/install_signer.h',
'browser/extensions/install_tracker.cc',
'browser/extensions/install_tracker.h',
'browser/extensions/install_tracker_factory.cc',
'browser/extensions/install_tracker_factory.h',
+ 'browser/extensions/install_verifier.cc',
+ 'browser/extensions/install_verifier.h',
'browser/extensions/location_bar_controller.h',
'browser/extensions/management_policy.cc',
'browser/extensions/management_policy.h',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 4a8a9c1..c05b8e7 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -812,6 +812,14 @@ const char kExplicitlyAllowedPorts[] = "explicitly-allowed-ports";
// Marks a renderer as extension process.
const char kExtensionProcess[] = "extension-process";
+// Turns on extension install verification if it would not otherwise have been
+// turned on.
+const char kExtensionsInstallVerification[] = "extensions-install-verification";
+
+// Specifies a comma-separated list of extension ids that should be forced to
+// be treated as not from the webstore when doing install verification.
+const char kExtensionsNotWebstore[] = "extensions-not-webstore";
+
// Frequency in seconds for Extensions auto-update.
const char kExtensionsUpdateFrequency[] = "extensions-update-frequency";
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 30e708e..c0f0ba3 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -229,6 +229,8 @@ extern const char kEnableWatchdog[];
extern const char kEnableWebSocketOverSpdy[];
extern const char kExplicitlyAllowedPorts[];
extern const char kExtensionProcess[];
+extern const char kExtensionsInstallVerification[];
+extern const char kExtensionsNotWebstore[];
extern const char kExtensionsUpdateFrequency[];
extern const char kExtraSearchQueryParams[];
extern const char kFakeVariationsChannel[];
diff --git a/chrome/common/extensions/manifest_url_handler.cc b/chrome/common/extensions/manifest_url_handler.cc
index 75f3690..52229bd 100644
--- a/chrome/common/extensions/manifest_url_handler.cc
+++ b/chrome/common/extensions/manifest_url_handler.cc
@@ -72,6 +72,14 @@ bool ManifestURL::UpdatesFromGallery(const Extension* extension) {
}
// static
+bool ManifestURL::UpdatesFromGallery(const base::DictionaryValue* manifest) {
+ std::string url;
+ if (!manifest->GetString(keys::kUpdateURL, &url))
+ return false;
+ return extension_urls::IsWebstoreUpdateUrl(GURL(url));
+}
+
+// static
const GURL& ManifestURL::GetOptionsPage(const Extension* extension) {
return GetManifestURL(extension, keys::kOptionsPage);
}
diff --git a/chrome/common/extensions/manifest_url_handler.h b/chrome/common/extensions/manifest_url_handler.h
index d11146b..34934dd 100644
--- a/chrome/common/extensions/manifest_url_handler.h
+++ b/chrome/common/extensions/manifest_url_handler.h
@@ -10,6 +10,10 @@
#include "chrome/common/extensions/extension.h"
#include "extensions/common/manifest_handler.h"
+namespace base {
+class DictionaryValue;
+}
+
namespace extensions {
// A structure to hold various URLs like devtools_page, homepage_url, etc
@@ -31,6 +35,7 @@ struct ManifestURL : public Extension::ManifestData {
// Returns true if this extension's update URL is the extension gallery.
static bool UpdatesFromGallery(const Extension* extension);
+ static bool UpdatesFromGallery(const base::DictionaryValue* manifest);
// Returns the Options Page for this extension.
static const GURL& GetOptionsPage(const Extension* extension);