// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This file declares util functions for setup project.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "chrome/installer/setup/setup_util.h"
#include <objbase.h>
#include <windows.h>
#include <stddef.h>
#include <wtsapi32.h>
#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/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/ranges/algorithm.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"
#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
namespace installer {
namespace {
// Event log providers registry location.
constexpr wchar_t kEventLogProvidersRegPath[] =
L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
// Remove the registration of the browser's DelegateExecute verb handler class.
// This was once registered in support of "metro" mode on Windows 8.
void RemoveLegacyIExecuteCommandKey(const InstallerState& installer_state) {
const std::wstring handler_class_uuid =
install_static::GetLegacyCommandExecuteImplClsid();
// No work to do if this mode of install never registered a DelegateExecute
// verb handler.
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);
// Delete both 64 and 32 keys to handle 32->64 or 64->32 migration.
for (REGSAM bitness : {KEY_WOW64_32KEY, KEY_WOW64_64KEY})
installer::DeleteRegistryKey(root, delegate_execute_path, bitness);
}
// "The binaries" once referred to the on-disk footprint of Chrome and/or Chrome
// Frame when the products were configured to share such on-disk bits. Support
// for this mode of install was dropped from ToT in December 2016. Remove any
// stray bits in the registry leftover from such installs.
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
// Assume that non-Google is Chromium branding.
std::wstring path(L"Software\\Chromium Binaries");
#endif
installer::DeleteRegistryKey(installer_state.root_key(), path,
KEY_WOW64_32KEY);
#endif // !BUILDFLAG(GOOGLE_CHROME_FOR_TESTING_BRANDING)
}
void RemoveAppLauncherVersionKey(const InstallerState& installer_state) {
// The app launcher was only registered for Google Chrome.
#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 // BUILDFLAG(GOOGLE_CHROME_BRANDING)
}
void RemoveLegacyChromeAppCommands(const InstallerState& installer_state) {
// These app commands were only registered for Google Chrome.
#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 // BUILDFLAG(GOOGLE_CHROME_BRANDING)
}
} // namespace
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);
// TODO(tommi): The version directory really should match the version of
// setup.exe. To begin with, we should at least DCHECK that that's true.
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);
}
base::FilePath FindArchiveToPatch(const InstallationState& original_state,
const InstallerState& installer_state,
const base::Version& desired_version) {
if (desired_version.IsValid()) {
base::FilePath archive(
installer_state.GetInstallerDirectory(desired_version)
.Append(kChromeArchive));
return base::PathExists(archive) ? archive : base::FilePath();
}
// Check based on the version number advertised to Google Update, since that
// is the value used to select a specific differential update. If an archive
// can't be found using that, fallback to using the newest version present.
base::FilePath patch_source;
const ProductState* product =
original_state.GetProductState(installer_state.system_install());
if (product) {
patch_source = installer_state.GetInstallerDirectory(product->version())
.Append(installer::kChromeArchive);
if (base::PathExists(patch_source))
return patch_source;
}
std::unique_ptr<base::Version> version(
installer::GetMaxVersionFromArchiveDir(installer_state.target_path()));
if (version) {
patch_source = installer_state.GetInstallerDirectory(*version).Append(
installer::kChromeArchive);
if (base::PathExists(patch_source))
return patch_source;
}
return base::FilePath();
}
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) {
// We use the main thread of the new process to run:
// Sleep(delay_before_delete_ms);
// DeleteFile(path);
// ExitProcess(0);
// This runs before the main routine of the process runs, so it doesn't
// matter much which executable we choose except that we don't want to
// use e.g. a console app that causes a window to be created.
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_IN_MIGRATION();
ok = FALSE;
} 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) {
// Don't use SetPriorityClass with PROCESS_MODE_BACKGROUND_BEGIN because it
// will cap the process working set to 32 MiB. See
// https://crbug.com/1475179.
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) {
// The following status values represent failed uninstalls:
// 15: CHROME_NOT_INSTALLED
// 20: UNINSTALL_FAILED
// 21: UNINSTALL_CANCELLED
return (install_status == UNINSTALL_SUCCESSFUL ||
install_status == UNINSTALL_REQUIRES_REBOOT);
}
bool ContainsUnsupportedSwitch(const base::CommandLine& cmd_line) {
static const char* const kLegacySwitches[] = {
// Chrome Frame ready-mode.
"ready-mode",
"ready-mode-opt-in",
"ready-mode-temp-opt-out",
"ready-mode-end-temp-opt-out",
// Chrome Frame quick-enable.
"quick-enable-cf",
// Installation of Chrome Frame.
"chrome-frame",
"migrate-chrome-frame",
// Stand-alone App Launcher.
"app-host",
"app-launcher",
};
for (size_t i = 0; i < std::size(kLegacySwitches); ++i) {
if (cmd_line.HasSwitch(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) {
// Downcase the list of keys to preserve (all must be ASCII strings).
std::set<std::wstring> lowered_keys_to_preserve;
base::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;
}
// Repeatedly iterate over all subkeys deleting those that should not be
// preserved until only those remain. Multiple passes are needed since
// deleting one key may change the enumeration order of all remaining keys.
// Subkeys or values to be skipped on subsequent passes.
std::set<std::wstring> to_skip;
DWORD index = 0;
const size_t kMaxKeyNameLength = 256; // MSDN says 255; +1 for terminator.
std::wstring name(kMaxKeyNameLength, wchar_t());
bool did_delete = false; // True if at least one item was deleted.
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) {
// Unexpected, but perhaps the max key name length was raised. MSDN
// doesn't clearly say that name_length will contain the necessary
// length in this case, so double the buffer and try again.
name.reserve(name.capacity() * 2);
continue;
}
if (result == ERROR_NO_MORE_ITEMS) {
if (!did_delete)
break; // All subkeys were deleted. The job is done.
// Otherwise, loop again.
did_delete = false;
index = 0;
continue;
}
if (result != ERROR_SUCCESS)
break;
// Shrink the string to the actual length of the name.
name.resize(name_length);
// Skip over this key if it couldn't be deleted on a previous iteration.
if (to_skip.count(name)) {
++index;
continue;
}
// Skip over this key if it is one of the keys to preserve.
if (base::IsStringASCII(name) &&
lowered_keys_to_preserve.count(base::ToLowerASCII(name))) {
// Add the true name of the key to the list of keys to skip for subsequent
// iterations.
to_skip.insert(name);
++index;
continue;
}
// Delete this key.
result = key.DeleteKey(name.c_str());
if (result != ERROR_SUCCESS) {
LOG(ERROR) << "Failed to delete subkey " << name << " under path "
<< path;
// Skip over this key on subsequent iterations.
to_skip.insert(name);
++index;
continue;
}
did_delete = true;
}
// Delete the key if it no longer has any subkeys.
if (to_skip.empty()) {
result = key.DeleteKey(L"");
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed to delete key " << path;
}
return;
}
// Delete all values since subkeys are being preserved.
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())) {
// Double the space to hold the value name and try again.
name.reserve(name.capacity() * 2);
continue;
}
// Otherwise, the max has been exceeded. Nothing more to be done.
break;
}
if (result == ERROR_NO_MORE_ITEMS) {
if (!did_delete)
break; // All values were deleted. The job is done.
// Otherwise, loop again.
did_delete = false;
index = 0;
continue;
}
if (result != ERROR_SUCCESS)
break;
// Shrink the string to the actual length of the name.
name.resize(name_length);
// Skip over this value if it couldn't be deleted on a previous iteration.
if (to_skip.count(name)) {
++index;
continue;
}
// Delete this value.
result = key.DeleteValue(name.c_str());
if (result != ERROR_SUCCESS) {
LOG(ERROR) << "Failed to delete value " << name << " in key " << path;
// Skip over this value on subsequent iterations.
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::CHROME_ARCHIVE_PATCH:
consumer_name = "ChromeArchivePatch";
break;
case UnPackConsumer::COMPRESSED_CHROME_ARCHIVE:
consumer_name = "CompressedChromeArchive";
break;
case UnPackConsumer::SETUP_EXE_PATCH:
consumer_name = "SetupExePatch";
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);
// Speicifes the number of event categories defined in the dll.
work_item_list->AddSetRegValueWorkItem(
HKEY_LOCAL_MACHINE, reg_path, WorkItem::kWow64Default, L"CategoryCount",
static_cast<DWORD>(1), true);
// Specifies the event type emitted by this event source.
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 the operation fails we log the error but still continue because none of
// these are critical for the proper operation of the browser.
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());
// TODO(http://crbug.com/668120): If the Event Viewer is open the provider dll
// will fail to get deleted. This doesn't fail the uninstallation altogether
// but leaves files behind.
installer::DeleteRegistryKey(HKEY_LOCAL_MACHINE, reg_path,
WorkItem::kWow64Default);
}
void DoLegacyCleanups(const InstallerState& installer_state,
InstallStatus install_status) {
// Do no harm if the install didn't succeed.
if (InstallUtil::GetInstallReturnCode(install_status))
return;
// Cleanups that apply to any install mode.
RemoveLegacyIExecuteCommandKey(installer_state);
// The cleanups below only apply to normal Chrome, not side-by-side (canary).
if (!install_static::InstallDetails::Get().is_primary_mode())
return;
RemoveBinariesVersionKey(installer_state);
RemoveAppLauncherVersionKey(installer_state);
RemoveLegacyChromeAppCommands(installer_state);
}
base::Time GetConsoleSessionStartTime() {
constexpr DWORD kInvalidSessionId = 0xFFFFFFFF;
DWORD console_session_id = ::WTSGetActiveConsoleSessionId();
if (console_session_id == kInvalidSessionId)
return base::Time();
wchar_t* buffer = nullptr;
DWORD buffer_size = 0;
if (!::WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE,
console_session_id, WTSSessionInfo, &buffer,
&buffer_size)) {
return base::Time();
}
absl::Cleanup wts_deleter = [buffer] { ::WTSFreeMemory(buffer); };
WTSINFO* wts_info = nullptr;
if (buffer_size < sizeof(*wts_info))
return base::Time();
wts_info = reinterpret_cast<WTSINFO*>(buffer);
FILETIME filetime = {wts_info->LogonTime.u.LowPart,
static_cast<DWORD>(wts_info->LogonTime.u.HighPart)};
return base::Time::FromFileTime(filetime);
}
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;
}
// The token passed on the command line is base64-encoded, but since this is
// on Windows, it is passed in as a wide string containing base64 values only.
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()) {
// The nonce command line argument is optional. If none is specified use
// an empty string.
return std::string();
}
// The nonce passed on the command line is base64-encoded.
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;
}
// Write the token both to the app-neutral and browser-specific locations.
// Only the former is mandatory -- the latter is best-effort.
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 the key couldn't be opened on the first iteration (the mandatory
// location), return failure straight away. Otherwise, continue iterating.
if (!key.Valid()) {
if (succeeded)
continue;
// Logging already performed in GetCloudManagementDmTokenLocation.
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;
} // Else ignore the failure to write to the best-effort location.
}
VLOG(1) << "Successfully stored specified DMToken in the registry.";
return true;
}
bool DeleteDMToken() {
DCHECK(install_static::IsSystemInstall());
// Delete the token from both the app-neutral and browser-specific locations.
// Only the former is mandatory -- the latter is best-effort.
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) {
// The registry key which stores the DMToken value was not found, so
// deletion is not necessary.
continue;
}
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed to open registry key HKLM\\" << key_path
<< " for deletion";
// If the key couldn't be opened for the mandatory location, return
// failure immediately. Otherwise, continue iterating.
if (!is_browser_location)
return false;
continue;
}
if (!DeleteRegistryValue(key.Handle(), std::wstring(), wow_access,
value_name)) {
if (!is_browser_location)
return false; // Logging already performed in `DeleteRegistryValue()`.
continue;
} // Else ignore the failure to write to the best-effort location.
// Delete the key if no other values or keys are present.
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);
}
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) {
// This is a downgrade. Write the value if this is the first one (i.e., no
// previous value exists). Otherwise, leave any existing value in place.
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) {
// This is a new install or an upgrade to/past a previous DowngradeVersion.
list->AddDeleteRegValueWorkItem(root, client_state_key, KEY_WOW64_32KEY,
kRegDowngradeVersion);
}
}
} // namespace installer