15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/external_registry_loader_win.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/bind.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h"
91320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/files/file_util.h"
10f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "base/files/scoped_file.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/metrics/histogram.h"
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h"
1303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "base/strings/stringprintf.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
15eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/version.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/registry.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/external_provider_impl.h"
2003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "components/crx_file/id_util.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using content::BrowserThread;
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The Registry subkey that contains information about external extensions.
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kRegistryExtensions[] = "Software\\Google\\Chrome\\Extensions";
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
30effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch// Registry value of the key that defines the installation parameter.
31effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochconst wchar_t kRegistryExtensionInstallParam[] = L"install_parameter";
32effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Registry value of the key that defines the path to the .crx file.
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t kRegistryExtensionPath[] = L"path";
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Registry value of that key that defines the current version of the .crx file.
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const wchar_t kRegistryExtensionVersion[] = L"version";
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Registry value of the key that defines an external update URL.
405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const wchar_t kRegistryExtensionUpdateUrl[] = L"update_url";
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool CanOpenFileForReading(const base::FilePath& path) {
43f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  base::ScopedFILE file_handle(base::OpenFile(path, "rb"));
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return file_handle.get() != NULL;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)std::string MakePrefName(const std::string& extension_id,
4803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)                         const std::string& pref_name) {
4903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  return base::StringPrintf("%s.%s", extension_id.c_str(), pref_name.c_str());
5003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
5103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace extensions {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExternalRegistryLoader::StartLoading() {
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserThread::PostTask(
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserThread::FILE, FROM_HERE,
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&ExternalRegistryLoader::LoadOnFileThread, this));
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void ExternalRegistryLoader::LoadOnFileThread() {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::TimeTicks start_time = base::TimeTicks::Now();
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_ptr<base::DictionaryValue> prefs(new base::DictionaryValue);
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // A map of IDs, to weed out duplicates between HKCU and HKLM.
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::set<base::string16> keys;
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::RegistryKeyIterator iterator_machine_key(
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      HKEY_LOCAL_MACHINE, base::ASCIIToWide(kRegistryExtensions).c_str());
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (; iterator_machine_key.Valid(); ++iterator_machine_key)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keys.insert(iterator_machine_key.Name());
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  base::win::RegistryKeyIterator iterator_user_key(
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      HKEY_CURRENT_USER, base::ASCIIToWide(kRegistryExtensions).c_str());
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (; iterator_user_key.Valid(); ++iterator_user_key)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    keys.insert(iterator_user_key.Name());
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Iterate over the keys found, first trying HKLM, then HKCU, as per Windows
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // policy conventions. We only fall back to HKCU if the HKLM key cannot be
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // opened, not if the data within the key is invalid, for example.
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (std::set<base::string16>::const_iterator it = keys.begin();
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)       it != keys.end(); ++it) {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base::win::RegKey key;
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::string16 key_path = base::ASCIIToWide(kRegistryExtensions);
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    key_path.append(L"\\");
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    key_path.append(*it);
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (key.Open(HKEY_LOCAL_MACHINE,
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 key_path.c_str(), KEY_READ) != ERROR_SUCCESS) {
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (key.Open(HKEY_CURRENT_USER,
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   key_path.c_str(), KEY_READ) != ERROR_SUCCESS) {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        LOG(ERROR) << "Unable to read registry key at path (HKLM & HKCU): "
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   << key_path << ".";
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    std::string id = base::UTF16ToASCII(*it);
996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    base::StringToLowerASCII(&id);
10003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    if (!crx_file::id_util::IdIsValid(id)) {
1015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      LOG(ERROR) << "Invalid id value " << id
1025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                 << " for key " << key_path << ".";
1035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
1045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
106effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    base::string16 extension_dist_id;
107effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    if (key.ReadValue(kRegistryExtensionInstallParam, &extension_dist_id) ==
108effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        ERROR_SUCCESS) {
10903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      prefs->SetString(MakePrefName(id, ExternalProviderImpl::kInstallParam),
110effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch                       base::UTF16ToASCII(extension_dist_id));
111effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
112effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // If there is an update URL present, copy it to prefs and ignore
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // path and version keys for this entry.
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::string16 extension_update_url;
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (key.ReadValue(kRegistryExtensionUpdateUrl, &extension_update_url)
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        == ERROR_SUCCESS) {
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      prefs->SetString(
11903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)          MakePrefName(id, ExternalProviderImpl::kExternalUpdateUrl),
120a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)          base::UTF16ToASCII(extension_update_url));
1215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      continue;
1225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
124a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::string16 extension_path_str;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (key.ReadValue(kRegistryExtensionPath, &extension_path_str)
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        != ERROR_SUCCESS) {
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(erikkay): find a way to get this into about:extensions
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Missing value " << kRegistryExtensionPath
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << " for key " << key_path << ".";
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    base::FilePath extension_path(extension_path_str);
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!extension_path.IsAbsolute()) {
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "File path " << extension_path_str
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << " needs to be absolute in key "
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << key_path;
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (!base::PathExists(extension_path)) {
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "File " << extension_path_str
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << " for key " << key_path
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << " does not exist or is not readable.";
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!CanOpenFileForReading(extension_path)) {
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "File " << extension_path_str
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << " for key " << key_path << " can not be read. "
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << "Check that users who should have the extension "
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << "installed have permission to read it.";
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
156a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    base::string16 extension_version;
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (key.ReadValue(kRegistryExtensionVersion, &extension_version)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        != ERROR_SUCCESS) {
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // TODO(erikkay): find a way to get this into about:extensions
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Missing value " << kRegistryExtensionVersion
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << " for key " << key_path << ".";
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    Version version(base::UTF16ToASCII(extension_version));
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!version.IsValid()) {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Invalid version value " << extension_version
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 << " for key " << key_path << ".";
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue;
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefs->SetString(
17303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        MakePrefName(id, ExternalProviderImpl::kExternalVersion),
174a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        base::UTF16ToASCII(extension_version));
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    prefs->SetString(
17603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        MakePrefName(id, ExternalProviderImpl::kExternalCrx),
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        extension_path_str);
17803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    prefs->SetBoolean(
17903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        MakePrefName(id, ExternalProviderImpl::kMayBeUntrusted),
18003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)        true);
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  prefs_.reset(prefs.release());
1841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  LOCAL_HISTOGRAM_TIMES("Extensions.ExternalRegistryLoaderWin",
1851320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci                        base::TimeTicks::Now() - start_time);
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  BrowserThread::PostTask(
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      BrowserThread::UI, FROM_HERE,
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base::Bind(&ExternalRegistryLoader::LoadFinished, this));
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace extensions
192