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