blob: d9c2c03b511e0d3bbff54119738217a6449c37f7 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/chrome_cleaner/engines/controllers/uwe_engine_cleaner_wrapper.h"
#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/barrier_closure.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/important_file_writer.h"
#include "base/json/json_string_value_serializer.h"
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/win/registry.h"
#include "chrome/chrome_cleaner/chrome_utils/extensions_util.h"
#include "chrome/chrome_cleaner/chrome_utils/force_installed_extension.h"
#include "chrome/chrome_cleaner/os/system_util.h"
namespace chrome_cleaner {
using base::ImportantFileWriter;
using ExtensionFilePath = base::string16;
using ExtensionRegistryPath = base::string16;
namespace {
// TODO(joenotcharles): Use RegKeyPath instead.
struct RegistryKey {
HKEY hkey;
const wchar_t* path;
};
const wchar_t kExtensionSettingsRegistryEntryName[] = L"ExtensionSettings";
const RegistryKey extension_forcelist_keys[] = {
{HKEY_LOCAL_MACHINE, kChromePoliciesKeyPath},
{HKEY_CURRENT_USER, kChromePoliciesKeyPath}};
bool RemoveExtensionSettingsPoliciesExtensionForAccessMask(
REGSAM access_mask,
ContentType content_type,
const base::string16& serialized_json) {
for (size_t i = 0; i < base::size(extension_forcelist_keys); ++i) {
base::win::RegKey key(extension_forcelist_keys[i].hkey,
extension_forcelist_keys[i].path, access_mask);
if (!WriteRegistryValue(kExtensionSettingsRegistryEntryName,
serialized_json, content_type, &key)) {
return false;
}
}
return true;
}
} // namespace
UwEEngineCleanerWrapper::UwEEngineCleanerWrapper(
std::unique_ptr<Cleaner> cleaner,
DisableExtensionsCallback disable_extensions_callback,
ChromePromptIPC* chrome_prompt_ipc)
: cleaner_(std::move(cleaner)),
disable_extensions_callback_(std::move(disable_extensions_callback)) {
chrome_prompt_ipc_ = chrome_prompt_ipc;
DCHECK(cleaner_);
}
UwEEngineCleanerWrapper::~UwEEngineCleanerWrapper() = default;
void UwEEngineCleanerWrapper::RemovePUPExtensions(
const std::vector<UwSId>& pup_ids) {
extension_removal_result_ = RESULT_CODE_FAILED;
base::ScopedClosureRunner done_closure(task_barrier_closure_);
// The modified JSON contents that we build up until we write out the contents
// at the end.
std::map<ExtensionFilePath, base::Value> default_extension_json;
std::map<ExtensionRegistryPath, std::pair<base::Value, ContentType>>
extension_settings_policies_json;
std::map<ExtensionFilePath, base::Value> master_preferences_json;
// Filter out any duplicates by extension id so we don't remove them twice.
std::set<ForceInstalledExtension, ExtensionIDCompare> extensions;
for (const UwSId& pup_id : pup_ids) {
PUPData::PUP* pup = PUPData::GetPUP(pup_id);
DCHECK(pup);
extensions.insert(pup->matched_extensions.begin(),
pup->matched_extensions.end());
}
std::vector<base::string16> master_preferences_extensions;
for (const ForceInstalledExtension& extension : extensions) {
switch (extension.install_method) {
case DEFAULT_APPS_EXTENSION: {
auto entry =
default_extension_json.find(extension.policy_file->path.value());
if (entry == default_extension_json.end()) {
base::Value new_json = extension.policy_file->json->data.Clone();
entry = default_extension_json
.insert({extension.policy_file->path.value(),
std::move(new_json)})
.first;
}
if (!RemoveDefaultExtension(extension, &entry->second)) {
LOG(ERROR) << "Could not remove default extension "
<< extension.id.AsString();
return;
}
break;
}
case POLICY_EXTENSION_FORCELIST: {
if (!RemoveForcelistPolicyExtension(extension)) {
LOG(ERROR) << "Could not remove Forcelist Policy extension "
<< extension.id.AsString();
return;
}
break;
}
case POLICY_EXTENSION_SETTINGS: {
auto entry = extension_settings_policies_json.find(
extension.policy_registry_entry->path);
if (entry == extension_settings_policies_json.end()) {
DCHECK(extension.policy_registry_entry);
DCHECK(extension.policy_registry_entry->json);
base::Value new_json =
extension.policy_registry_entry->json->data.Clone();
std::pair<base::Value, ContentType> entry_value =
std::make_pair(std::move(new_json),
extension.policy_registry_entry->content_type);
entry = extension_settings_policies_json
.insert({extension.policy_registry_entry->path,
std::move(entry_value)})
.first;
}
base::Value* json_entry = &entry->second.first;
if (!RemoveExtensionSettingsPoliciesExtension(extension, json_entry)) {
LOG(ERROR) << "Could not remove Policy Settings extension "
<< extension.id.AsString();
return;
}
break;
}
case POLICY_MASTER_PREFERENCES: {
auto entry =
master_preferences_json.find(extension.policy_file->path.value());
if (entry == master_preferences_json.end()) {
base::Value new_json = extension.policy_file->json->data.Clone();
entry = master_preferences_json
.insert({extension.policy_file->path.value(),
std::move(new_json)})
.first;
}
if (!RemoveMasterPreferencesExtension(extension, &entry->second)) {
LOG(ERROR) << "Could not remove master preferences extension "
<< extension.id.AsString();
return;
}
master_preferences_extensions.push_back(
base::UTF8ToUTF16(extension.id.AsString()));
break;
}
case INSTALL_METHOD_UNSPECIFIED:
default: {
LOG(ERROR) << "Unknown extension force install method "
<< extension.install_method;
return;
}
}
}
for (const auto& entry : default_extension_json) {
std::string serialized_json;
JSONStringValueSerializer serializer(&serialized_json);
if (!serializer.Serialize(entry.second)) {
LOG(ERROR) << "Could not serialize json";
return;
}
if (!ImportantFileWriter::WriteFileAtomically(base::FilePath(entry.first),
serialized_json, "")) {
LOG(ERROR) << "Could not write default extensions json to file "
<< SanitizePath(base::FilePath(entry.first));
return;
}
}
for (const auto& entry : extension_settings_policies_json) {
std::string serialized_json;
const base::Value& json = entry.second.first;
ContentType content_type = entry.second.second;
JSONStringValueSerializer serializer(&serialized_json);
if (!serializer.Serialize(json)) {
LOG(ERROR) << "Could not serialize json";
return;
}
base::string16 serialized_json16 = base::UTF8ToUTF16(serialized_json);
if (!RemoveExtensionSettingsPoliciesExtensionForAccessMask(
KEY_WOW64_32KEY | KEY_WRITE, content_type, serialized_json16)) {
LOG(ERROR) << "Could not remove extension settings from registry";
return;
}
if (IsX64Architecture()) {
if (!RemoveExtensionSettingsPoliciesExtensionForAccessMask(
KEY_WOW64_64KEY | KEY_WRITE, content_type, serialized_json16)) {
LOG(ERROR) << "Could not remove extension settings from registry";
return;
}
}
}
for (const auto& entry : master_preferences_json) {
std::string serialized_json;
JSONStringValueSerializer serializer(&serialized_json);
if (!serializer.Serialize(entry.second)) {
LOG(ERROR) << "Could not serialize json";
return;
}
if (!ImportantFileWriter::WriteFileAtomically(base::FilePath(entry.first),
serialized_json, "")) {
LOG(ERROR) << "Could not write master preferences json to file "
<< SanitizePath(base::FilePath(entry.first));
return;
}
}
// avoid the round trip if there are no master preferences extensions
// to remove.
if (master_preferences_extensions.size() > 0) {
done_closure.ReplaceClosure(base::DoNothing());
std::move(disable_extensions_callback_)
.Run(const_cast<std::vector<base::string16>&>(
master_preferences_extensions),
base::BindOnce(&UwEEngineCleanerWrapper::DisableExtensionDone,
base::Unretained(this)));
} else {
extension_removal_result_ = RESULT_CODE_SUCCESS;
}
}
void UwEEngineCleanerWrapper::DisableExtensionDone(bool result) {
if (result) {
extension_removal_result_ = RESULT_CODE_SUCCESS;
}
task_barrier_closure_.Run();
}
void UwEEngineCleanerWrapper::OnTotallyDone(
scoped_refptr<base::SequencedTaskRunner> task_runner) {
is_totally_done_ = true;
ResultCode final_result = uws_removal_result_ != RESULT_CODE_SUCCESS
? uws_removal_result_
: extension_removal_result_;
task_runner->PostTask(
FROM_HERE, base::BindOnce(std::move(done_callback_), final_result));
}
void UwEEngineCleanerWrapper::OnDoneUwSCleanup(ResultCode status) {
uws_removal_result_ = status;
task_barrier_closure_.Run();
}
void UwEEngineCleanerWrapper::Start(const std::vector<UwSId>& pup_ids,
DoneCallback done_callback) {
is_totally_done_ = false;
done_callback_ = std::move(done_callback);
task_barrier_closure_ = base::BarrierClosure(
2, base::BindOnce(&UwEEngineCleanerWrapper::OnTotallyDone,
base::Unretained(this),
base::SequencedTaskRunnerHandle::Get()));
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
PostTask(FROM_HERE, {base::ThreadPool(), base::MayBlock()},
base::BindOnce(&UwEEngineCleanerWrapper::TryRemovePUPExtensions,
base::Unretained(this), pup_ids));
cleaner_->Start(pup_ids,
base::BindOnce(&UwEEngineCleanerWrapper::OnDoneUwSCleanup,
base::Unretained(this)));
}
void UwEEngineCleanerWrapper::TryRemovePUPExtensions(
const std::vector<UwSId>& pup_ids) {
if (chrome_prompt_ipc_) {
chrome_prompt_ipc_->TryDeleteExtensions(
base::BindOnce(&UwEEngineCleanerWrapper::RemovePUPExtensions,
base::Unretained(this), std::move(pup_ids)),
base::BindOnce(&UwEEngineCleanerWrapper::DisableExtensionDone,
base::Unretained(this), true));
} else {
DisableExtensionDone(true);
}
}
void UwEEngineCleanerWrapper::StartPostReboot(const std::vector<UwSId>& pup_ids,
DoneCallback done_callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
is_totally_done_ = true;
cleaner_->StartPostReboot(pup_ids, std::move(done_callback));
}
void UwEEngineCleanerWrapper::Stop() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
is_totally_done_ = true;
extension_removal_result_ = RESULT_CODE_FAILED;
cleaner_->Stop();
}
bool UwEEngineCleanerWrapper::IsCompletelyDone() const {
return cleaner_->IsCompletelyDone() && is_totally_done_;
}
bool UwEEngineCleanerWrapper::CanClean(const std::vector<UwSId>& pup_ids) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return cleaner_->CanClean(pup_ids);
}
} // namespace chrome_cleaner