blob: b9d5a0f8e71f35cdee537cc48bf6fb32a556a940 [file] [log] [blame]
[email protected]f4091a32012-06-05 22:21:571// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]8e4560b62011-01-14 10:09:142// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]5df038b2012-07-16 19:03:275#include "chrome/browser/extensions/external_registry_loader_win.h"
[email protected]8e4560b62011-01-14 10:09:146
Jinho Bangb5216cec2018-01-17 19:43:117#include <memory>
8
[email protected]8e6ac4b2011-10-17 19:04:319#include "base/bind.h"
[email protected]57999812013-02-24 05:40:5210#include "base/files/file_path.h"
thestig18dfb7a52014-08-26 10:44:0411#include "base/files/file_util.h"
[email protected]0910bae2014-06-10 17:53:2112#include "base/files/scoped_file.h"
asvitkineaa060312016-09-01 22:44:1313#include "base/metrics/histogram_macros.h"
Gabriel Charette14520232018-04-30 23:27:2214#include "base/sequenced_task_runner.h"
[email protected]46acbf12013-06-10 18:43:4215#include "base/strings/string_util.h"
[email protected]2b6a5802014-08-16 07:58:0816#include "base/strings/stringprintf.h"
[email protected]112158af2013-06-07 23:46:1817#include "base/strings/utf_string_conversions.h"
Istiaque Ahmed35e30fd2017-08-10 17:41:3018#include "base/task_scheduler/post_task.h"
19#include "base/threading/thread_restrictions.h"
[email protected]41a17c52013-06-28 00:27:5320#include "base/time/time.h"
[email protected]8e4560b62011-01-14 10:09:1421#include "base/values.h"
22#include "base/version.h"
23#include "base/win/registry.h"
[email protected]5df038b2012-07-16 19:03:2724#include "chrome/browser/extensions/external_provider_impl.h"
[email protected]fdd28372014-08-21 02:27:2625#include "components/crx_file/id_util.h"
[email protected]c38831a12011-10-28 12:44:4926#include "content/public/browser/browser_thread.h"
[email protected]8e4560b62011-01-14 10:09:1427
[email protected]631bb742011-11-02 11:29:3928using content::BrowserThread;
29
[email protected]8e4560b62011-01-14 10:09:1430namespace {
31
[email protected]8e4560b62011-01-14 10:09:1432// The Registry subkey that contains information about external extensions.
thestig11bf74d2014-11-24 20:14:4233const base::char16 kRegistryExtensions[] =
34 L"Software\\Google\\Chrome\\Extensions";
[email protected]8e4560b62011-01-14 10:09:1435
[email protected]d8fd0fd2014-03-24 13:16:0636// Registry value of the key that defines the installation parameter.
thestig11bf74d2014-11-24 20:14:4237const base::char16 kRegistryExtensionInstallParam[] = L"install_parameter";
[email protected]d8fd0fd2014-03-24 13:16:0638
[email protected]5873a5a2013-12-16 19:53:0039// Registry value of the key that defines the path to the .crx file.
thestig11bf74d2014-11-24 20:14:4240const base::char16 kRegistryExtensionPath[] = L"path";
[email protected]8e4560b62011-01-14 10:09:1441
42// Registry value of that key that defines the current version of the .crx file.
thestig11bf74d2014-11-24 20:14:4243const base::char16 kRegistryExtensionVersion[] = L"version";
[email protected]8e4560b62011-01-14 10:09:1444
[email protected]5873a5a2013-12-16 19:53:0045// Registry value of the key that defines an external update URL.
thestig11bf74d2014-11-24 20:14:4246const base::char16 kRegistryExtensionUpdateUrl[] = L"update_url";
[email protected]5873a5a2013-12-16 19:53:0047
[email protected]650b2d52013-02-10 03:41:4548bool CanOpenFileForReading(const base::FilePath& path) {
Istiaque Ahmed35e30fd2017-08-10 17:41:3049 // Note: Because this ScopedFILE is used on the stack and not passed around
50 // threads/sequences, this method doesn't require callers to run on tasks with
51 // BLOCK_SHUTDOWN. SKIP_ON_SHUTDOWN is enough and safe because it guarantees
52 // that if a task starts, it will always finish, and will block shutdown at
53 // that point.
[email protected]0910bae2014-06-10 17:53:2154 base::ScopedFILE file_handle(base::OpenFile(path, "rb"));
[email protected]b967a1b2011-08-17 14:39:5855 return file_handle.get() != NULL;
56}
57
[email protected]2b6a5802014-08-16 07:58:0858std::string MakePrefName(const std::string& extension_id,
59 const std::string& pref_name) {
60 return base::StringPrintf("%s.%s", extension_id.c_str(), pref_name.c_str());
61}
62
[email protected]8e4560b62011-01-14 10:09:1463} // namespace
64
[email protected]5df038b2012-07-16 19:03:2765namespace extensions {
66
lazyboy36920272017-04-04 23:51:3267ExternalRegistryLoader::ExternalRegistryLoader()
68 : attempted_watching_registry_(false) {}
69
Istiaque Ahmed35e30fd2017-08-10 17:41:3070ExternalRegistryLoader::~ExternalRegistryLoader() {}
71
[email protected]5df038b2012-07-16 19:03:2772void ExternalRegistryLoader::StartLoading() {
Istiaque Ahmedf6e72622017-09-08 23:14:1773 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Istiaque Ahmed35e30fd2017-08-10 17:41:3074 GetOrCreateTaskRunner()->PostTask(
75 FROM_HERE,
76 base::BindOnce(&ExternalRegistryLoader::LoadOnBlockingThread, this));
[email protected]8e4560b62011-01-14 10:09:1477}
78
dchengc963c7142016-04-08 03:55:2279std::unique_ptr<base::DictionaryValue>
Istiaque Ahmed35e30fd2017-08-10 17:41:3080ExternalRegistryLoader::LoadPrefsOnBlockingThread() {
Francois Doray66bdfd82017-10-20 13:50:3781 base::AssertBlockingAllowed();
Jinho Bangb5216cec2018-01-17 19:43:1182 auto prefs = std::make_unique<base::DictionaryValue>();
[email protected]8e4560b62011-01-14 10:09:1483
[email protected]1358b282011-09-15 15:35:1484 // A map of IDs, to weed out duplicates between HKCU and HKLM.
[email protected]d2065e062013-12-12 23:49:5285 std::set<base::string16> keys;
[email protected]1358b282011-09-15 15:35:1486 base::win::RegistryKeyIterator iterator_machine_key(
wfh9353beb92014-10-09 04:31:3887 HKEY_LOCAL_MACHINE,
thestig11bf74d2014-11-24 20:14:4288 kRegistryExtensions,
wfh9353beb92014-10-09 04:31:3889 KEY_WOW64_32KEY);
[email protected]1358b282011-09-15 15:35:1490 for (; iterator_machine_key.Valid(); ++iterator_machine_key)
91 keys.insert(iterator_machine_key.Name());
92 base::win::RegistryKeyIterator iterator_user_key(
thestig11bf74d2014-11-24 20:14:4293 HKEY_CURRENT_USER, kRegistryExtensions);
[email protected]1358b282011-09-15 15:35:1494 for (; iterator_user_key.Valid(); ++iterator_user_key)
95 keys.insert(iterator_user_key.Name());
96
97 // Iterate over the keys found, first trying HKLM, then HKCU, as per Windows
98 // policy conventions. We only fall back to HKCU if the HKLM key cannot be
99 // opened, not if the data within the key is invalid, for example.
[email protected]d2065e062013-12-12 23:49:52100 for (std::set<base::string16>::const_iterator it = keys.begin();
[email protected]1358b282011-09-15 15:35:14101 it != keys.end(); ++it) {
[email protected]8e4560b62011-01-14 10:09:14102 base::win::RegKey key;
thestig11bf74d2014-11-24 20:14:42103 base::string16 key_path = kRegistryExtensions;
[email protected]8e4560b62011-01-14 10:09:14104 key_path.append(L"\\");
[email protected]1358b282011-09-15 15:35:14105 key_path.append(*it);
106 if (key.Open(HKEY_LOCAL_MACHINE,
wfh9353beb92014-10-09 04:31:38107 key_path.c_str(),
108 KEY_READ | KEY_WOW64_32KEY) != ERROR_SUCCESS &&
109 key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ) !=
110 ERROR_SUCCESS) {
111 LOG(ERROR) << "Unable to read registry key at path (HKLM & HKCU): "
112 << key_path << ".";
113 continue;
[email protected]8e4560b62011-01-14 10:09:14114 }
[email protected]b967a1b2011-08-17 14:39:58115
brettwfce8d192015-08-10 19:07:51116 std::string id = base::ToLowerASCII(base::UTF16ToASCII(*it));
[email protected]fdd28372014-08-21 02:27:26117 if (!crx_file::id_util::IdIsValid(id)) {
[email protected]5873a5a2013-12-16 19:53:00118 LOG(ERROR) << "Invalid id value " << id
119 << " for key " << key_path << ".";
120 continue;
121 }
122
[email protected]d8fd0fd2014-03-24 13:16:06123 base::string16 extension_dist_id;
124 if (key.ReadValue(kRegistryExtensionInstallParam, &extension_dist_id) ==
125 ERROR_SUCCESS) {
[email protected]2b6a5802014-08-16 07:58:08126 prefs->SetString(MakePrefName(id, ExternalProviderImpl::kInstallParam),
[email protected]d8fd0fd2014-03-24 13:16:06127 base::UTF16ToASCII(extension_dist_id));
128 }
129
[email protected]5873a5a2013-12-16 19:53:00130 // If there is an update URL present, copy it to prefs and ignore
131 // path and version keys for this entry.
132 base::string16 extension_update_url;
133 if (key.ReadValue(kRegistryExtensionUpdateUrl, &extension_update_url)
134 == ERROR_SUCCESS) {
135 prefs->SetString(
[email protected]2b6a5802014-08-16 07:58:08136 MakePrefName(id, ExternalProviderImpl::kExternalUpdateUrl),
[email protected]74f778e2014-03-14 21:11:46137 base::UTF16ToASCII(extension_update_url));
[email protected]5873a5a2013-12-16 19:53:00138 continue;
139 }
140
[email protected]439f1e32013-12-09 20:09:09141 base::string16 extension_path_str;
[email protected]b967a1b2011-08-17 14:39:58142 if (key.ReadValue(kRegistryExtensionPath, &extension_path_str)
143 != ERROR_SUCCESS) {
144 // TODO(erikkay): find a way to get this into about:extensions
145 LOG(ERROR) << "Missing value " << kRegistryExtensionPath
146 << " for key " << key_path << ".";
147 continue;
148 }
149
[email protected]650b2d52013-02-10 03:41:45150 base::FilePath extension_path(extension_path_str);
[email protected]b967a1b2011-08-17 14:39:58151 if (!extension_path.IsAbsolute()) {
152 LOG(ERROR) << "File path " << extension_path_str
153 << " needs to be absolute in key "
154 << key_path;
155 continue;
156 }
157
[email protected]7567484142013-07-11 17:36:07158 if (!base::PathExists(extension_path)) {
[email protected]b967a1b2011-08-17 14:39:58159 LOG(ERROR) << "File " << extension_path_str
160 << " for key " << key_path
161 << " does not exist or is not readable.";
162 continue;
163 }
164
165 if (!CanOpenFileForReading(extension_path)) {
166 LOG(ERROR) << "File " << extension_path_str
167 << " for key " << key_path << " can not be read. "
168 << "Check that users who should have the extension "
169 << "installed have permission to read it.";
170 continue;
171 }
172
[email protected]439f1e32013-12-09 20:09:09173 base::string16 extension_version;
[email protected]b967a1b2011-08-17 14:39:58174 if (key.ReadValue(kRegistryExtensionVersion, &extension_version)
175 != ERROR_SUCCESS) {
176 // TODO(erikkay): find a way to get this into about:extensions
177 LOG(ERROR) << "Missing value " << kRegistryExtensionVersion
178 << " for key " << key_path << ".";
179 continue;
180 }
181
pwnallcbd73192016-08-22 18:59:17182 base::Version version(base::UTF16ToASCII(extension_version));
[email protected]12126d372012-07-11 18:40:53183 if (!version.IsValid()) {
[email protected]b967a1b2011-08-17 14:39:58184 LOG(ERROR) << "Invalid version value " << extension_version
185 << " for key " << key_path << ".";
186 continue;
187 }
188
189 prefs->SetString(
[email protected]2b6a5802014-08-16 07:58:08190 MakePrefName(id, ExternalProviderImpl::kExternalVersion),
[email protected]74f778e2014-03-14 21:11:46191 base::UTF16ToASCII(extension_version));
[email protected]b967a1b2011-08-17 14:39:58192 prefs->SetString(
[email protected]2b6a5802014-08-16 07:58:08193 MakePrefName(id, ExternalProviderImpl::kExternalCrx),
[email protected]b967a1b2011-08-17 14:39:58194 extension_path_str);
[email protected]2b6a5802014-08-16 07:58:08195 prefs->SetBoolean(
196 MakePrefName(id, ExternalProviderImpl::kMayBeUntrusted),
197 true);
[email protected]8e4560b62011-01-14 10:09:14198 }
199
lazyboye8634172016-01-28 00:10:48200 return prefs;
201}
202
Istiaque Ahmed35e30fd2017-08-10 17:41:30203void ExternalRegistryLoader::LoadOnBlockingThread() {
204 DCHECK(task_runner_);
205 DCHECK(task_runner_->RunsTasksInCurrentSequence());
lazyboye8634172016-01-28 00:10:48206 base::TimeTicks start_time = base::TimeTicks::Now();
Istiaque Ahmed35e30fd2017-08-10 17:41:30207 std::unique_ptr<base::DictionaryValue> prefs = LoadPrefsOnBlockingThread();
asvitkinec0fb8022014-08-26 04:39:35208 LOCAL_HISTOGRAM_TIMES("Extensions.ExternalRegistryLoaderWin",
209 base::TimeTicks::Now() - start_time);
[email protected]8e4560b62011-01-14 10:09:14210 BrowserThread::PostTask(
211 BrowserThread::UI, FROM_HERE,
lazyboye8634172016-01-28 00:10:48212 base::Bind(&ExternalRegistryLoader::CompleteLoadAndStartWatchingRegistry,
lazyboy57972e02017-04-12 22:40:59213 this, base::Passed(&prefs)));
lazyboye8634172016-01-28 00:10:48214}
215
lazyboy57972e02017-04-12 22:40:59216void ExternalRegistryLoader::CompleteLoadAndStartWatchingRegistry(
217 std::unique_ptr<base::DictionaryValue> prefs) {
Istiaque Ahmed35e30fd2017-08-10 17:41:30218 DCHECK_CURRENTLY_ON(BrowserThread::UI);
lazyboy57972e02017-04-12 22:40:59219 DCHECK(prefs);
Istiaque Ahmedef99c8ea2017-09-06 21:19:57220 LoadFinished(std::move(prefs));
lazyboye8634172016-01-28 00:10:48221
lazyboy36920272017-04-04 23:51:32222 // Attempt to watch registry if we haven't already.
223 if (attempted_watching_registry_)
224 return;
225
wez2884d9b2017-02-15 01:19:26226 LONG result = ERROR_SUCCESS;
227 if ((result = hklm_key_.Create(HKEY_LOCAL_MACHINE, kRegistryExtensions,
228 KEY_NOTIFY | KEY_WOW64_32KEY)) ==
229 ERROR_SUCCESS) {
lazyboye8634172016-01-28 00:10:48230 base::win::RegKey::ChangeCallback callback =
231 base::Bind(&ExternalRegistryLoader::OnRegistryKeyChanged,
232 base::Unretained(this), base::Unretained(&hklm_key_));
233 hklm_key_.StartWatching(callback);
234 } else {
wez2884d9b2017-02-15 01:19:26235 LOG(WARNING) << "Error observing HKLM: " << result;
lazyboye8634172016-01-28 00:10:48236 }
237
wez2884d9b2017-02-15 01:19:26238 if ((result = hkcu_key_.Create(HKEY_CURRENT_USER, kRegistryExtensions,
239 KEY_NOTIFY)) == ERROR_SUCCESS) {
lazyboye8634172016-01-28 00:10:48240 base::win::RegKey::ChangeCallback callback =
241 base::Bind(&ExternalRegistryLoader::OnRegistryKeyChanged,
242 base::Unretained(this), base::Unretained(&hkcu_key_));
243 hkcu_key_.StartWatching(callback);
244 } else {
wez2884d9b2017-02-15 01:19:26245 LOG(WARNING) << "Error observing HKCU: " << result;
lazyboye8634172016-01-28 00:10:48246 }
lazyboy36920272017-04-04 23:51:32247
248 attempted_watching_registry_ = true;
lazyboye8634172016-01-28 00:10:48249}
250
251void ExternalRegistryLoader::OnRegistryKeyChanged(base::win::RegKey* key) {
252 // |OnRegistryKeyChanged| is removed as an observer when the ChangeCallback is
253 // called, so we need to re-register.
254 key->StartWatching(base::Bind(&ExternalRegistryLoader::OnRegistryKeyChanged,
255 base::Unretained(this), base::Unretained(key)));
256
Istiaque Ahmed35e30fd2017-08-10 17:41:30257 GetOrCreateTaskRunner()->PostTask(
258 FROM_HERE,
259 base::BindOnce(&ExternalRegistryLoader::UpatePrefsOnBlockingThread,
260 this));
lazyboye8634172016-01-28 00:10:48261}
262
Istiaque Ahmed35e30fd2017-08-10 17:41:30263scoped_refptr<base::SequencedTaskRunner>
264ExternalRegistryLoader::GetOrCreateTaskRunner() {
265 if (!task_runner_.get()) {
266 task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
267 {// Requires I/O for registry.
268 base::MayBlock(),
269
270 // Inherit priority.
271
272 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
273 }
274 return task_runner_;
275}
276
277void ExternalRegistryLoader::UpatePrefsOnBlockingThread() {
278 DCHECK(task_runner_);
279 DCHECK(task_runner_->RunsTasksInCurrentSequence());
lazyboye8634172016-01-28 00:10:48280 base::TimeTicks start_time = base::TimeTicks::Now();
Istiaque Ahmed35e30fd2017-08-10 17:41:30281 std::unique_ptr<base::DictionaryValue> prefs = LoadPrefsOnBlockingThread();
lazyboye8634172016-01-28 00:10:48282 LOCAL_HISTOGRAM_TIMES("Extensions.ExternalRegistryLoaderWinUpdate",
283 base::TimeTicks::Now() - start_time);
284 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
285 base::Bind(&ExternalRegistryLoader::OnUpdated, this,
286 base::Passed(&prefs)));
[email protected]8e4560b62011-01-14 10:09:14287}
[email protected]5df038b2012-07-16 19:03:27288
289} // namespace extensions