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 42 bool CanOpenFileForReading(const base::FilePath& path) { 43 base::ScopedFILE file_handle(base::OpenFile(path, "rb")); 44 return file_handle.get() != NULL; 45 } 46 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 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 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