• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/extensions/external_registry_loader_win.h"
6 
7 #include "base/bind.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/files/scoped_file.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/time/time.h"
16 #include "base/values.h"
17 #include "base/version.h"
18 #include "base/win/registry.h"
19 #include "chrome/browser/extensions/external_provider_impl.h"
20 #include "components/crx_file/id_util.h"
21 #include "content/public/browser/browser_thread.h"
22 
23 using content::BrowserThread;
24 
25 namespace {
26 
27 // The Registry subkey that contains information about external extensions.
28 const char kRegistryExtensions[] = "Software\\Google\\Chrome\\Extensions";
29 
30 // Registry value of the key that defines the installation parameter.
31 const wchar_t kRegistryExtensionInstallParam[] = L"install_parameter";
32 
33 // Registry value of the key that defines the path to the .crx file.
34 const wchar_t kRegistryExtensionPath[] = L"path";
35 
36 // Registry value of that key that defines the current version of the .crx file.
37 const wchar_t kRegistryExtensionVersion[] = L"version";
38 
39 // Registry value of the key that defines an external update URL.
40 const wchar_t kRegistryExtensionUpdateUrl[] = L"update_url";
41 
CanOpenFileForReading(const base::FilePath & path)42 bool CanOpenFileForReading(const base::FilePath& path) {
43   base::ScopedFILE file_handle(base::OpenFile(path, "rb"));
44   return file_handle.get() != NULL;
45 }
46 
MakePrefName(const std::string & extension_id,const std::string & pref_name)47 std::string MakePrefName(const std::string& extension_id,
48                          const std::string& pref_name) {
49   return base::StringPrintf("%s.%s", extension_id.c_str(), pref_name.c_str());
50 }
51 
52 }  // namespace
53 
54 namespace extensions {
55 
StartLoading()56 void ExternalRegistryLoader::StartLoading() {
57   CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
58   BrowserThread::PostTask(
59       BrowserThread::FILE, FROM_HERE,
60       base::Bind(&ExternalRegistryLoader::LoadOnFileThread, this));
61 }
62 
LoadOnFileThread()63 void ExternalRegistryLoader::LoadOnFileThread() {
64   CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
65   base::TimeTicks start_time = base::TimeTicks::Now();
66   scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
67 
68   // A map of IDs, to weed out duplicates between HKCU and HKLM.
69   std::set<base::string16> keys;
70   base::win::RegistryKeyIterator iterator_machine_key(
71       HKEY_LOCAL_MACHINE, base::ASCIIToWide(kRegistryExtensions).c_str());
72   for (; iterator_machine_key.Valid(); ++iterator_machine_key)
73     keys.insert(iterator_machine_key.Name());
74   base::win::RegistryKeyIterator iterator_user_key(
75       HKEY_CURRENT_USER, base::ASCIIToWide(kRegistryExtensions).c_str());
76   for (; iterator_user_key.Valid(); ++iterator_user_key)
77     keys.insert(iterator_user_key.Name());
78 
79   // Iterate over the keys found, first trying HKLM, then HKCU, as per Windows
80   // policy conventions. We only fall back to HKCU if the HKLM key cannot be
81   // opened, not if the data within the key is invalid, for example.
82   for (std::set<base::string16>::const_iterator it = keys.begin();
83        it != keys.end(); ++it) {
84     base::win::RegKey key;
85     base::string16 key_path = base::ASCIIToWide(kRegistryExtensions);
86     key_path.append(L"\\");
87     key_path.append(*it);
88     if (key.Open(HKEY_LOCAL_MACHINE,
89                  key_path.c_str(), KEY_READ) != ERROR_SUCCESS) {
90       if (key.Open(HKEY_CURRENT_USER,
91                    key_path.c_str(), KEY_READ) != ERROR_SUCCESS) {
92         LOG(ERROR) << "Unable to read registry key at path (HKLM & HKCU): "
93                    << key_path << ".";
94         continue;
95       }
96     }
97 
98     std::string id = base::UTF16ToASCII(*it);
99     base::StringToLowerASCII(&id);
100     if (!crx_file::id_util::IdIsValid(id)) {
101       LOG(ERROR) << "Invalid id value " << id
102                  << " for key " << key_path << ".";
103       continue;
104     }
105 
106     base::string16 extension_dist_id;
107     if (key.ReadValue(kRegistryExtensionInstallParam, &extension_dist_id) ==
108         ERROR_SUCCESS) {
109       prefs->SetString(MakePrefName(id, ExternalProviderImpl::kInstallParam),
110                        base::UTF16ToASCII(extension_dist_id));
111     }
112 
113     // If there is an update URL present, copy it to prefs and ignore
114     // path and version keys for this entry.
115     base::string16 extension_update_url;
116     if (key.ReadValue(kRegistryExtensionUpdateUrl, &extension_update_url)
117         == ERROR_SUCCESS) {
118       prefs->SetString(
119           MakePrefName(id, ExternalProviderImpl::kExternalUpdateUrl),
120           base::UTF16ToASCII(extension_update_url));
121       continue;
122     }
123 
124     base::string16 extension_path_str;
125     if (key.ReadValue(kRegistryExtensionPath, &extension_path_str)
126         != ERROR_SUCCESS) {
127       // TODO(erikkay): find a way to get this into about:extensions
128       LOG(ERROR) << "Missing value " << kRegistryExtensionPath
129                  << " for key " << key_path << ".";
130       continue;
131     }
132 
133     base::FilePath extension_path(extension_path_str);
134     if (!extension_path.IsAbsolute()) {
135       LOG(ERROR) << "File path " << extension_path_str
136                  << " needs to be absolute in key "
137                  << key_path;
138       continue;
139     }
140 
141     if (!base::PathExists(extension_path)) {
142       LOG(ERROR) << "File " << extension_path_str
143                  << " for key " << key_path
144                  << " does not exist or is not readable.";
145       continue;
146     }
147 
148     if (!CanOpenFileForReading(extension_path)) {
149       LOG(ERROR) << "File " << extension_path_str
150                  << " for key " << key_path << " can not be read. "
151                  << "Check that users who should have the extension "
152                  << "installed have permission to read it.";
153       continue;
154     }
155 
156     base::string16 extension_version;
157     if (key.ReadValue(kRegistryExtensionVersion, &extension_version)
158         != ERROR_SUCCESS) {
159       // TODO(erikkay): find a way to get this into about:extensions
160       LOG(ERROR) << "Missing value " << kRegistryExtensionVersion
161                  << " for key " << key_path << ".";
162       continue;
163     }
164 
165     Version version(base::UTF16ToASCII(extension_version));
166     if (!version.IsValid()) {
167       LOG(ERROR) << "Invalid version value " << extension_version
168                  << " for key " << key_path << ".";
169       continue;
170     }
171 
172     prefs->SetString(
173         MakePrefName(id, ExternalProviderImpl::kExternalVersion),
174         base::UTF16ToASCII(extension_version));
175     prefs->SetString(
176         MakePrefName(id, ExternalProviderImpl::kExternalCrx),
177         extension_path_str);
178     prefs->SetBoolean(
179         MakePrefName(id, ExternalProviderImpl::kMayBeUntrusted),
180         true);
181   }
182 
183   prefs_.reset(prefs.release());
184   LOCAL_HISTOGRAM_TIMES("Extensions.ExternalRegistryLoaderWin",
185                         base::TimeTicks::Now() - start_time);
186   BrowserThread::PostTask(
187       BrowserThread::UI, FROM_HERE,
188       base::Bind(&ExternalRegistryLoader::LoadFinished, this));
189 }
190 
191 }  // namespace extensions
192