blob: adc164e634a9ffb4a00b48419ba1b0e490d52e78 [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
[email protected]8e6ac4b2011-10-17 19:04:317#include "base/bind.h"
[email protected]57999812013-02-24 05:40:528#include "base/files/file_path.h"
thestig18dfb7a52014-08-26 10:44:049#include "base/files/file_util.h"
[email protected]0910bae2014-06-10 17:53:2110#include "base/files/scoped_file.h"
asvitkineaa060312016-09-01 22:44:1311#include "base/metrics/histogram_macros.h"
[email protected]46acbf12013-06-10 18:43:4212#include "base/strings/string_util.h"
[email protected]2b6a5802014-08-16 07:58:0813#include "base/strings/stringprintf.h"
[email protected]112158af2013-06-07 23:46:1814#include "base/strings/utf_string_conversions.h"
[email protected]41a17c52013-06-28 00:27:5315#include "base/time/time.h"
[email protected]8e4560b62011-01-14 10:09:1416#include "base/values.h"
17#include "base/version.h"
18#include "base/win/registry.h"
[email protected]5df038b2012-07-16 19:03:2719#include "chrome/browser/extensions/external_provider_impl.h"
[email protected]fdd28372014-08-21 02:27:2620#include "components/crx_file/id_util.h"
[email protected]c38831a12011-10-28 12:44:4921#include "content/public/browser/browser_thread.h"
[email protected]8e4560b62011-01-14 10:09:1422
[email protected]631bb742011-11-02 11:29:3923using content::BrowserThread;
24
[email protected]8e4560b62011-01-14 10:09:1425namespace {
26
[email protected]8e4560b62011-01-14 10:09:1427// The Registry subkey that contains information about external extensions.
thestig11bf74d2014-11-24 20:14:4228const base::char16 kRegistryExtensions[] =
29 L"Software\\Google\\Chrome\\Extensions";
[email protected]8e4560b62011-01-14 10:09:1430
[email protected]d8fd0fd2014-03-24 13:16:0631// Registry value of the key that defines the installation parameter.
thestig11bf74d2014-11-24 20:14:4232const base::char16 kRegistryExtensionInstallParam[] = L"install_parameter";
[email protected]d8fd0fd2014-03-24 13:16:0633
[email protected]5873a5a2013-12-16 19:53:0034// Registry value of the key that defines the path to the .crx file.
thestig11bf74d2014-11-24 20:14:4235const base::char16 kRegistryExtensionPath[] = L"path";
[email protected]8e4560b62011-01-14 10:09:1436
37// Registry value of that key that defines the current version of the .crx file.
thestig11bf74d2014-11-24 20:14:4238const base::char16 kRegistryExtensionVersion[] = L"version";
[email protected]8e4560b62011-01-14 10:09:1439
[email protected]5873a5a2013-12-16 19:53:0040// Registry value of the key that defines an external update URL.
thestig11bf74d2014-11-24 20:14:4241const base::char16 kRegistryExtensionUpdateUrl[] = L"update_url";
[email protected]5873a5a2013-12-16 19:53:0042
[email protected]650b2d52013-02-10 03:41:4543bool CanOpenFileForReading(const base::FilePath& path) {
[email protected]0910bae2014-06-10 17:53:2144 base::ScopedFILE file_handle(base::OpenFile(path, "rb"));
[email protected]b967a1b2011-08-17 14:39:5845 return file_handle.get() != NULL;
46}
47
[email protected]2b6a5802014-08-16 07:58:0848std::string MakePrefName(const std::string& extension_id,
49 const std::string& pref_name) {
50 return base::StringPrintf("%s.%s", extension_id.c_str(), pref_name.c_str());
51}
52
[email protected]8e4560b62011-01-14 10:09:1453} // namespace
54
[email protected]5df038b2012-07-16 19:03:2755namespace extensions {
56
57void ExternalRegistryLoader::StartLoading() {
[email protected]8e4560b62011-01-14 10:09:1458 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
59 BrowserThread::PostTask(
60 BrowserThread::FILE, FROM_HERE,
[email protected]5df038b2012-07-16 19:03:2761 base::Bind(&ExternalRegistryLoader::LoadOnFileThread, this));
[email protected]8e4560b62011-01-14 10:09:1462}
63
dchengc963c7142016-04-08 03:55:2264std::unique_ptr<base::DictionaryValue>
lazyboye8634172016-01-28 00:10:4865ExternalRegistryLoader::LoadPrefsOnFileThread() {
[email protected]8e4560b62011-01-14 10:09:1466 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
dchengc963c7142016-04-08 03:55:2267 std::unique_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
[email protected]8e4560b62011-01-14 10:09:1468
[email protected]1358b282011-09-15 15:35:1469 // A map of IDs, to weed out duplicates between HKCU and HKLM.
[email protected]d2065e062013-12-12 23:49:5270 std::set<base::string16> keys;
[email protected]1358b282011-09-15 15:35:1471 base::win::RegistryKeyIterator iterator_machine_key(
wfh9353beb92014-10-09 04:31:3872 HKEY_LOCAL_MACHINE,
thestig11bf74d2014-11-24 20:14:4273 kRegistryExtensions,
wfh9353beb92014-10-09 04:31:3874 KEY_WOW64_32KEY);
[email protected]1358b282011-09-15 15:35:1475 for (; iterator_machine_key.Valid(); ++iterator_machine_key)
76 keys.insert(iterator_machine_key.Name());
77 base::win::RegistryKeyIterator iterator_user_key(
thestig11bf74d2014-11-24 20:14:4278 HKEY_CURRENT_USER, kRegistryExtensions);
[email protected]1358b282011-09-15 15:35:1479 for (; iterator_user_key.Valid(); ++iterator_user_key)
80 keys.insert(iterator_user_key.Name());
81
82 // Iterate over the keys found, first trying HKLM, then HKCU, as per Windows
83 // policy conventions. We only fall back to HKCU if the HKLM key cannot be
84 // opened, not if the data within the key is invalid, for example.
[email protected]d2065e062013-12-12 23:49:5285 for (std::set<base::string16>::const_iterator it = keys.begin();
[email protected]1358b282011-09-15 15:35:1486 it != keys.end(); ++it) {
[email protected]8e4560b62011-01-14 10:09:1487 base::win::RegKey key;
thestig11bf74d2014-11-24 20:14:4288 base::string16 key_path = kRegistryExtensions;
[email protected]8e4560b62011-01-14 10:09:1489 key_path.append(L"\\");
[email protected]1358b282011-09-15 15:35:1490 key_path.append(*it);
91 if (key.Open(HKEY_LOCAL_MACHINE,
wfh9353beb92014-10-09 04:31:3892 key_path.c_str(),
93 KEY_READ | KEY_WOW64_32KEY) != ERROR_SUCCESS &&
94 key.Open(HKEY_CURRENT_USER, key_path.c_str(), KEY_READ) !=
95 ERROR_SUCCESS) {
96 LOG(ERROR) << "Unable to read registry key at path (HKLM & HKCU): "
97 << key_path << ".";
98 continue;
[email protected]8e4560b62011-01-14 10:09:1499 }
[email protected]b967a1b2011-08-17 14:39:58100
brettwfce8d192015-08-10 19:07:51101 std::string id = base::ToLowerASCII(base::UTF16ToASCII(*it));
[email protected]fdd28372014-08-21 02:27:26102 if (!crx_file::id_util::IdIsValid(id)) {
[email protected]5873a5a2013-12-16 19:53:00103 LOG(ERROR) << "Invalid id value " << id
104 << " for key " << key_path << ".";
105 continue;
106 }
107
[email protected]d8fd0fd2014-03-24 13:16:06108 base::string16 extension_dist_id;
109 if (key.ReadValue(kRegistryExtensionInstallParam, &extension_dist_id) ==
110 ERROR_SUCCESS) {
[email protected]2b6a5802014-08-16 07:58:08111 prefs->SetString(MakePrefName(id, ExternalProviderImpl::kInstallParam),
[email protected]d8fd0fd2014-03-24 13:16:06112 base::UTF16ToASCII(extension_dist_id));
113 }
114
[email protected]5873a5a2013-12-16 19:53:00115 // If there is an update URL present, copy it to prefs and ignore
116 // path and version keys for this entry.
117 base::string16 extension_update_url;
118 if (key.ReadValue(kRegistryExtensionUpdateUrl, &extension_update_url)
119 == ERROR_SUCCESS) {
120 prefs->SetString(
[email protected]2b6a5802014-08-16 07:58:08121 MakePrefName(id, ExternalProviderImpl::kExternalUpdateUrl),
[email protected]74f778e2014-03-14 21:11:46122 base::UTF16ToASCII(extension_update_url));
[email protected]5873a5a2013-12-16 19:53:00123 continue;
124 }
125
[email protected]439f1e32013-12-09 20:09:09126 base::string16 extension_path_str;
[email protected]b967a1b2011-08-17 14:39:58127 if (key.ReadValue(kRegistryExtensionPath, &extension_path_str)
128 != ERROR_SUCCESS) {
129 // TODO(erikkay): find a way to get this into about:extensions
130 LOG(ERROR) << "Missing value " << kRegistryExtensionPath
131 << " for key " << key_path << ".";
132 continue;
133 }
134
[email protected]650b2d52013-02-10 03:41:45135 base::FilePath extension_path(extension_path_str);
[email protected]b967a1b2011-08-17 14:39:58136 if (!extension_path.IsAbsolute()) {
137 LOG(ERROR) << "File path " << extension_path_str
138 << " needs to be absolute in key "
139 << key_path;
140 continue;
141 }
142
[email protected]7567484142013-07-11 17:36:07143 if (!base::PathExists(extension_path)) {
[email protected]b967a1b2011-08-17 14:39:58144 LOG(ERROR) << "File " << extension_path_str
145 << " for key " << key_path
146 << " does not exist or is not readable.";
147 continue;
148 }
149
150 if (!CanOpenFileForReading(extension_path)) {
151 LOG(ERROR) << "File " << extension_path_str
152 << " for key " << key_path << " can not be read. "
153 << "Check that users who should have the extension "
154 << "installed have permission to read it.";
155 continue;
156 }
157
[email protected]439f1e32013-12-09 20:09:09158 base::string16 extension_version;
[email protected]b967a1b2011-08-17 14:39:58159 if (key.ReadValue(kRegistryExtensionVersion, &extension_version)
160 != ERROR_SUCCESS) {
161 // TODO(erikkay): find a way to get this into about:extensions
162 LOG(ERROR) << "Missing value " << kRegistryExtensionVersion
163 << " for key " << key_path << ".";
164 continue;
165 }
166
pwnallcbd73192016-08-22 18:59:17167 base::Version version(base::UTF16ToASCII(extension_version));
[email protected]12126d372012-07-11 18:40:53168 if (!version.IsValid()) {
[email protected]b967a1b2011-08-17 14:39:58169 LOG(ERROR) << "Invalid version value " << extension_version
170 << " for key " << key_path << ".";
171 continue;
172 }
173
174 prefs->SetString(
[email protected]2b6a5802014-08-16 07:58:08175 MakePrefName(id, ExternalProviderImpl::kExternalVersion),
[email protected]74f778e2014-03-14 21:11:46176 base::UTF16ToASCII(extension_version));
[email protected]b967a1b2011-08-17 14:39:58177 prefs->SetString(
[email protected]2b6a5802014-08-16 07:58:08178 MakePrefName(id, ExternalProviderImpl::kExternalCrx),
[email protected]b967a1b2011-08-17 14:39:58179 extension_path_str);
[email protected]2b6a5802014-08-16 07:58:08180 prefs->SetBoolean(
181 MakePrefName(id, ExternalProviderImpl::kMayBeUntrusted),
182 true);
[email protected]8e4560b62011-01-14 10:09:14183 }
184
lazyboye8634172016-01-28 00:10:48185 return prefs;
186}
187
188void ExternalRegistryLoader::LoadOnFileThread() {
189 base::TimeTicks start_time = base::TimeTicks::Now();
dchengc963c7142016-04-08 03:55:22190 std::unique_ptr<base::DictionaryValue> initial_prefs =
191 LoadPrefsOnFileThread();
lazyboye8634172016-01-28 00:10:48192 prefs_.reset(initial_prefs.release());
asvitkinec0fb8022014-08-26 04:39:35193 LOCAL_HISTOGRAM_TIMES("Extensions.ExternalRegistryLoaderWin",
194 base::TimeTicks::Now() - start_time);
[email protected]8e4560b62011-01-14 10:09:14195 BrowserThread::PostTask(
196 BrowserThread::UI, FROM_HERE,
lazyboye8634172016-01-28 00:10:48197 base::Bind(&ExternalRegistryLoader::CompleteLoadAndStartWatchingRegistry,
198 this));
199}
200
201void ExternalRegistryLoader::CompleteLoadAndStartWatchingRegistry() {
202 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
203 LoadFinished();
204
205 // Start watching registry.
206 if (hklm_key_.Create(HKEY_LOCAL_MACHINE, kRegistryExtensions,
207 KEY_NOTIFY | KEY_WOW64_32KEY) == ERROR_SUCCESS) {
208 base::win::RegKey::ChangeCallback callback =
209 base::Bind(&ExternalRegistryLoader::OnRegistryKeyChanged,
210 base::Unretained(this), base::Unretained(&hklm_key_));
211 hklm_key_.StartWatching(callback);
212 } else {
213 LOG(WARNING) << "Error observing HKLM.";
214 }
215
216 if (hkcu_key_.Create(HKEY_CURRENT_USER, kRegistryExtensions, KEY_NOTIFY) ==
217 ERROR_SUCCESS) {
218 base::win::RegKey::ChangeCallback callback =
219 base::Bind(&ExternalRegistryLoader::OnRegistryKeyChanged,
220 base::Unretained(this), base::Unretained(&hkcu_key_));
221 hkcu_key_.StartWatching(callback);
222 } else {
223 LOG(WARNING) << "Error observing HKCU.";
224 }
225}
226
227void ExternalRegistryLoader::OnRegistryKeyChanged(base::win::RegKey* key) {
228 // |OnRegistryKeyChanged| is removed as an observer when the ChangeCallback is
229 // called, so we need to re-register.
230 key->StartWatching(base::Bind(&ExternalRegistryLoader::OnRegistryKeyChanged,
231 base::Unretained(this), base::Unretained(key)));
232
233 BrowserThread::PostTask(
234 BrowserThread::FILE, FROM_HERE,
235 base::Bind(&ExternalRegistryLoader::UpdatePrefsOnFileThread, this));
236}
237
238void ExternalRegistryLoader::UpdatePrefsOnFileThread() {
239 CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
240 base::TimeTicks start_time = base::TimeTicks::Now();
dchengc963c7142016-04-08 03:55:22241 std::unique_ptr<base::DictionaryValue> prefs = LoadPrefsOnFileThread();
lazyboye8634172016-01-28 00:10:48242 LOCAL_HISTOGRAM_TIMES("Extensions.ExternalRegistryLoaderWinUpdate",
243 base::TimeTicks::Now() - start_time);
244 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
245 base::Bind(&ExternalRegistryLoader::OnUpdated, this,
246 base::Passed(&prefs)));
[email protected]8e4560b62011-01-14 10:09:14247}
[email protected]5df038b2012-07-16 19:03:27248
249} // namespace extensions