File: chrome_elf_init.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (226 lines) | stat: -rw-r--r-- 9,009 bytes parent folder | download | duplicates (7)
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);
  }
}