chrome_elf_init_win.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/bind.h"
6#include "base/metrics/field_trial.h"
7#include "base/metrics/histogram.h"
8#include "base/metrics/sparse_histogram.h"
9#include "base/strings/utf_string_conversions.h"
10#include "base/win/registry.h"
11#include "chrome/browser/chrome_elf_init_win.h"
12#include "chrome_elf/blacklist/blacklist.h"
13#include "chrome_elf/chrome_elf_constants.h"
14#include "chrome_elf/dll_hash/dll_hash.h"
15#include "content/public/browser/browser_thread.h"
16#include "version.h"  // NOLINT
17
18namespace {
19
20const char kBrowserBlacklistTrialName[] = "BrowserBlacklist";
21const char kBrowserBlacklistTrialEnabledGroupName[] = "Enabled";
22
23// How long to wait, in seconds, before reporting for the second (and last
24// time), what dlls were blocked from the browser process.
25const int kBlacklistReportingDelaySec = 600;
26
27// This enum is used to define the buckets for an enumerated UMA histogram.
28// Hence,
29//   (a) existing enumerated constants should never be deleted or reordered, and
30//   (b) new constants should only be appended in front of
31//       BLACKLIST_SETUP_EVENT_MAX.
32enum BlacklistSetupEventType {
33  // The blacklist beacon has placed to enable the browser blacklisting.
34  BLACKLIST_SETUP_ENABLED = 0,
35
36  // The blacklist was successfully enabled.
37  BLACKLIST_SETUP_RAN_SUCCESSFULLY,
38
39  // The blacklist setup code failed to execute.
40  BLACKLIST_SETUP_FAILED,
41
42  // The blacklist thunk setup code failed to execute.
43  BLACKLIST_THUNK_SETUP_FAILED,
44
45  // The blacklist interception code failed to execute.
46  BLACKLIST_INTERCEPTION_FAILED,
47
48  // Always keep this at the end.
49  BLACKLIST_SETUP_EVENT_MAX,
50};
51
52void RecordBlacklistSetupEvent(BlacklistSetupEventType blacklist_setup_event) {
53  UMA_HISTOGRAM_ENUMERATION("Blacklist.Setup",
54                            blacklist_setup_event,
55                            BLACKLIST_SETUP_EVENT_MAX);
56}
57
58// Report which DLLs were prevented from being loaded.
59void ReportSuccessfulBlocks() {
60  // Figure out how many dlls were blocked.
61  int num_blocked_dlls = 0;
62  blacklist::SuccessfullyBlocked(NULL, &num_blocked_dlls);
63
64  if (num_blocked_dlls == 0)
65    return;
66
67  // Now retrieve the list of blocked dlls.
68  std::vector<const wchar_t*> blocked_dlls(num_blocked_dlls);
69  blacklist::SuccessfullyBlocked(&blocked_dlls[0], &num_blocked_dlls);
70
71  // Send up the hashes of the blocked dlls via UMA.
72  for (size_t i = 0; i < blocked_dlls.size(); ++i) {
73    std::string dll_name_utf8;
74    base::WideToUTF8(blocked_dlls[i], wcslen(blocked_dlls[i]), &dll_name_utf8);
75    int uma_hash = DllNameToHash(dll_name_utf8);
76
77    UMA_HISTOGRAM_SPARSE_SLOWLY("Blacklist.Blocked", uma_hash);
78  }
79}
80
81}  // namespace
82
83void InitializeChromeElf() {
84  if (base::FieldTrialList::FindFullName(kBrowserBlacklistTrialName) ==
85      kBrowserBlacklistTrialEnabledGroupName) {
86    BrowserBlacklistBeaconSetup();
87  } else {
88    // Disable the blacklist for all future runs by removing the beacon.
89    base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER);
90    blacklist_registry_key.DeleteKey(blacklist::kRegistryBeaconPath);
91  }
92
93  // Report all successful blacklist interceptions.
94  ReportSuccessfulBlocks();
95
96  // Schedule another task to report all sucessful interceptions later.
97  // This time delay should be long enough to catch any dlls that attempt to
98  // inject after Chrome has started up.
99  content::BrowserThread::PostDelayedTask(
100      content::BrowserThread::UI,
101      FROM_HERE,
102      base::Bind(&ReportSuccessfulBlocks),
103      base::TimeDelta::FromSeconds(kBlacklistReportingDelaySec));
104}
105
106void BrowserBlacklistBeaconSetup() {
107  base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER,
108                                           blacklist::kRegistryBeaconPath,
109                                           KEY_QUERY_VALUE | KEY_SET_VALUE);
110
111  // No point in trying to continue if the registry key isn't valid.
112  if (!blacklist_registry_key.Valid())
113    return;
114
115  // Record the results of the last blacklist setup.
116  DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX;
117  blacklist_registry_key.ReadValueDW(blacklist::kBeaconState, &blacklist_state);
118
119  if (blacklist_state == blacklist::BLACKLIST_ENABLED) {
120    RecordBlacklistSetupEvent(BLACKLIST_SETUP_RAN_SUCCESSFULLY);
121  } else {
122    switch (blacklist_state) {
123      case blacklist::BLACKLIST_SETUP_RUNNING:
124        RecordBlacklistSetupEvent(BLACKLIST_SETUP_FAILED);
125        break;
126      case blacklist::BLACKLIST_THUNK_SETUP:
127        RecordBlacklistSetupEvent(BLACKLIST_THUNK_SETUP_FAILED);
128        break;
129      case blacklist::BLACKLIST_INTERCEPTING:
130        RecordBlacklistSetupEvent(BLACKLIST_INTERCEPTION_FAILED);
131        break;
132    }
133
134    // Since some part of the blacklist failed, mark it as disabled
135    // for this version.
136    if (blacklist_state != blacklist::BLACKLIST_DISABLED) {
137      blacklist_registry_key.WriteValue(blacklist::kBeaconState,
138                                        blacklist::BLACKLIST_DISABLED);
139    }
140  }
141
142  // Find the last recorded blacklist version.
143  base::string16 blacklist_version;
144  blacklist_registry_key.ReadValue(blacklist::kBeaconVersion,
145                                   &blacklist_version);
146
147  if (blacklist_version != TEXT(CHROME_VERSION_STRING)) {
148    // The blacklist hasn't been enabled for this version yet, so enable it.
149    LONG set_version = blacklist_registry_key.WriteValue(
150        blacklist::kBeaconVersion,
151        TEXT(CHROME_VERSION_STRING));
152
153    LONG set_state = blacklist_registry_key.WriteValue(
154        blacklist::kBeaconState,
155        blacklist::BLACKLIST_ENABLED);
156
157    // Only report the blacklist as getting setup when both registry writes
158    // succeed, since otherwise the blacklist wasn't properly setup.
159    if (set_version == ERROR_SUCCESS && set_state == ERROR_SUCCESS)
160      RecordBlacklistSetupEvent(BLACKLIST_SETUP_ENABLED);
161  }
162}
163