blob: b7ba96ced0268cea79eba942766d542f7f1aef85 [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "extensions/browser/updater/update_data_provider.h"
#include <optional>
#include <utility>
#include "base/base64.h"
#include "base/containers/to_vector.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/strings/string_util.h"
#include "base/task/thread_pool.h"
#include "components/crx_file/crx_verifier.h"
#include "components/update_client/utils.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "crypto/hash.h"
#include "extensions/browser/content_verifier/content_verifier.h"
#include "extensions/browser/disable_reason.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/install/crx_install_error.h"
#include "extensions/browser/updater/manifest_fetch_data.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/extension_urls.h"
#include "extensions/common/verifier_formats.h"
namespace extensions {
namespace {
using UpdateClientCallback = UpdateDataProvider::UpdateClientCallback;
void PostErrorTasks(const base::FilePath& unpacked_dir,
UpdateClientCallback update_client_callback) {
base::ThreadPool::PostTask(
FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()},
base::GetDeletePathRecursivelyCallback(unpacked_dir));
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(std::move(update_client_callback),
update_client::CrxInstaller::Result(
update_client::InstallError::GENERIC_ERROR)));
}
} // namespace
UpdateDataProvider::UpdateDataProvider(content::BrowserContext* browser_context)
: browser_context_(browser_context) {}
UpdateDataProvider::~UpdateDataProvider() = default;
void UpdateDataProvider::Shutdown() {
browser_context_ = nullptr;
}
void UpdateDataProvider::GetData(
bool install_immediately,
const ExtensionUpdateDataMap& update_crx_component,
const std::vector<std::string>& ids,
base::OnceCallback<
void(const std::vector<std::optional<update_client::CrxComponent>>&)>
callback) {
std::vector<std::optional<update_client::CrxComponent>> data;
if (!browser_context_) {
for (size_t i = 0; i < ids.size(); i++) {
data.push_back(std::nullopt);
}
std::move(callback).Run(data);
return;
}
const ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
const ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context_);
for (const auto& id : ids) {
const Extension* extension = registry->GetInstalledExtension(id);
data.push_back(extension ? std::make_optional<update_client::CrxComponent>()
: std::nullopt);
if (!extension) {
continue;
}
DCHECK_NE(0u, update_crx_component.count(id));
const ExtensionUpdateData& extension_data = update_crx_component.at(id);
auto& crx_component = data.back();
auto pubkey = base::Base64Decode(extension->public_key());
if (!pubkey) {
continue;
}
crx_component->pk_hash = base::ToVector(crypto::hash::Sha256(*pubkey));
crx_component->app_id =
update_client::GetCrxIdFromPublicKeyHash(crx_component->pk_hash);
if (extension_data.is_corrupt_reinstall) {
crx_component->version = base::Version("0.0.0.0");
} else {
crx_component->version =
extension_data.pending_version
? base::Version(*extension_data.pending_version)
: extension->version();
crx_component->fingerprint = extension_data.pending_fingerprint.value_or(
extension->DifferentialFingerprint());
}
bool allow_dev = extension_urls::GetWebstoreUpdateUrl() !=
extension_urls::GetDefaultWebstoreUpdateUrl();
crx_component->requires_network_encryption = !allow_dev;
crx_component->crx_format_requirement =
extension->from_webstore() ? GetWebstoreVerifierFormat(allow_dev)
: GetPolicyVerifierFormat();
crx_component->installer = base::MakeRefCounted<ExtensionInstaller>(
id, extension->path(), install_immediately,
base::BindRepeating(&UpdateDataProvider::RunInstallCallback, this));
if (!ExtensionsBrowserClient::Get()->IsExtensionEnabled(id,
browser_context_)) {
DisableReasonSet disable_reasons = extension_prefs->GetDisableReasons(id);
if (disable_reasons.empty() ||
disable_reasons.contains(disable_reason::DISABLE_UNKNOWN)) {
crx_component->disabled_reasons.push_back(0);
}
// We are only interested in valid disable reasons from here.
disable_reasons.erase(disable_reason::DISABLE_UNKNOWN);
for (int reason : disable_reasons) {
crx_component->disabled_reasons.push_back(reason);
}
}
crx_component->install_source = extension_data.is_corrupt_reinstall
? "reinstall"
: extension_data.install_source;
crx_component->install_location =
ManifestFetchData::GetSimpleLocationString(extension->location());
}
std::move(callback).Run(data);
}
void UpdateDataProvider::RunInstallCallback(
const ExtensionId& extension_id,
const std::string& public_key,
const base::FilePath& unpacked_dir,
bool install_immediately,
UpdateClientCallback update_client_callback) {
VLOG(3) << "UpdateDataProvider::RunInstallCallback " << extension_id << " "
<< public_key;
if (!browser_context_) {
PostErrorTasks(unpacked_dir, std::move(update_client_callback));
return;
}
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&UpdateDataProvider::InstallUpdateCallback, this,
extension_id, public_key, unpacked_dir,
install_immediately, std::move(update_client_callback)));
}
void UpdateDataProvider::InstallUpdateCallback(
const ExtensionId& extension_id,
const std::string& public_key,
const base::FilePath& unpacked_dir,
bool install_immediately,
UpdateClientCallback update_client_callback) {
if (!browser_context_) {
PostErrorTasks(unpacked_dir, std::move(update_client_callback));
return;
}
// Error codes are converted into integers and may collide with codes from
// other embedders. However, for any given extension ID, there should be only
// one embedder, so the server should be able to figure it out.
ExtensionSystem::Get(browser_context_)
->InstallUpdate(
extension_id, public_key, unpacked_dir, install_immediately,
base::BindOnce(
[](UpdateClientCallback callback,
const std::optional<CrxInstallError>& error) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
update_client::CrxInstaller::Result result(0);
if (error.has_value()) {
int detail =
error->type() ==
CrxInstallErrorType::SANDBOXED_UNPACKER_FAILURE
? static_cast<int>(error->sandbox_failure_detail())
: static_cast<int>(error->detail());
result = update_client::CrxInstaller::Result(
static_cast<int>(error->type()), detail);
}
std::move(callback).Run(result);
},
std::move(update_client_callback)));
}
} // namespace extensions