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