Home | History | Annotate | Download | only in extensions
      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