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