// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/updater/app/app_server_win.h"
#include <regstr.h>
#include <wrl/module.h>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include "base/check.h"
#include "base/command_line.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/scoped_temp_dir.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/types/expected_macros.h"
#include "base/win/atl.h"
#include "base/win/registry.h"
#include "base/win/windows_types.h"
#include "chrome/installer/util/work_item_list.h"
#include "chrome/updater/app/server/win/update_service_internal_stub_win.h"
#include "chrome/updater/app/server/win/update_service_stub_win.h"
#include "chrome/updater/constants.h"
#include "chrome/updater/registration_data.h"
#include "chrome/updater/update_service.h"
#include "chrome/updater/update_service_internal.h"
#include "chrome/updater/updater_scope.h"
#include "chrome/updater/updater_version.h"
#include "chrome/updater/util/util.h"
#include "chrome/updater/util/win_util.h"
#include "chrome/updater/win/setup/setup_util.h"
#include "chrome/updater/win/setup/uninstall.h"
#include "chrome/updater/win/task_scheduler.h"
#include "chrome/updater/win/win_constants.h"
namespace updater {
namespace {
std::wstring GetCOMGroup(const std::wstring& prefix, UpdaterScope scope) {
return base::StrCat({prefix, base::ASCIIToWide(UpdaterScopeToString(scope))});
}
std::wstring COMGroup(UpdaterScope scope) {
return GetCOMGroup(L"Active", scope);
}
std::wstring COMGroupInternal(UpdaterScope scope) {
return GetCOMGroup(L"Internal", scope);
}
HRESULT AddAllowedAce(HANDLE object,
SE_OBJECT_TYPE object_type,
const CSid& sid,
ACCESS_MASK required_permissions,
UINT8 required_ace_flags) {
CDacl dacl;
if (!AtlGetDacl(object, object_type, &dacl)) {
return HRESULTFromLastError();
}
int ace_count = dacl.GetAceCount();
for (int i = 0; i < ace_count; ++i) {
CSid sid_entry;
ACCESS_MASK existing_permissions = 0;
BYTE existing_ace_flags = 0;
dacl.GetAclEntry(i, &sid_entry, &existing_permissions, NULL,
&existing_ace_flags);
if (sid_entry == sid &&
required_permissions == (existing_permissions & required_permissions) &&
required_ace_flags == (existing_ace_flags & ~INHERITED_ACE)) {
return S_OK;
}
}
if (!dacl.AddAllowedAce(sid, required_permissions, required_ace_flags) ||
!AtlSetDacl(object, object_type, dacl)) {
return HRESULTFromLastError();
}
return S_OK;
}
bool CreateClientStateMedium() {
base::win::RegKey key;
LONG result = key.Create(HKEY_LOCAL_MACHINE, CLIENT_STATE_MEDIUM_KEY,
Wow6432(KEY_WRITE));
if (result != ERROR_SUCCESS) {
VLOG(2) << __func__ << " failed: CreateKey returned " << result;
return false;
}
// Authenticated non-admins may read, write, create subkeys and values.
// The override privileges apply to all subkeys and values but not to the
// ClientStateMedium key itself.
HRESULT hr = AddAllowedAce(
key.Handle(), SE_REGISTRY_KEY, Sids::Interactive(),
KEY_READ | KEY_SET_VALUE | KEY_CREATE_SUB_KEY,
CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE);
if (FAILED(hr)) {
VLOG(2) << __func__ << " failed: AddAllowedAce returned " << hr;
return false;
}
return true;
}
// Install Updater.exe as GoogleUpdate.exe in the file system under
// Google\Update. And add a "pv" registry value under the
// UPDATER_KEY\Clients\{GoogleUpdateAppId}.
// Finally, update the registry value for the "UninstallCmdLine".
bool SwapGoogleUpdate(UpdaterScope scope,
const base::FilePath& updater_path,
const base::FilePath& temp_path,
HKEY root,
WorkItemList* list) {
CHECK(list);
const std::optional<base::FilePath> target_path =
GetGoogleUpdateExePath(scope);
if (!target_path || !base::CreateDirectory(target_path->DirName())) {
return false;
}
list->AddCopyTreeWorkItem(updater_path, *target_path, temp_path,
WorkItem::ALWAYS);
const std::wstring google_update_appid_key =
GetAppClientsKey(kLegacyGoogleUpdateAppID);
list->AddCreateRegKeyWorkItem(root, COMPANY_KEY, KEY_WOW64_32KEY);
list->AddCreateRegKeyWorkItem(root, UPDATER_KEY, KEY_WOW64_32KEY);
list->AddCreateRegKeyWorkItem(root, CLIENTS_KEY, KEY_WOW64_32KEY);
list->AddCreateRegKeyWorkItem(root, CLIENT_STATE_KEY, KEY_WOW64_32KEY);
list->AddCreateRegKeyWorkItem(root, google_update_appid_key, KEY_WOW64_32KEY);
list->AddCreateRegKeyWorkItem(
root, GetAppClientStateKey(kLegacyGoogleUpdateAppID), KEY_WOW64_32KEY);
list->AddSetRegValueWorkItem(root, google_update_appid_key, KEY_WOW64_32KEY,
kRegValuePV, kUpdaterVersionUtf16, true);
list->AddSetRegValueWorkItem(
root, GetAppClientStateKey(kLegacyGoogleUpdateAppID), KEY_WOW64_32KEY,
kRegValuePV, kUpdaterVersionUtf16, true);
list->AddSetRegValueWorkItem(
root, google_update_appid_key, KEY_WOW64_32KEY, kRegValueName,
base::ASCIIToWide(PRODUCT_FULLNAME_STRING), true);
list->AddSetRegValueWorkItem(
root, UPDATER_KEY, KEY_WOW64_32KEY, kRegValueUninstallCmdLine,
[scope, &updater_path] {
base::CommandLine uninstall_if_unused_command(updater_path);
uninstall_if_unused_command.AppendSwitch(kWakeSwitch);
if (IsSystemInstall(scope)) {
uninstall_if_unused_command.AppendSwitch(kSystemSwitch);
}
return uninstall_if_unused_command.GetCommandLineString();
}(),
true);
list->AddSetRegValueWorkItem(root, UPDATER_KEY, KEY_WOW64_32KEY,
kRegValueVersion, kUpdaterVersionUtf16, true);
return true;
}
// Uninstall the GoogleUpdate services, run values, scheduled tasks, and files.
bool UninstallGoogleUpdate(UpdaterScope scope) {
VLOG(2) << __func__;
if (IsSystemInstall(scope)) {
// Delete the GoogleUpdate services.
ForEachServiceWithPrefix(
kLegacyServiceNamePrefix, kLegacyServiceDisplayNamePrefix,
[](const std::wstring& service_name) {
VLOG(2) << __func__ << ": Deleting legacy service: " << service_name;
if (!DeleteService(service_name)) {
VLOG(1) << __func__
<< ": failed to delete service: " << service_name;
}
});
} else {
// Delete the GoogleUpdate run values.
ForEachRegistryRunValueWithPrefix(
kLegacyRunValuePrefix, [](const std::wstring& run_name) {
VLOG(2) << __func__ << ": Deleting legacy run value: " << run_name;
base::win::RegKey(HKEY_CURRENT_USER, REGSTR_PATH_RUN, KEY_WRITE)
.DeleteValue(run_name.c_str());
});
}
// Delete the GoogleUpdate tasks. This is a best-effort operation.
scoped_refptr<TaskScheduler> task_scheduler(
TaskScheduler::CreateInstance(scope, /*use_task_subfolders=*/false));
if (task_scheduler) {
task_scheduler->ForEachTaskWithPrefix(
IsSystemInstall(scope) ? kLegacyTaskNamePrefixSystem
: kLegacyTaskNamePrefixUser,
[&task_scheduler](const std::wstring& task_name) {
VLOG(2) << __func__ << ": Deleting legacy task: " << task_name;
bool is_deleted = task_scheduler->DeleteTask(task_name);
VLOG_IF(2, !is_deleted) << "Legacy task was not deleted.";
});
}
// Keep only `GoogleUpdate.exe` and nothing else under `\Google\Update`.
return DeleteExcept(GetGoogleUpdateExePath(scope));
}
} // namespace
HRESULT IsCOMCallerAllowed() {
if (!IsSystemInstall()) {
return S_OK;
}
ASSIGN_OR_RETURN(const bool result, IsCOMCallerAdmin(), [](HRESULT error) {
LOG(ERROR) << "IsCOMCallerAdmin failed: " << std::hex << error;
return error;
});
return result ? S_OK : E_ACCESSDENIED;
}
scoped_refptr<App> MakeAppServer() {
return GetAppServerWinInstance();
}
// Returns a leaky singleton instance of `AppServerWin`.
scoped_refptr<AppServerWin> GetAppServerWinInstance() {
static base::NoDestructor<scoped_refptr<AppServerWin>> app_server{
base::MakeRefCounted<AppServerWin>()};
return *app_server;
}
AppServerWin::AppServerWin() = default;
AppServerWin::~AppServerWin() {
NOTREACHED_IN_MIGRATION(); // The instance of this class is a leaky
// singleton.
}
void AppServerWin::PostRpcTask(base::OnceClosure task) {
GetAppServerWinInstance()->PostRpcTaskOnMainSequence(std::move(task));
}
void AppServerWin::Stop() {
VLOG(2) << __func__ << ": COM server is shutting down.";
UnregisterClassObjects();
main_task_runner_->PostTask(FROM_HERE, base::BindOnce([] {
scoped_refptr<AppServerWin> this_server =
GetAppServerWinInstance();
this_server->update_service_ = nullptr;
this_server->update_service_internal_ = nullptr;
this_server->Shutdown(0);
}));
}
void AppServerWin::PostRpcTaskOnMainSequence(base::OnceClosure task) {
main_task_runner_->PostTask(FROM_HERE, std::move(task));
}
HRESULT AppServerWin::RegisterClassObjects() {
// Register COM class objects that are under either the ActiveSystem or the
// ActiveUser group.
// See wrl_classes.cc for details on the COM classes within the group.
return Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::GetModule()
.RegisterObjects(COMGroup(updater_scope()).c_str());
}
HRESULT AppServerWin::RegisterInternalClassObjects() {
// Register COM class objects that are under either the InternalSystem or the
// InternalUser group.
// See wrl_classes.cc for details on the COM classes within the group.
return Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::GetModule()
.RegisterObjects(COMGroupInternal(updater_scope()).c_str());
}
void AppServerWin::UnregisterClassObjects() {
const HRESULT hr =
Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::GetModule()
.UnregisterObjects();
LOG_IF(ERROR, FAILED(hr)) << "UnregisterObjects failed; hr: " << hr;
}
void AppServerWin::CreateWRLModule() {
Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::Create(
this, &AppServerWin::Stop);
}
void AppServerWin::TaskStarted() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
const auto count =
Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::GetModule()
.IncrementObjectCount();
VLOG(2) << "Starting task, Microsoft::WRL::Module count: " << count;
AppServer::TaskStarted();
}
bool AppServerWin::ShutdownIfIdleAfterTask() {
return false;
}
void AppServerWin::OnDelayedTaskComplete() {
const auto count =
Microsoft::WRL::Module<Microsoft::WRL::OutOfProc>::GetModule()
.DecrementObjectCount();
VLOG(2) << "Completed task, Microsoft::WRL::Module count: " << count;
}
void AppServerWin::ActiveDuty(scoped_refptr<UpdateService> update_service) {
update_service_ = base::MakeRefCounted<UpdateServiceStubWin>(
std::move(update_service),
base::BindRepeating(&AppServerWin::TaskStarted, this),
base::BindRepeating(&AppServerWin::TaskCompleted, this));
Start(base::BindOnce(&AppServerWin::RegisterClassObjects,
base::Unretained(this)));
}
void AppServerWin::ActiveDutyInternal(
scoped_refptr<UpdateServiceInternal> update_service_internal) {
update_service_internal_ = base::MakeRefCounted<UpdateServiceInternalStubWin>(
std::move(update_service_internal),
base::BindRepeating(&AppServerWin::TaskStarted, this),
base::BindRepeating(&AppServerWin::TaskCompleted, this));
Start(base::BindOnce(&AppServerWin::RegisterInternalClassObjects,
base::Unretained(this)));
}
void AppServerWin::Start(base::OnceCallback<HRESULT()> register_callback) {
CreateWRLModule();
HRESULT hr = std::move(register_callback).Run();
if (FAILED(hr)) {
Shutdown(hr);
}
}
void AppServerWin::UninstallSelf() {
UninstallCandidate(updater_scope());
}
bool AppServerWin::SwapInNewVersion() {
std::unique_ptr<WorkItemList> list(WorkItem::CreateWorkItemList());
const std::optional<base::FilePath> versioned_directory =
GetVersionedInstallDirectory(updater_scope());
if (!versioned_directory) {
return false;
}
const base::FilePath updater_path =
versioned_directory->Append(GetExecutableRelativePath());
if (IsSystemInstall(updater_scope()) && !CreateClientStateMedium()) {
return false;
}
std::optional<base::ScopedTempDir> temp_dir = CreateSecureTempDir();
if (!temp_dir) {
return false;
}
if (!SwapGoogleUpdate(updater_scope(), updater_path, temp_dir->GetPath(),
UpdaterScopeToHKeyRoot(updater_scope()), list.get())) {
return false;
}
if (IsSystemInstall(updater_scope())) {
AddComServiceWorkItems(updater_path, false, list.get());
} else {
AddComServerWorkItems(updater_path, false, list.get());
}
const base::ScopedClosureRunner reset_shutdown_event(
SignalShutdownEvent(updater_scope()));
std::optional<base::FilePath> target =
GetGoogleUpdateExePath(updater_scope());
if (target) {
StopProcessesUnderPath(target->DirName(), base::Seconds(45));
}
if (!list->Do()) {
return false;
}
if (!UninstallGoogleUpdate(updater_scope())) {
VLOG(1) << "UninstallGoogleUpdate() failed.";
}
if (!IsSystemInstall(updater_scope())) {
if (!DeleteLegacyEntriesPerUser()) {
VLOG(1) << "DeleteLegacyEntriesPerUser() failed.";
}
}
return true;
}
void AppServerWin::RepairUpdater(UpdaterScope scope, bool is_internal) {
if (AreComInterfacesPresent(scope, is_internal)) {
return;
}
InstallComInterfaces(scope, is_internal);
}
} // namespace updater