1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
// Copyright 2014 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/browser/win/chrome_elf_init.h"
#include <stddef.h>
#include "base/functional/bind.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/registry.h"
#include "chrome/browser/browser_process.h"
#include "chrome/chrome_elf/blocklist_constants.h"
#include "chrome/chrome_elf/chrome_elf_constants.h"
#include "chrome/chrome_elf/dll_hash/dll_hash.h"
#include "chrome/chrome_elf/third_party_dlls/public_api.h"
#include "chrome/common/chrome_version.h"
#include "chrome/common/pref_names.h"
#include "chrome/install_static/install_util.h"
#include "components/prefs/pref_service.h"
#include "components/variations/variations_associated_data.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/content_features.h"
#include "sandbox/policy/features.h"
const char kBrowserBlocklistTrialName[] = "BrowserBlocklist";
const char kBrowserBlocklistTrialDisabledGroupName[] = "NoBlocklist";
namespace {
// This enum is used to define the buckets for an enumerated UMA histogram.
// Hence,
// (a) existing enumerated constants should never be deleted or reordered, and
// (b) new constants should only be appended in front of
// BLOCKLIST_SETUP_EVENT_MAX.
enum BlocklistSetupEventType {
// The blocklist beacon has placed to enable the browser blocklisting.
BLOCKLIST_SETUP_ENABLED = 0,
// The blocklist was successfully enabled.
BLOCKLIST_SETUP_RAN_SUCCESSFULLY,
// The blocklist setup code failed to execute.
BLOCKLIST_SETUP_FAILED,
// The blocklist thunk setup code failed. This is probably an indication
// that something else patched that code first.
BLOCKLIST_THUNK_SETUP_FAILED,
// Deprecated. The blocklist interception code failed to execute.
BLOCKLIST_INTERCEPTION_FAILED,
// The blocklist was disabled for this run (after it failed too many times).
BLOCKLIST_SETUP_DISABLED,
// Always keep this at the end.
BLOCKLIST_SETUP_EVENT_MAX,
};
void RecordBlocklistSetupEvent(BlocklistSetupEventType blocklist_setup_event) {
base::UmaHistogramEnumeration("ChromeElf.Beacon.SetupStatus",
blocklist_setup_event,
BLOCKLIST_SETUP_EVENT_MAX);
}
std::wstring GetBeaconRegistryPath() {
return install_static::GetRegistryPath().append(
blocklist::kRegistryBeaconKeyName);
}
// This enum is used to define the buckets for an enumerated UMA histogram.
// Hence,
// (a) existing enumerated constants should never be deleted or reordered, and
// (b) new constants should only be appended in front of EXTENSIONPOINT_MAX.
enum ExtensionPointEnableState {
// Extension point mitigation disabled due to presence of legacy IME.
EXTENSIONPOINT_DISABLED_IME,
// Extension point mitigation enabled.
EXTENSIONPOINT_ENABLED,
// Always keep this at the end.
EXTENSIONPOINT_MAX,
};
void RecordExtensionPointsEnableState(ExtensionPointEnableState enable_state) {
base::UmaHistogramEnumeration("ChromeElf.ExtensionPoint.EnableState",
enable_state, EXTENSIONPOINT_MAX);
}
ExtensionPointEnableState GetExtensionPointsEnableState() {
// Legacy IMEs can be detected as HKLs that have a file name.
int list_size = GetKeyboardLayoutList(0, nullptr);
if (list_size != 0) {
std::vector<HKL> hkl_list(list_size);
if (GetKeyboardLayoutList(list_size, hkl_list.data()) == list_size) {
for (auto* hkl : hkl_list) {
if (ImmGetIMEFileName(hkl, nullptr, 0) != 0)
return EXTENSIONPOINT_DISABLED_IME;
}
}
}
return EXTENSIONPOINT_ENABLED;
}
bool IsBrowserLegacyExtensionPointsBlocked() {
PrefService* local_state = g_browser_process->local_state();
if (!local_state ||
!local_state->HasPrefPath(prefs::kBlockBrowserLegacyExtensionPoints) ||
!local_state->IsManagedPreference(
prefs::kBlockBrowserLegacyExtensionPoints))
return true;
return local_state->GetBoolean(prefs::kBlockBrowserLegacyExtensionPoints);
}
} // namespace
void InitializeChromeElf() {
if (base::FieldTrialList::FindFullName(kBrowserBlocklistTrialName) ==
kBrowserBlocklistTrialDisabledGroupName) {
// Disable the blocklist for all future runs by removing the beacon.
base::win::RegKey blocklist_registry_key(HKEY_CURRENT_USER);
blocklist_registry_key.DeleteKey(GetBeaconRegistryPath().c_str());
} else {
BrowserBlocklistBeaconSetup();
}
// Make sure the registry key we read earlier in startup
// sandbox::MITIGATION_EXTENSION_POINT_DISABLE is set properly in reg.
// Note: the very existence of this key signals elf to not enable
// this mitigation on browser next start.
const std::wstring reg_path(install_static::GetRegistryPath().append(
elf_sec::kRegBrowserExtensionPointKeyName));
base::win::RegKey browser_extension_point_registry_key(
HKEY_CURRENT_USER, reg_path.c_str(), KEY_READ);
ExtensionPointEnableState extension_point_enable_state =
GetExtensionPointsEnableState();
RecordExtensionPointsEnableState(extension_point_enable_state);
bool enable_extension_point_policy =
(extension_point_enable_state == EXTENSIONPOINT_ENABLED) &&
base::FeatureList::IsEnabled(
sandbox::policy::features::kWinSboxDisableExtensionPoints) &&
IsBrowserLegacyExtensionPointsBlocked();
if (enable_extension_point_policy) {
if (!browser_extension_point_registry_key.Valid()) {
(void)browser_extension_point_registry_key.Create(
HKEY_CURRENT_USER, reg_path.c_str(), KEY_WRITE);
}
} else {
if (browser_extension_point_registry_key.Valid()) {
browser_extension_point_registry_key.DeleteKey(L"");
}
}
}
void BrowserBlocklistBeaconSetup() {
base::win::RegKey blocklist_registry_key(HKEY_CURRENT_USER,
GetBeaconRegistryPath().c_str(),
KEY_QUERY_VALUE | KEY_SET_VALUE);
// No point in trying to continue if the registry key isn't valid.
if (!blocklist_registry_key.Valid())
return;
// Record the results of the last blocklist setup.
DWORD blocklist_state = blocklist::BLOCKLIST_STATE_MAX;
blocklist_registry_key.ReadValueDW(blocklist::kBeaconState, &blocklist_state);
if (blocklist_state == blocklist::BLOCKLIST_ENABLED) {
// The blocklist setup didn't crash, so we report if it was enabled or not.
if (IsThirdPartyInitialized()) {
RecordBlocklistSetupEvent(BLOCKLIST_SETUP_RAN_SUCCESSFULLY);
} else {
// The only way for the blocklist to be enabled, but not fully
// initialized is if the thunk setup failed. See blocklist.cc
// for more details.
RecordBlocklistSetupEvent(BLOCKLIST_THUNK_SETUP_FAILED);
}
// Regardless of if the blocklist was fully enabled or not, report how many
// times we had to try to set it up.
DWORD attempt_count = 0;
blocklist_registry_key.ReadValueDW(blocklist::kBeaconAttemptCount,
&attempt_count);
base::UmaHistogramCounts100("ChromeElf.Beacon.RetryAttemptsBeforeSuccess",
attempt_count);
} else if (blocklist_state == blocklist::BLOCKLIST_SETUP_FAILED) {
// We can set the state to disabled without checking that the maximum number
// of attempts was exceeded because blocklist.cc has already done this.
RecordBlocklistSetupEvent(BLOCKLIST_SETUP_FAILED);
blocklist_registry_key.WriteValue(blocklist::kBeaconState,
blocklist::BLOCKLIST_DISABLED);
} else if (blocklist_state == blocklist::BLOCKLIST_DISABLED) {
RecordBlocklistSetupEvent(BLOCKLIST_SETUP_DISABLED);
}
// Find the last recorded blocklist version.
std::wstring blocklist_version;
blocklist_registry_key.ReadValue(blocklist::kBeaconVersion,
&blocklist_version);
if (blocklist_version != TEXT(CHROME_VERSION_STRING)) {
// The blocklist hasn't been enabled for this version yet, so enable it
// and reset the failure count to zero.
LONG set_version = blocklist_registry_key.WriteValue(
blocklist::kBeaconVersion,
TEXT(CHROME_VERSION_STRING));
LONG set_state = blocklist_registry_key.WriteValue(
blocklist::kBeaconState,
blocklist::BLOCKLIST_ENABLED);
blocklist_registry_key.WriteValue(blocklist::kBeaconAttemptCount,
static_cast<DWORD>(0));
// Only report the blocklist as getting setup when both registry writes
// succeed, since otherwise the blocklist wasn't properly setup.
if (set_version == ERROR_SUCCESS && set_state == ERROR_SUCCESS)
RecordBlocklistSetupEvent(BLOCKLIST_SETUP_ENABLED);
}
}
|