#include "chrome/installer/setup/setup_util.h"
#include <objbase.h>
#include <windows.h>
#include <stddef.h>
#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <limits>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/cpu.h"
#include "base/files/file.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/version.h"
#include "base/win/registry.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "chrome/install_static/install_details.h"
#include "chrome/install_static/install_modes.h"
#include "chrome/install_static/install_util.h"
#include "chrome/installer/setup/installer_state.h"
#include "chrome/installer/setup/setup_constants.h"
#include "chrome/installer/setup/user_hive_visitor.h"
#include "chrome/installer/util/app_command.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/google_update_settings.h"
#include "chrome/installer/util/initial_preferences.h"
#include "chrome/installer/util/initial_preferences_constants.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/installation_state.h"
#include "chrome/installer/util/registry_util.h"
#include "chrome/installer/util/util_constants.h"
#include "chrome/installer/util/work_item.h"
#include "chrome/installer/util/work_item_list.h"
namespace installer {
namespace {
constexpr wchar_t kEventLogProvidersRegPath[] =
L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
void RemoveLegacyIExecuteCommandKey(const InstallerState& installer_state) {
const std::wstring handler_class_uuid =
install_static::GetLegacyCommandExecuteImplClsid();
if (handler_class_uuid.empty())
return;
const HKEY root = installer_state.root_key();
std::wstring delegate_execute_path(L"Software\\Classes\\CLSID\\");
delegate_execute_path.append(handler_class_uuid);
for (REGSAM bitness : {KEY_WOW64_32KEY, KEY_WOW64_64KEY})
installer::DeleteRegistryKey(root, delegate_execute_path, bitness);
}
void RemoveBinariesVersionKey(const InstallerState& installer_state) {
#if !BUILDFLAG(GOOGLE_CHROME_FOR_TESTING_BRANDING)
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
std::wstring path(install_static::GetClientsKeyPath(
L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}"));
#else
std::wstring path(L"Software\\Chromium Binaries");
#endif
installer::DeleteRegistryKey(installer_state.root_key(), path,
KEY_WOW64_32KEY);
#endif
}
void RemoveAppLauncherVersionKey(const InstallerState& installer_state) {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
static constexpr wchar_t kLauncherGuid[] =
L"{FDA71E6F-AC4C-4a00-8B70-9958A68906BF}";
installer::DeleteRegistryKey(installer_state.root_key(),
install_static::GetClientsKeyPath(kLauncherGuid),
KEY_WOW64_32KEY);
#endif
}
void RemoveLegacyChromeAppCommands(const InstallerState& installer_state) {
#if BUILDFLAG(GOOGLE_CHROME_BRANDING)
std::unique_ptr<WorkItemList> list(WorkItem::CreateWorkItemList());
AppCommand(L"install-extension", {})
.AddDeleteAppCommandWorkItems(installer_state.root_key(), list.get());
list->Do();
#endif
}
}
const char kUnPackStatusMetricsName[] = "Setup.Install.LzmaUnPackStatus";
base::Version* GetMaxVersionFromArchiveDir(const base::FilePath& chrome_path) {
VLOG(1) << "Looking for Chrome version folder under " << chrome_path.value();
base::FileEnumerator version_enum(chrome_path, false,
base::FileEnumerator::DIRECTORIES);
std::unique_ptr<base::Version> max_version(new base::Version("0.0.0.0"));
bool version_found = false;
while (!version_enum.Next().empty()) {
base::FileEnumerator::FileInfo find_data = version_enum.GetInfo();
VLOG(1) << "directory found: " << find_data.GetName().value();
std::unique_ptr<base::Version> found_version(
new base::Version(base::WideToASCII(find_data.GetName().value())));
if (found_version->IsValid() &&
found_version->CompareTo(*max_version.get()) > 0) {
max_version = std::move(found_version);
version_found = true;
}
}
return (version_found ? max_version.release() : nullptr);
}
bool DeleteFileFromTempProcess(const base::FilePath& path,
uint32_t delay_before_delete_ms) {
static const wchar_t kRunDll32Path[] =
L"%SystemRoot%\\System32\\rundll32.exe";
wchar_t rundll32[MAX_PATH];
DWORD size =
ExpandEnvironmentStrings(kRunDll32Path, rundll32, std::size(rundll32));
if (!size || size >= MAX_PATH)
return false;
STARTUPINFO startup = {sizeof(STARTUPINFO)};
PROCESS_INFORMATION pi = {0};
BOOL ok = ::CreateProcess(nullptr, rundll32, nullptr, nullptr, FALSE,
CREATE_SUSPENDED, nullptr, nullptr, &startup, &pi);
if (ok) {
size = static_cast<DWORD>((path.value().length() + 1) *
sizeof(path.value()[0]));
void* mem = ::VirtualAllocEx(pi.hProcess, nullptr, size, MEM_COMMIT,
PAGE_READWRITE);
if (mem) {
SIZE_T written = 0;
::WriteProcessMemory(pi.hProcess, mem, path.value().c_str(),
(path.value().size() + 1) * sizeof(path.value()[0]),
&written);
HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll");
PAPCFUNC sleep =
reinterpret_cast<PAPCFUNC>(::GetProcAddress(kernel32, "Sleep"));
PAPCFUNC delete_file =
reinterpret_cast<PAPCFUNC>(::GetProcAddress(kernel32, "DeleteFileW"));
PAPCFUNC exit_process =
reinterpret_cast<PAPCFUNC>(::GetProcAddress(kernel32, "ExitProcess"));
if (!sleep || !delete_file || !exit_process) {
NOTREACHED();
} else {
::QueueUserAPC(sleep, pi.hThread, delay_before_delete_ms);
::QueueUserAPC(delete_file, pi.hThread,
reinterpret_cast<ULONG_PTR>(mem));
::QueueUserAPC(exit_process, pi.hThread, 0);
::ResumeThread(pi.hThread);
}
} else {
PLOG(ERROR) << "VirtualAllocEx";
::TerminateProcess(pi.hProcess, ~static_cast<UINT>(0));
}
::CloseHandle(pi.hThread);
::CloseHandle(pi.hProcess);
}
return ok != FALSE;
}
bool AdjustThreadPriority() {
const DWORD priority_class = ::GetPriorityClass(::GetCurrentProcess());
if (priority_class == BELOW_NORMAL_PRIORITY_CLASS ||
priority_class == IDLE_PRIORITY_CLASS) {
const BOOL result =
::SetThreadPriority(::GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN);
PLOG_IF(WARNING, !result) << "Failed to enter background mode.";
return !!result;
}
if (priority_class == 0)
PLOG(WARNING) << "Failed to get the process's priority class.";
return false;
}
bool IsUninstallSuccess(InstallStatus install_status) {
return (install_status == UNINSTALL_SUCCESSFUL ||
install_status == UNINSTALL_REQUIRES_REBOOT);
}
bool ContainsUnsupportedSwitch(const base::CommandLine& cmd_line) {
static const char* const kLegacySwitches[] = {
"ready-mode",
"ready-mode-opt-in",
"ready-mode-temp-opt-out",
"ready-mode-end-temp-opt-out",
"quick-enable-cf",
"chrome-frame",
"migrate-chrome-frame",
"app-host",
"app-launcher",
};
for (size_t i = 0; i < std::size(kLegacySwitches); ++i) {
if (cmd_line.HasSwitch(UNSAFE_TODO(kLegacySwitches[i]))) {
return true;
}
}
return false;
}
bool IsProcessorSupported() {
#if defined(ARCH_CPU_X86_FAMILY)
return base::CPU().has_sse3();
#elif defined(ARCH_CPU_ARM64)
return true;
#else
#error Port
#endif
}
void DeleteRegistryKeyPartial(
HKEY root,
const std::wstring& path,
const std::vector<std::wstring>& keys_to_preserve) {
std::set<std::wstring> lowered_keys_to_preserve;
std::ranges::transform(
keys_to_preserve,
std::inserter(lowered_keys_to_preserve, lowered_keys_to_preserve.begin()),
[](const std::wstring& str) {
DCHECK(!str.empty());
DCHECK(base::IsStringASCII(str));
return base::ToLowerASCII(str);
});
base::win::RegKey key;
LONG result =
key.Open(root, path.c_str(),
(KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_SET_VALUE));
if (result != ERROR_SUCCESS) {
LOG_IF(ERROR, result != ERROR_FILE_NOT_FOUND)
<< "Failed to open " << path << "; result = " << result;
return;
}
std::set<std::wstring> to_skip;
DWORD index = 0;
const size_t kMaxKeyNameLength = 256;
std::wstring name(kMaxKeyNameLength, wchar_t());
bool did_delete = false;
while (true) {
DWORD name_length = base::saturated_cast<DWORD>(name.capacity());
name.resize(name_length);
result = ::RegEnumKeyEx(key.Handle(), index, &name[0], &name_length,
nullptr, nullptr, nullptr, nullptr);
if (result == ERROR_MORE_DATA) {
name.reserve(name.capacity() * 2);
continue;
}
if (result == ERROR_NO_MORE_ITEMS) {
if (!did_delete)
break;
did_delete = false;
index = 0;
continue;
}
if (result != ERROR_SUCCESS)
break;
name.resize(name_length);
if (to_skip.count(name)) {
++index;
continue;
}
if (base::IsStringASCII(name) &&
lowered_keys_to_preserve.count(base::ToLowerASCII(name))) {
to_skip.insert(name);
++index;
continue;
}
result = key.DeleteKey(name.c_str());
if (result != ERROR_SUCCESS) {
LOG(ERROR) << "Failed to delete subkey " << name << " under path "
<< path;
to_skip.insert(name);
++index;
continue;
}
did_delete = true;
}
if (to_skip.empty()) {
result = key.DeleteKey(L"");
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed to delete key " << path;
}
return;
}
to_skip.clear();
did_delete = false;
index = 0;
while (true) {
DWORD name_length = base::saturated_cast<int16_t>(name.capacity());
name.resize(name_length);
result = ::RegEnumValue(key.Handle(), index, &name[0], &name_length,
nullptr, nullptr, nullptr, nullptr);
if (result == ERROR_MORE_DATA) {
if (name_length <
static_cast<DWORD>(std::numeric_limits<int16_t>::max())) {
name.reserve(name.capacity() * 2);
continue;
}
break;
}
if (result == ERROR_NO_MORE_ITEMS) {
if (!did_delete)
break;
did_delete = false;
index = 0;
continue;
}
if (result != ERROR_SUCCESS)
break;
name.resize(name_length);
if (to_skip.count(name)) {
++index;
continue;
}
result = key.DeleteValue(name.c_str());
if (result != ERROR_SUCCESS) {
LOG(ERROR) << "Failed to delete value " << name << " in key " << path;
to_skip.insert(name);
++index;
continue;
}
did_delete = true;
}
}
bool IsDowngradeAllowed(const InitialPreferences& prefs) {
bool allow_downgrade = false;
return prefs.GetBool(initial_preferences::kAllowDowngrade,
&allow_downgrade) &&
allow_downgrade;
}
int GetInstallAge(const InstallerState& installer_state) {
base::File::Info info;
if (!base::GetFileInfo(installer_state.target_path(), &info))
return -1;
base::TimeDelta age = base::Time::Now() - info.creation_time;
return age >= base::TimeDelta() ? age.InDays() : -1;
}
void RecordUnPackMetrics(UnPackStatus unpack_status, UnPackConsumer consumer) {
std::string consumer_name;
switch (consumer) {
case UnPackConsumer::COMPRESSED_CHROME_ARCHIVE:
consumer_name = "CompressedChromeArchive";
break;
case UnPackConsumer::UNCOMPRESSED_CHROME_ARCHIVE:
consumer_name = "UncompressedChromeArchive";
break;
}
base::UmaHistogramExactLinear(
std::string(std::string(kUnPackStatusMetricsName) + "_" + consumer_name),
unpack_status, UNPACK_STATUS_COUNT);
}
void RegisterEventLogProvider(const base::FilePath& install_directory,
const base::Version& version) {
std::wstring reg_path(kEventLogProvidersRegPath);
reg_path.append(install_static::InstallDetails::Get().install_full_name());
VLOG(1) << "Registering Chrome's event log provider at " << reg_path;
std::unique_ptr<WorkItemList> work_item_list(WorkItem::CreateWorkItemList());
work_item_list->set_log_message("Register event log provider");
work_item_list->AddCreateRegKeyWorkItem(HKEY_LOCAL_MACHINE, reg_path,
WorkItem::kWow64Default);
work_item_list->AddSetRegValueWorkItem(
HKEY_LOCAL_MACHINE, reg_path, WorkItem::kWow64Default, L"CategoryCount",
static_cast<DWORD>(1), true);
work_item_list->AddSetRegValueWorkItem(
HKEY_LOCAL_MACHINE, reg_path, WorkItem::kWow64Default, L"TypesSupported",
static_cast<DWORD>(EVENTLOG_ERROR_TYPE | EVENTLOG_INFORMATION_TYPE |
EVENTLOG_WARNING_TYPE),
true);
const base::FilePath provider(
install_directory.AppendASCII(version.GetString())
.Append(FILE_PATH_LITERAL("eventlog_provider.dll")));
static constexpr const wchar_t* kFileKeys[] = {
L"CategoryMessageFile",
L"EventMessageFile",
L"ParameterMessageFile",
};
for (const wchar_t* file_key : kFileKeys) {
work_item_list->AddSetRegValueWorkItem(HKEY_LOCAL_MACHINE, reg_path,
WorkItem::kWow64Default, file_key,
provider.value(), true);
}
if (!work_item_list->Do())
work_item_list->Rollback();
}
void DeRegisterEventLogProvider() {
std::wstring reg_path(kEventLogProvidersRegPath);
reg_path.append(install_static::InstallDetails::Get().install_full_name());
installer::DeleteRegistryKey(HKEY_LOCAL_MACHINE, reg_path,
WorkItem::kWow64Default);
}
void DoLegacyCleanups(const InstallerState& installer_state,
InstallStatus install_status) {
if (InstallUtil::GetInstallReturnCode(install_status))
return;
RemoveLegacyIExecuteCommandKey(installer_state);
if (!install_static::InstallDetails::Get().is_primary_mode())
return;
RemoveBinariesVersionKey(installer_state);
RemoveAppLauncherVersionKey(installer_state);
RemoveLegacyChromeAppCommands(installer_state);
}
std::optional<std::string> DecodeDMTokenSwitchValue(
const std::wstring& encoded_token) {
if (encoded_token.empty()) {
LOG(ERROR) << "Empty DMToken specified on the command line";
return std::nullopt;
}
std::string token;
if (!base::IsStringASCII(encoded_token) ||
!base::Base64Decode(base::WideToASCII(encoded_token), &token)) {
LOG(ERROR) << "DMToken passed on the command line is not correctly encoded";
return std::nullopt;
}
return token;
}
std::optional<std::string> DecodeNonceSwitchValue(
const std::string& encoded_nonce) {
if (encoded_nonce.empty()) {
return std::string();
}
std::string nonce;
if (!base::Base64Decode(encoded_nonce, &nonce)) {
LOG(ERROR) << "Nonce passed on the command line is not correctly encoded";
return std::nullopt;
}
return nonce;
}
bool StoreDMToken(const std::string& token) {
DCHECK(install_static::IsSystemInstall());
if (token.size() > kMaxDMTokenLength) {
LOG(ERROR) << "DMToken length out of bounds";
return false;
}
base::win::RegKey key;
std::wstring value_name;
bool succeeded = false;
for (const auto& is_browser_location : {InstallUtil::BrowserLocation(false),
InstallUtil::BrowserLocation(true)}) {
std::tie(key, value_name) = InstallUtil::GetCloudManagementDmTokenLocation(
InstallUtil::ReadOnly(false), is_browser_location);
if (!key.Valid()) {
if (succeeded)
continue;
return false;
}
auto result =
key.WriteValue(value_name.c_str(), token.data(),
base::saturated_cast<DWORD>(token.size()), REG_BINARY);
if (result == ERROR_SUCCESS) {
succeeded = true;
} else if (!succeeded) {
::SetLastError(result);
PLOG(ERROR) << "Unable to write specified DMToken to the registry";
return false;
}
}
VLOG(1) << "Successfully stored specified DMToken in the registry.";
return true;
}
bool DeleteDMToken() {
DCHECK(install_static::IsSystemInstall());
for (const auto& is_browser_location : {InstallUtil::BrowserLocation(false),
InstallUtil::BrowserLocation(true)}) {
auto [key_path, value_name] =
InstallUtil::GetCloudManagementDmTokenPath(is_browser_location);
REGSAM wow_access = is_browser_location ? KEY_WOW64_64KEY : KEY_WOW64_32KEY;
base::win::RegKey key;
auto result = key.Open(HKEY_LOCAL_MACHINE, key_path.c_str(),
KEY_QUERY_VALUE | KEY_SET_VALUE | wow_access);
if (result == ERROR_FILE_NOT_FOUND) {
continue;
}
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed to open registry key HKLM\\" << key_path
<< " for deletion";
if (!is_browser_location)
return false;
continue;
}
if (!DeleteRegistryValue(key.Handle(), std::wstring(), wow_access,
value_name)) {
if (!is_browser_location)
return false;
continue;
}
if (key.GetValueCount().value_or(1) == 0) {
key.DeleteKey(L"", base::win::RegKey::RecursiveDelete(false));
}
}
VLOG(1) << "Successfully deleted DMToken from the registry.";
return true;
}
base::FilePath GetNotificationHelperPath(const base::FilePath& target_path,
const base::Version& version) {
return target_path.AppendASCII(version.GetString())
.Append(kNotificationHelperExe);
}
base::FilePath GetWerHelperPath(const base::FilePath& target_path,
const base::Version& version) {
return target_path.AppendASCII(version.GetString()).Append(kWerDll);
}
std::wstring GetWerHelperRegistryPath() {
return L"Software\\Microsoft\\Windows\\Windows Error Reporting"
L"\\RuntimeExceptionHelperModules";
}
base::FilePath GetElevationServicePath(const base::FilePath& target_path,
const base::Version& version) {
return target_path.AppendASCII(version.GetString())
.Append(kElevationServiceExe);
}
base::FilePath GetTracingServicePath(const base::FilePath& target_path,
const base::Version& version) {
return target_path.AppendASCII(version.GetString())
.Append(kElevatedTracingServiceExe);
}
void AddUpdateDowngradeVersionItem(HKEY root,
const base::Version& current_version,
const base::Version& new_version,
WorkItemList* list) {
DCHECK(list);
DCHECK(new_version.IsValid());
const auto downgrade_version = InstallUtil::GetDowngradeVersion();
const std::wstring client_state_key = install_static::GetClientStateKeyPath();
if (current_version.IsValid() && new_version < current_version) {
if (!downgrade_version) {
list->AddSetRegValueWorkItem(
root, client_state_key, KEY_WOW64_32KEY, kRegDowngradeVersion,
base::ASCIIToWide(current_version.GetString()), true);
}
} else if (!current_version.IsValid() || new_version >= downgrade_version) {
list->AddDeleteRegValueWorkItem(root, client_state_key, KEY_WOW64_32KEY,
kRegDowngradeVersion);
}
}
}