blob: 44b0a402a7b729251f3d40d5020682530b6707da [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"
[email protected]46acbf12013-06-10 18:43:4214#include "base/strings/string_util.h"
[email protected]2b6a5802014-08-16 07:58:0815#include "base/strings/stringprintf.h"
[email protected]112158af2013-06-07 23:46:1816#include "base/strings/utf_string_conversions.h"
Istiaque Ahmed35e30fd2017-08-10 17:41:3017#include "base/task_scheduler/post_task.h"
18#include "base/threading/thread_restrictions.h"
[email protected]41a17c52013-06-28 00:27:5319#include "base/time/time.h"
[email protected]8e4560b62011-01-14 10:09:1420#include "base/values.h"
21#include "base/version.h"
22#include "base/win/registry.h"
[email protected]5df038b2012-07-16 19:03:2723#include "chrome/browser/extensions/external_provider_impl.h"
[email protected]fdd28372014-08-21 02:27:2624#include "components/crx_file/id_util.h"
[email protected]c38831a12011-10-28 12:44:4925#include "content/public/browser/browser_thread.h"
[email protected]8e4560b62011-01-14 10:09:1426
[email protected]631bb742011-11-02 11:29:3927using content::BrowserThread;
28
[email protected]8e4560b62011-01-14 10:09:1429namespace {
30
[email protected]8e4560b62011-01-14 10:09:1431// The Registry subkey that contains information about external extensions.
thestig11bf74d2014-11-24 20:14:4232const base::char16 kRegistryExtensions[] =
33 L"Software\\Google\\Chrome\\Extensions";
[email protected]8e4560b62011-01-14 10:09:1434
[email protected]d8fd0fd2014-03-24 13:16:0635// Registry value of the key that defines the installation parameter.
thestig11bf74d2014-11-24 20:14:4236const base::char16 kRegistryExtensionInstallParam[] = L"install_parameter";
[email protected]d8fd0fd2014-03-24 13:16:0637
[email protected]5873a5a2013-12-16 19:53:0038// Registry value of the key that defines the path to the .crx file.
thestig11bf74d2014-11-24 20:14:4239const base::char16 kRegistryExtensionPath[] = L"path";
[email protected]8e4560b62011-01-14 10:09:1440
41// Registry value of that key that defines the current version of the .crx file.
thestig11bf74d2014-11-24 20:14:4242const base::char16 kRegistryExtensionVersion[] = L"version";
[email protected]8e4560b62011-01-14 10:09:1443
[email protected]5873a5a2013-12-16 19:53:0044// Registry value of the key that defines an external update URL.
thestig11bf74d2014-11-24 20:14:4245const base::char16 kRegistryExtensionUpdateUrl[] = L"update_url";
[email protected]5873a5a2013-12-16 19:53:0046
[email protected]650b2d52013-02-10 03:41:4547bool CanOpenFileForReading(const base::FilePath& path) {
Istiaque Ahmed35e30fd2017-08-10 17:41:3048 // Note: Because this ScopedFILE is used on the stack and not passed around
49 // threads/sequences, this method doesn't require callers to run on tasks with
50 // BLOCK_SHUTDOWN. SKIP_ON_SHUTDOWN is enough and safe because it guarantees
51 // that if a task starts, it will always finish, and will block shutdown at
52 // that point.
[email protected]0910bae2014-06-10 17:53:2153 base::ScopedFILE file_handle(base::OpenFile(path, "rb"));
[email protected]b967a1b2011-08-17 14:39:5854 return file_handle.get() != NULL;
55}
56
[email protected]2b6a5802014-08-16 07:58:0857std::string MakePrefName(const std::string& extension_id,
58 const std::string& pref_name) {
59 return base::StringPrintf("%s.%s", extension_id.c_str(), pref_name.c_str());
60}
61
[email protected]8e4560b62011-01-14 10:09:1462} // namespace
63
[email protected]5df038b2012-07-16 19:03:2764namespace extensions {
65
lazyboy36920272017-04-04 23:51:3266ExternalRegistryLoader::ExternalRegistryLoader()
67 : attempted_watching_registry_(false) {}
68
Istiaque Ahmed35e30fd2017-08-10 17:41:3069ExternalRegistryLoader::~ExternalRegistryLoader() {}
70
[email protected]5df038b2012-07-16 19:03:2771void ExternalRegistryLoader::StartLoading() {
Istiaque Ahmedf6e72622017-09-08 23:14:1772 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Istiaque Ahmed35e30fd2017-08-10 17:41:3073 GetOrCreateTaskRunner()->PostTask(
74 FROM_HERE,
75 base::BindOnce(&ExternalRegistryLoader::LoadOnBlockingThread, this));
[email protected]8e4560b62011-01-14 10:09:1476}
77
dchengc963c7142016-04-08 03:55:2278std::unique_ptr<base::DictionaryValue>
Istiaque Ahmed35e30fd2017-08-10 17:41:3079ExternalRegistryLoader::LoadPrefsOnBlockingThread() {
Francois Doray66bdfd82017-10-20 13:50:3780 base::AssertBlockingAllowed();
Jinho Bangb5216cec2018-01-17 19:43:1181 auto prefs = std::make_unique<base::DictionaryValue>();
[email protected]8e4560b62011-01-14 10:09:1482
[email protected]1358b282011-09-15 15:35:1483 // A map of IDs, to weed out duplicates between HKCU and HKLM.
[email protected]d2065e062013-12-12 23:49:5284 std::set<base::string16> keys;
[email protected]1358b282011-09-15 15:35:1485 base::win::RegistryKeyIterator iterator_machine_key(
wfh9353beb92014-10-09 04:31:3886 HKEY_LOCAL_MACHINE,
thestig11bf74d2014-11-24 20:14:4287 kRegistryExtensions,
wfh9353beb92014-10-09 04:31:3888 KEY_WOW64_32KEY);
[email protected]1358b282011-09-15 15:35:1489 for (; iterator_machine_key.Valid(); ++iterator_machine_key)
90 keys.insert(iterator_machine_key.Name());
91 base::win::RegistryKeyIterator iterator_user_key(
thestig11bf74d2014-11-24 20:14:4292 HKEY_CURRENT_USER, kRegistryExtensions);
[email protected]1358b282011-09-15 15:35:1493 for (; iterator_user_key.Valid(); ++iterator_user_key)
94 keys.insert(iterator_user_key.Name());
95
96 // Iterate over the keys found, first trying HKLM, then HKCU, as per Windows
97 // policy conventions. We only fall back to HKCU if the HKLM key cannot be
98 // opened, not if the data within the key is invalid, for example.
[email protected]d2065e062013-12-12 23:49:5299 for (std::set<base::string16>::const_iterator it = keys.begin();
[email protected]1358b282011-09-15 15:35:14100 it != keys.end(); ++it) {
[email protected]8e4560b62011-01-14 10:09:14101 base::win::RegKey key;
thestig11bf74d2014-11-24 20:14:42102 base::string16 key_path = kRegistryExtensions;
[email protected]8e4560b62011-01-14 10:09:14103 key_path.append(L"\\");
[email protected]1358b282011-09-15 15:35:14104 key_path.append(*it);
105 if (key.Open(HKEY_LOCAL_MACHINE,
wfh9353beb92014-10-09 04:31:38106 key_path.c_str(),
107 KEY_READ | KEY_WOW64_32KEY) != ERROR_SUCCESS &&
108 key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ) !=
109 ERROR_SUCCESS) {
110 LOG(ERROR) << "Unable to read registry key at path (HKLM & HKCU): "
111 << key_path << ".";
112 continue;
[email protected]8e4560b62011-01-14 10:09:14113 }
[email protected]b967a1b2011-08-17 14:39:58114
brettwfce8d192015-08-10 19:07:51115 std::string id = base::ToLowerASCII(base::UTF16ToASCII(*it));
[email protected]fdd28372014-08-21 02:27:26116 if (!crx_file::id_util::IdIsValid(id)) {
[email protected]5873a5a2013-12-16 19:53:00117 LOG(ERROR) << "Invalid id value " << id
118 << " for key " << key_path << ".";
119 continue;
120 }
121
[email protected]d8fd0fd2014-03-24 13:16:06122 base::string16 extension_dist_id;
123 if (key.ReadValue(kRegistryExtensionInstallParam, &extension_dist_id) ==
124 ERROR_SUCCESS) {
[email protected]2b6a5802014-08-16 07:58:08125 prefs->SetString(MakePrefName(id, ExternalProviderImpl::kInstallParam),
[email protected]d8fd0fd2014-03-24 13:16:06126 base::UTF16ToASCII(extension_dist_id));
127 }
128
[email protected]5873a5a2013-12-16 19:53:00129 // If there is an update URL present, copy it to prefs and ignore
130 // path and version keys for this entry.
131 base::string16 extension_update_url;
132 if (key.ReadValue(kRegistryExtensionUpdateUrl, &extension_update_url)
133 == ERROR_SUCCESS) {
134 prefs->SetString(
[email protected]2b6a5802014-08-16 07:58:08135 MakePrefName(id, ExternalProviderImpl::kExternalUpdateUrl),
[email protected]74f778e2014-03-14 21:11:46136 base::UTF16ToASCII(extension_update_url));
[email protected]5873a5a2013-12-16 19:53:00137 continue;
138 }
139
[email protected]439f1e32013-12-09 20:09:09140 base::string16 extension_path_str;
[email protected]b967a1b2011-08-17 14:39:58141 if (key.ReadValue(kRegistryExtensionPath, &extension_path_str)
142 != ERROR_SUCCESS) {
143 // TODO(erikkay): find a way to get this into about:extensions
144 LOG(ERROR) << "Missing value " << kRegistryExtensionPath
145 << " for key " << key_path << ".";
146 continue;
147 }
148
[email protected]650b2d52013-02-10 03:41:45149 base::FilePath extension_path(extension_path_str);
[email protected]b967a1b2011-08-17 14:39:58150 if (!extension_path.IsAbsolute()) {
151 LOG(ERROR) << "File path " << extension_path_str
152 << " needs to be absolute in key "
153 << key_path;
154 continue;
155 }
156
[email protected]7567484142013-07-11 17:36:07157 if (!base::PathExists(extension_path)) {
[email protected]b967a1b2011-08-17 14:39:58158 LOG(ERROR) << "File " << extension_path_str
159 << " for key " << key_path
160 << " does not exist or is not readable.";
161 continue;
162 }
163
164 if (!CanOpenFileForReading(extension_path)) {
165 LOG(ERROR) << "File " << extension_path_str
166 << " for key " << key_path << " can not be read. "
167 << "Check that users who should have the extension "
168 << "installed have permission to read it.";
169 continue;
170 }
171
[email protected]439f1e32013-12-09 20:09:09172 base::string16 extension_version;
[email protected]b967a1b2011-08-17 14:39:58173 if (key.ReadValue(kRegistryExtensionVersion, &extension_version)
174 != ERROR_SUCCESS) {
175 // TODO(erikkay): find a way to get this into about:extensions
176 LOG(ERROR) << "Missing value " << kRegistryExtensionVersion
177 << " for key " << key_path << ".";
178 continue;
179 }
180
pwnallcbd73192016-08-22 18:59:17181 base::Version version(base::UTF16ToASCII(extension_version));
[email protected]12126d372012-07-11 18:40:53182 if (!version.IsValid()) {
[email protected]b967a1b2011-08-17 14:39:58183 LOG(ERROR) << "Invalid version value " << extension_version
184 << " for key " << key_path << ".";
185 continue;
186 }
187
188 prefs->SetString(
[email protected]2b6a5802014-08-16 07:58:08189 MakePrefName(id, ExternalProviderImpl::kExternalVersion),
[email protected]74f778e2014-03-14 21:11:46190 base::UTF16ToASCII(extension_version));
[email protected]b967a1b2011-08-17 14:39:58191 prefs->SetString(
[email protected]2b6a5802014-08-16 07:58:08192 MakePrefName(id, ExternalProviderImpl::kExternalCrx),
[email protected]b967a1b2011-08-17 14:39:58193 extension_path_str);
[email protected]2b6a5802014-08-16 07:58:08194 prefs->SetBoolean(
195 MakePrefName(id, ExternalProviderImpl::kMayBeUntrusted),
196 true);
[email protected]8e4560b62011-01-14 10:09:14197 }
198
lazyboye8634172016-01-28 00:10:48199 return prefs;
200}
201
Istiaque Ahmed35e30fd2017-08-10 17:41:30202void ExternalRegistryLoader::LoadOnBlockingThread() {
203 DCHECK(task_runner_);
204 DCHECK(task_runner_->RunsTasksInCurrentSequence());
lazyboye8634172016-01-28 00:10:48205 base::TimeTicks start_time = base::TimeTicks::Now();
Istiaque Ahmed35e30fd2017-08-10 17:41:30206 std::unique_ptr<base::DictionaryValue> prefs = LoadPrefsOnBlockingThread();
asvitkinec0fb8022014-08-26 04:39:35207 LOCAL_HISTOGRAM_TIMES("Extensions.ExternalRegistryLoaderWin",
208 base::TimeTicks::Now() - start_time);
[email protected]8e4560b62011-01-14 10:09:14209 BrowserThread::PostTask(
210 BrowserThread::UI, FROM_HERE,
lazyboye8634172016-01-28 00:10:48211 base::Bind(&ExternalRegistryLoader::CompleteLoadAndStartWatchingRegistry,
lazyboy57972e02017-04-12 22:40:59212 this, base::Passed(&prefs)));
lazyboye8634172016-01-28 00:10:48213}
214
lazyboy57972e02017-04-12 22:40:59215void ExternalRegistryLoader::CompleteLoadAndStartWatchingRegistry(
216 std::unique_ptr<base::DictionaryValue> prefs) {
Istiaque Ahmed35e30fd2017-08-10 17:41:30217 DCHECK_CURRENTLY_ON(BrowserThread::UI);
lazyboy57972e02017-04-12 22:40:59218 DCHECK(prefs);
Istiaque Ahmedef99c8ea2017-09-06 21:19:57219 LoadFinished(std::move(prefs));
lazyboye8634172016-01-28 00:10:48220
lazyboy36920272017-04-04 23:51:32221 // Attempt to watch registry if we haven't already.
222 if (attempted_watching_registry_)
223 return;
224
wez2884d9b2017-02-15 01:19:26225 LONG result = ERROR_SUCCESS;
226 if ((result = hklm_key_.Create(HKEY_LOCAL_MACHINE, kRegistryExtensions,
227 KEY_NOTIFY | KEY_WOW64_32KEY)) ==
228 ERROR_SUCCESS) {
lazyboye8634172016-01-28 00:10:48229 base::win::RegKey::ChangeCallback callback =
230 base::Bind(&ExternalRegistryLoader::OnRegistryKeyChanged,
231 base::Unretained(this), base::Unretained(&hklm_key_));
232 hklm_key_.StartWatching(callback);
233 } else {
wez2884d9b2017-02-15 01:19:26234 LOG(WARNING) << "Error observing HKLM: " << result;
lazyboye8634172016-01-28 00:10:48235 }
236
wez2884d9b2017-02-15 01:19:26237 if ((result = hkcu_key_.Create(HKEY_CURRENT_USER, kRegistryExtensions,
238 KEY_NOTIFY)) == ERROR_SUCCESS) {
lazyboye8634172016-01-28 00:10:48239 base::win::RegKey::ChangeCallback callback =
240 base::Bind(&ExternalRegistryLoader::OnRegistryKeyChanged,
241 base::Unretained(this), base::Unretained(&hkcu_key_));
242 hkcu_key_.StartWatching(callback);
243 } else {
wez2884d9b2017-02-15 01:19:26244 LOG(WARNING) << "Error observing HKCU: " << result;
lazyboye8634172016-01-28 00:10:48245 }
lazyboy36920272017-04-04 23:51:32246
247 attempted_watching_registry_ = true;
lazyboye8634172016-01-28 00:10:48248}
249
250void ExternalRegistryLoader::OnRegistryKeyChanged(base::win::RegKey* key) {
251 // |OnRegistryKeyChanged| is removed as an observer when the ChangeCallback is
252 // called, so we need to re-register.
253 key->StartWatching(base::Bind(&ExternalRegistryLoader::OnRegistryKeyChanged,
254 base::Unretained(this), base::Unretained(key)));
255
Istiaque Ahmed35e30fd2017-08-10 17:41:30256 GetOrCreateTaskRunner()->PostTask(
257 FROM_HERE,
258 base::BindOnce(&ExternalRegistryLoader::UpatePrefsOnBlockingThread,
259 this));
lazyboye8634172016-01-28 00:10:48260}
261
Istiaque Ahmed35e30fd2017-08-10 17:41:30262scoped_refptr<base::SequencedTaskRunner>
263ExternalRegistryLoader::GetOrCreateTaskRunner() {
264 if (!task_runner_.get()) {
265 task_runner_ = base::CreateSequencedTaskRunnerWithTraits(
266 {// Requires I/O for registry.
267 base::MayBlock(),
268
269 // Inherit priority.
270
271 base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
272 }
273 return task_runner_;
274}
275
276void ExternalRegistryLoader::UpatePrefsOnBlockingThread() {
277 DCHECK(task_runner_);
278 DCHECK(task_runner_->RunsTasksInCurrentSequence());
lazyboye8634172016-01-28 00:10:48279 base::TimeTicks start_time = base::TimeTicks::Now();
Istiaque Ahmed35e30fd2017-08-10 17:41:30280 std::unique_ptr<base::DictionaryValue> prefs = LoadPrefsOnBlockingThread();
lazyboye8634172016-01-28 00:10:48281 LOCAL_HISTOGRAM_TIMES("Extensions.ExternalRegistryLoaderWinUpdate",
282 base::TimeTicks::Now() - start_time);
283 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
284 base::Bind(&ExternalRegistryLoader::OnUpdated, this,
285 base::Passed(&prefs)));
[email protected]8e4560b62011-01-14 10:09:14286}
[email protected]5df038b2012-07-16 19:03:27287
288} // namespace extensions