#include "chrome/browser/component_updater/recovery_improved_component_installer.h"
#include "base/task/sequenced_task_runner.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
#include <iterator>
#include <tuple>
#include <utility>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/process/launch.h"
#include "base/process/process.h"
#include "base/sequence_checker.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/component_updater/component_updater_utils.h"
#include "components/services/unzip/content/unzip_service.h"
#include "components/update_client/patcher.h"
#include "components/update_client/unpacker.h"
#include "components/update_client/unzip/unzip_impl.h"
#if BUILDFLAG(IS_POSIX)
#include <errno.h>
#endif
namespace component_updater {
constexpr base::TaskTraits
RecoveryComponentActionHandler::kThreadPoolTaskTraits;
constexpr base::TaskTraits
RecoveryComponentActionHandler::kThreadPoolTaskTraitsRunCommand;
RecoveryComponentActionHandler::RecoveryComponentActionHandler(
const std::vector<uint8_t>& key_hash,
crx_file::VerifierFormat verifier_format)
: key_hash_(key_hash), verifier_format_(verifier_format) {}
RecoveryComponentActionHandler::~RecoveryComponentActionHandler() = default;
void RecoveryComponentActionHandler::Handle(const base::FilePath& action,
const std::string& session_id,
Callback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
crx_path_ = action;
session_id_ = session_id;
callback_ = std::move(callback);
base::ThreadPool::CreateSequencedTaskRunner(kThreadPoolTaskTraits)
->PostTask(
FROM_HERE,
component_updater::IsPerUserInstall()
? base::BindOnce(&RecoveryComponentActionHandler::Unpack, this)
: base::BindOnce(&RecoveryComponentActionHandler::Elevate, this,
std::move(callback_)));
}
void RecoveryComponentActionHandler::Unpack() {
auto unzipper = base::MakeRefCounted<update_client::UnzipChromiumFactory>(
base::BindRepeating(&unzip::LaunchUnzipper))
->Create();
update_client::Unpacker::Unpack(
key_hash_, crx_path_, std::move(unzipper), verifier_format_,
base::BindOnce(&RecoveryComponentActionHandler::UnpackComplete, this));
}
void RecoveryComponentActionHandler::UnpackComplete(
const update_client::Unpacker::Result& result) {
if (result.error != update_client::UnpackerError::kNone) {
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback_), false,
static_cast<int>(result.error), result.extended_error));
return;
}
unpack_path_ = result.unpack_path;
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&RecoveryComponentActionHandler::RunCommand,
this, MakeCommandLine(result.unpack_path)));
}
void RecoveryComponentActionHandler::RunCommand(
const base::CommandLine& cmdline) {
PrepareFiles(unpack_path_);
VLOG(1) << "run command: " << cmdline.GetCommandLineString();
auto process_or_error = [&cmdline]() -> base::expected<base::Process, int> {
base::LaunchOptions options;
#if BUILDFLAG(IS_WIN)
options.start_hidden = true;
#endif
base::Process process = base::LaunchProcess(cmdline, options);
if (!process.IsValid()) {
#if BUILDFLAG(IS_WIN)
return base::unexpected(::GetLastError());
#elif BUILDFLAG(IS_POSIX)
return base::unexpected(errno);
#else
return base::unexpected(0);
#endif
}
return std::move(process);
}();
base::ThreadPool::PostTask(
FROM_HERE, kThreadPoolTaskTraitsRunCommand,
base::BindOnce(&RecoveryComponentActionHandler::WaitForCommand, this,
std::move(process_or_error)));
}
void RecoveryComponentActionHandler::WaitForCommand(
base::expected<base::Process, int> process_or_error) {
int exit_code = 0;
int extra_code1 = 0;
bool succeeded = false;
constexpr base::TimeDelta kMaxWaitTime = base::Seconds(600);
if (process_or_error.has_value()) {
succeeded =
process_or_error->WaitForExitWithTimeout(kMaxWaitTime, &exit_code);
} else {
exit_code =
static_cast<int>(update_client::InstallError::LAUNCH_PROCESS_FAILED);
extra_code1 = process_or_error.error();
}
base::DeletePathRecursively(unpack_path_);
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback_), succeeded, exit_code, extra_code1));
}
constexpr uint8_t kRecoveryImprovedPublicKeySHA256[32] = {
0x87, 0xdb, 0x24, 0xde, 0x24, 0x76, 0x30, 0x46, 0x3c, 0x71, 0x83,
0x97, 0xd7, 0x32, 0x75, 0xcc, 0xd5, 0x7f, 0xec, 0x09, 0x60, 0x6d,
0x20, 0xc3, 0x81, 0xd7, 0xce, 0x7b, 0x10, 0x15, 0x44, 0xd1};
bool RecoveryImprovedInstallerPolicy::
SupportsGroupPolicyEnabledComponentUpdates() const {
return true;
}
bool RecoveryImprovedInstallerPolicy::RequiresNetworkEncryption() const {
return false;
}
update_client::CrxInstaller::Result
RecoveryImprovedInstallerPolicy::OnCustomInstall(
const base::Value::Dict& manifest,
const base::FilePath& install_dir) {
return update_client::CrxInstaller::Result(0);
}
void RecoveryImprovedInstallerPolicy::OnCustomUninstall() {}
void RecoveryImprovedInstallerPolicy::ComponentReady(
const base::Version& version,
const base::FilePath& install_dir,
base::Value::Dict manifest) {
DVLOG(1) << "RecoveryImproved component is ready.";
}
bool RecoveryImprovedInstallerPolicy::VerifyInstallation(
const base::Value::Dict& manifest,
const base::FilePath& install_dir) const {
return true;
}
base::FilePath RecoveryImprovedInstallerPolicy::GetRelativeInstallDir() const {
return base::FilePath(FILE_PATH_LITERAL("RecoveryImproved"));
}
void RecoveryImprovedInstallerPolicy::GetHash(
std::vector<uint8_t>* hash) const {
hash->assign(std::begin(kRecoveryImprovedPublicKeySHA256),
std::end(kRecoveryImprovedPublicKeySHA256));
}
std::string RecoveryImprovedInstallerPolicy::GetName() const {
return "Chrome Improved Recovery";
}
update_client::InstallerAttributes
RecoveryImprovedInstallerPolicy::GetInstallerAttributes() const {
return {};
}
void RegisterRecoveryImprovedComponent(ComponentUpdateService* cus,
PrefService* prefs) {
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
DVLOG(1) << "Registering RecoveryImproved component.";
auto installer = base::MakeRefCounted<ComponentInstaller>(
std::make_unique<RecoveryImprovedInstallerPolicy>(prefs),
RecoveryComponentActionHandler::MakeActionHandler());
installer->Register(cus, base::OnceClosure());
#endif
}
}
#else
namespace component_updater {
void RegisterRecoveryImprovedComponent(ComponentUpdateService* cus,
PrefService* prefs) { … }
}
#endif