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 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 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 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