blob: 0afc60dd0ef96c82e927800797ca652acddf4170 [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"
Gabriel Charette44db1422018-08-06 11:19:3318#include "base/task/post_task.h"
Istiaque Ahmed35e30fd2017-08-10 17:41:3019#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"
Eric Seckler8652dcd52018-09-20 10:42:2826#include "content/public/browser/browser_task_traits.h"
[email protected]c38831a12011-10-28 12:44:4927#include "content/public/browser/browser_thread.h"
[email protected]8e4560b62011-01-14 10:09:1428
[email protected]631bb742011-11-02 11:29:3929using content::BrowserThread;
30
[email protected]8e4560b62011-01-14 10:09:1431namespace {
32
[email protected]8e4560b62011-01-14 10:09:1433// The Registry subkey that contains information about external extensions.
thestig11bf74d2014-11-24 20:14:4234const base::char16 kRegistryExtensions[] =
35 L"Software\\Google\\Chrome\\Extensions";
[email protected]8e4560b62011-01-14 10:09:1436
[email protected]d8fd0fd2014-03-24 13:16:0637// Registry value of the key that defines the installation parameter.
thestig11bf74d2014-11-24 20:14:4238const base::char16 kRegistryExtensionInstallParam[] = L"install_parameter";
[email protected]d8fd0fd2014-03-24 13:16:0639
[email protected]5873a5a2013-12-16 19:53:0040// Registry value of the key that defines the path to the .crx file.
thestig11bf74d2014-11-24 20:14:4241const base::char16 kRegistryExtensionPath[] = L"path";
[email protected]8e4560b62011-01-14 10:09:1442
43// Registry value of that key that defines the current version of the .crx file.
thestig11bf74d2014-11-24 20:14:4244const base::char16 kRegistryExtensionVersion[] = L"version";
[email protected]8e4560b62011-01-14 10:09:1445
[email protected]5873a5a2013-12-16 19:53:0046// Registry value of the key that defines an external update URL.
thestig11bf74d2014-11-24 20:14:4247const base::char16 kRegistryExtensionUpdateUrl[] = L"update_url";
[email protected]5873a5a2013-12-16 19:53:0048
[email protected]650b2d52013-02-10 03:41:4549bool CanOpenFileForReading(const base::FilePath& path) {
Istiaque Ahmed35e30fd2017-08-10 17:41:3050 // Note: Because this ScopedFILE is used on the stack and not passed around
51 // threads/sequences, this method doesn't require callers to run on tasks with
52 // BLOCK_SHUTDOWN. SKIP_ON_SHUTDOWN is enough and safe because it guarantees
53 // that if a task starts, it will always finish, and will block shutdown at
54 // that point.
[email protected]0910bae2014-06-10 17:53:2155 base::ScopedFILE file_handle(base::OpenFile(path, "rb"));
[email protected]b967a1b2011-08-17 14:39:5856 return file_handle.get() != NULL;
57}
58
[email protected]2b6a5802014-08-16 07:58:0859std::string MakePrefName(const std::string& extension_id,
60 const std::string& pref_name) {
61 return base::StringPrintf("%s.%s", extension_id.c_str(), pref_name.c_str());
62}
63
[email protected]8e4560b62011-01-14 10:09:1464} // namespace
65
[email protected]5df038b2012-07-16 19:03:2766namespace extensions {
67
lazyboy36920272017-04-04 23:51:3268ExternalRegistryLoader::ExternalRegistryLoader()
69 : attempted_watching_registry_(false) {}
70
Istiaque Ahmed35e30fd2017-08-10 17:41:3071ExternalRegistryLoader::~ExternalRegistryLoader() {}
72
[email protected]5df038b2012-07-16 19:03:2773void ExternalRegistryLoader::StartLoading() {
Istiaque Ahmedf6e72622017-09-08 23:14:1774 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Istiaque Ahmed35e30fd2017-08-10 17:41:3075 GetOrCreateTaskRunner()->PostTask(
76 FROM_HERE,
77 base::BindOnce(&ExternalRegistryLoader::LoadOnBlockingThread, this));
[email protected]8e4560b62011-01-14 10:09:1478}
79
dchengc963c7142016-04-08 03:55:2280std::unique_ptr<base::DictionaryValue>
Istiaque Ahmed35e30fd2017-08-10 17:41:3081ExternalRegistryLoader::LoadPrefsOnBlockingThread() {
Etienne Pierre-doraya4195e592018-10-18 16:36:4282 base::AssertBlockingAllowedDeprecated();
Jinho Bangb5216cec2018-01-17 19:43:1183 auto prefs = std::make_unique<base::DictionaryValue>();
[email protected]8e4560b62011-01-14 10:09:1484
[email protected]1358b282011-09-15 15:35:1485 // A map of IDs, to weed out duplicates between HKCU and HKLM.
[email protected]d2065e062013-12-12 23:49:5286 std::set<base::string16> keys;
[email protected]1358b282011-09-15 15:35:1487 base::win::RegistryKeyIterator iterator_machine_key(
wfh9353beb92014-10-09 04:31:3888 HKEY_LOCAL_MACHINE,
thestig11bf74d2014-11-24 20:14:4289 kRegistryExtensions,
wfh9353beb92014-10-09 04:31:3890 KEY_WOW64_32KEY);
[email protected]1358b282011-09-15 15:35:1491 for (; iterator_machine_key.Valid(); ++iterator_machine_key)
92 keys.insert(iterator_machine_key.Name());
93 base::win::RegistryKeyIterator iterator_user_key(
thestig11bf74d2014-11-24 20:14:4294 HKEY_CURRENT_USER, kRegistryExtensions);
[email protected]1358b282011-09-15 15:35:1495 for (; iterator_user_key.Valid(); ++iterator_user_key)
96 keys.insert(iterator_user_key.Name());
97
98 // Iterate over the keys found, first trying HKLM, then HKCU, as per Windows
99 // policy conventions. We only fall back to HKCU if the HKLM key cannot be
100 // opened, not if the data within the key is invalid, for example.
[email protected]d2065e062013-12-12 23:49:52101 for (std::set<base::string16>::const_iterator it = keys.begin();
[email protected]1358b282011-09-15 15:35:14102 it != keys.end(); ++it) {
[email protected]8e4560b62011-01-14 10:09:14103 base::win::RegKey key;
thestig11bf74d2014-11-24 20:14:42104 base::string16 key_path = kRegistryExtensions;
[email protected]8e4560b62011-01-14 10:09:14105 key_path.append(L"\\");
[email protected]1358b282011-09-15 15:35:14106 key_path.append(*it);
107 if (key.Open(HKEY_LOCAL_MACHINE,
wfh9353beb92014-10-09 04:31:38108 key_path.c_str(),
109 KEY_READ | KEY_WOW64_32KEY) != ERROR_SUCCESS &&
110 key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ) !=
111 ERROR_SUCCESS) {
112 LOG(ERROR) << "Unable to read registry key at path (HKLM & HKCU): "
113 << key_path << ".";
114 continue;
[email protected]8e4560b62011-01-14 10:09:14115 }
[email protected]b967a1b2011-08-17 14:39:58116
brettwfce8d192015-08-10 19:07:51117 std::string id = base::ToLowerASCII(base::UTF16ToASCII(*it));
[email protected]fdd28372014-08-21 02:27:26118 if (!crx_file::id_util::IdIsValid(id)) {
[email protected]5873a5a2013-12-16 19:53:00119 LOG(ERROR) << "Invalid id value " << id
120 << " for key " << key_path << ".";
121 continue;
122 }
123
[email protected]d8fd0fd2014-03-24 13:16:06124 base::string16 extension_dist_id;
125 if (key.ReadValue(kRegistryExtensionInstallParam, &extension_dist_id) ==
126 ERROR_SUCCESS) {
[email protected]2b6a5802014-08-16 07:58:08127 prefs->SetString(MakePrefName(id, ExternalProviderImpl::kInstallParam),
[email protected]d8fd0fd2014-03-24 13:16:06128 base::UTF16ToASCII(extension_dist_id));
129 }
130
[email protected]5873a5a2013-12-16 19:53:00131 // If there is an update URL present, copy it to prefs and ignore
132 // path and version keys for this entry.
133 base::string16 extension_update_url;
134 if (key.ReadValue(kRegistryExtensionUpdateUrl, &extension_update_url)
135 == ERROR_SUCCESS) {
136 prefs->SetString(
[email protected]2b6a5802014-08-16 07:58:08137 MakePrefName(id, ExternalProviderImpl::kExternalUpdateUrl),
[email protected]74f778e2014-03-14 21:11:46138 base::UTF16ToASCII(extension_update_url));
[email protected]5873a5a2013-12-16 19:53:00139 continue;
140 }
141
[email protected]439f1e32013-12-09 20:09:09142 base::string16 extension_path_str;
[email protected]b967a1b2011-08-17 14:39:58143 if (key.ReadValue(kRegistryExtensionPath, &extension_path_str)
144 != ERROR_SUCCESS) {
145 // TODO(erikkay): find a way to get this into about:extensions
146 LOG(ERROR) << "Missing value " << kRegistryExtensionPath
147 << " for key " << key_path << ".";
148 continue;
149 }
150
[email protected]650b2d52013-02-10 03:41:45151 base::FilePath extension_path(extension_path_str);
[email protected]b967a1b2011-08-17 14:39:58152 if (!extension_path.IsAbsolute()) {
153 LOG(ERROR) << "File path " << extension_path_str
154 << " needs to be absolute in key "
155 << key_path;
156 continue;
157 }
158
[email protected]7567484142013-07-11 17:36:07159 if (!base::PathExists(extension_path)) {
[email protected]b967a1b2011-08-17 14:39:58160 LOG(ERROR) << "File " << extension_path_str
161 << " for key " << key_path
162 << " does not exist or is not readable.";
163 continue;
164 }
165
166 if (!CanOpenFileForReading(extension_path)) {
167 LOG(ERROR) << "File " << extension_path_str
168 << " for key " << key_path << " can not be read. "
169 << "Check that users who should have the extension "
170 << "installed have permission to read it.";
171 continue;
172 }
173
[email protected]439f1e32013-12-09 20:09:09174 base::string16 extension_version;
[email protected]b967a1b2011-08-17 14:39:58175 if (key.ReadValue(kRegistryExtensionVersion, &extension_version)
176 != ERROR_SUCCESS) {
177 // TODO(erikkay): find a way to get this into about:extensions
178 LOG(ERROR) << "Missing value " << kRegistryExtensionVersion
179 << " for key " << key_path << ".";
180 continue;
181 }
182
pwnallcbd73192016-08-22 18:59:17183 base::Version version(base::UTF16ToASCII(extension_version));
[email protected]12126d372012-07-11 18:40:53184 if (!version.IsValid()) {
[email protected]b967a1b2011-08-17 14:39:58185 LOG(ERROR) << "Invalid version value " << extension_version
186 << " for key " << key_path << ".";
187 continue;
188 }
189
190 prefs->SetString(
[email protected]2b6a5802014-08-16 07:58:08191 MakePrefName(id, ExternalProviderImpl::kExternalVersion),
[email protected]74f778e2014-03-14 21:11:46192 base::UTF16ToASCII(extension_version));
[email protected]b967a1b2011-08-17 14:39:58193 prefs->SetString(
[email protected]2b6a5802014-08-16 07:58:08194 MakePrefName(id, ExternalProviderImpl::kExternalCrx),
[email protected]b967a1b2011-08-17 14:39:58195 extension_path_str);
[email protected]2b6a5802014-08-16 07:58:08196 prefs->SetBoolean(
197 MakePrefName(id, ExternalProviderImpl::kMayBeUntrusted),
198 true);
[email protected]8e4560b62011-01-14 10:09:14199 }
200
lazyboye8634172016-01-28 00:10:48201 return prefs;
202}
203
Istiaque Ahmed35e30fd2017-08-10 17:41:30204void ExternalRegistryLoader::LoadOnBlockingThread() {
205 DCHECK(task_runner_);
206 DCHECK(task_runner_->RunsTasksInCurrentSequence());
lazyboye8634172016-01-28 00:10:48207 base::TimeTicks start_time = base::TimeTicks::Now();
Istiaque Ahmed35e30fd2017-08-10 17:41:30208 std::unique_ptr<base::DictionaryValue> prefs = LoadPrefsOnBlockingThread();
asvitkinec0fb8022014-08-26 04:39:35209 LOCAL_HISTOGRAM_TIMES("Extensions.ExternalRegistryLoaderWin",
210 base::TimeTicks::Now() - start_time);
Eric Seckler8652dcd52018-09-20 10:42:28211 base::PostTaskWithTraits(
212 FROM_HERE, {BrowserThread::UI},
lazyboye8634172016-01-28 00:10:48213 base::Bind(&ExternalRegistryLoader::CompleteLoadAndStartWatchingRegistry,
lazyboy57972e02017-04-12 22:40:59214 this, base::Passed(&prefs)));
lazyboye8634172016-01-28 00:10:48215}
216
lazyboy57972e02017-04-12 22:40:59217void ExternalRegistryLoader::CompleteLoadAndStartWatchingRegistry(
218 std::unique_ptr<base::DictionaryValue> prefs) {
Istiaque Ahmed35e30fd2017-08-10 17:41:30219 DCHECK_CURRENTLY_ON(BrowserThread::UI);
lazyboy57972e02017-04-12 22:40:59220 DCHECK(prefs);
Istiaque Ahmedef99c8ea2017-09-06 21:19:57221 LoadFinished(std::move(prefs));
lazyboye8634172016-01-28 00:10:48222
lazyboy36920272017-04-04 23:51:32223 // Attempt to watch registry if we haven't already.
224 if (attempted_watching_registry_)
225 return;
226
wez2884d9b2017-02-15 01:19:26227 LONG result = ERROR_SUCCESS;
228 if ((result = hklm_key_.Create(HKEY_LOCAL_MACHINE, kRegistryExtensions,
229 KEY_NOTIFY | KEY_WOW64_32KEY)) ==
230 ERROR_SUCCESS) {
lazyboye8634172016-01-28 00:10:48231 base::win::RegKey::ChangeCallback callback =
232 base::Bind(&ExternalRegistryLoader::OnRegistryKeyChanged,
233 base::Unretained(this), base::Unretained(&hklm_key_));
234 hklm_key_.StartWatching(callback);
235 } else {
wez2884d9b2017-02-15 01:19:26236 LOG(WARNING) << "Error observing HKLM: " << result;
lazyboye8634172016-01-28 00:10:48237 }
238
wez2884d9b2017-02-15 01:19:26239 if ((result = hkcu_key_.Create(HKEY_CURRENT_USER, kRegistryExtensions,
240 KEY_NOTIFY)) == ERROR_SUCCESS) {
lazyboye8634172016-01-28 00:10:48241 base::win::RegKey::ChangeCallback callback =
242 base::Bind(&ExternalRegistryLoader::OnRegistryKeyChanged,
243 base::Unretained(this), base::Unretained(&hkcu_key_));
244 hkcu_key_.StartWatching(callback);
245 } else {
wez2884d9b2017-02-15 01:19:26246 LOG(WARNING) << "Error observing HKCU: " << result;
lazyboye8634172016-01-28 00:10:48247 }
lazyboy36920272017-04-04 23:51:32248
249 attempted_watching_registry_ = true;
lazyboye8634172016-01-28 00:10:48250}
251
252void ExternalRegistryLoader::OnRegistryKeyChanged(base::win::RegKey* key) {
253 // |OnRegistryKeyChanged| is removed as an observer when the ChangeCallback is
254 // called, so we need to re-register.
255 key->StartWatching(base::Bind(&ExternalRegistryLoader::OnRegistryKeyChanged,
256 base::Unretained(this), base::Unretained(key)));
257
Istiaque Ahmed35e30fd2017-08-10 17:41:30258 GetOrCreateTaskRunner()->PostTask(
259 FROM_HERE,
260 base::BindOnce(&ExternalRegistryLoader::UpatePrefsOnBlockingThread,
261 this));
lazyboye8634172016-01-28 00:10:48262}
263
Istiaque Ahmed35e30fd2017-08-10 17:41:30264scoped_refptr<base::SequencedTaskRunner>
265ExternalRegistryLoader::GetOrCreateTaskRunner() {
266 if (!task_runner_.get()) {
267 task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
268 {// Requires I/O for registry.
269 base::MayBlock(),
270
271 // Inherit priority.
272
273 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
274 }
275 return task_runner_;
276}
277
278void ExternalRegistryLoader::UpatePrefsOnBlockingThread() {
279 DCHECK(task_runner_);
280 DCHECK(task_runner_->RunsTasksInCurrentSequence());
lazyboye8634172016-01-28 00:10:48281 base::TimeTicks start_time = base::TimeTicks::Now();
Istiaque Ahmed35e30fd2017-08-10 17:41:30282 std::unique_ptr<base::DictionaryValue> prefs = LoadPrefsOnBlockingThread();
lazyboye8634172016-01-28 00:10:48283 LOCAL_HISTOGRAM_TIMES("Extensions.ExternalRegistryLoaderWinUpdate",
284 base::TimeTicks::Now() - start_time);
Eric Seckler8652dcd52018-09-20 10:42:28285 base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
286 base::Bind(&ExternalRegistryLoader::OnUpdated, this,
287 base::Passed(&prefs)));
[email protected]8e4560b62011-01-14 10:09:14288}
[email protected]5df038b2012-07-16 19:03:27289
290} // namespace extensions