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