OLD | NEW |
| (Empty) |
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 <stddef.h> | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/metrics/field_trial.h" | |
9 #include "base/metrics/histogram.h" | |
10 #include "base/metrics/sparse_histogram.h" | |
11 #include "base/strings/utf_string_conversions.h" | |
12 #include "base/win/registry.h" | |
13 #include "chrome/browser/chrome_elf_init_win.h" | |
14 #include "chrome/common/chrome_version.h" | |
15 #include "chrome_elf/blacklist/blacklist.h" | |
16 #include "chrome_elf/chrome_elf_constants.h" | |
17 #include "chrome_elf/dll_hash/dll_hash.h" | |
18 #include "components/variations/variations_associated_data.h" | |
19 #include "content/public/browser/browser_thread.h" | |
20 | |
21 const char kBrowserBlacklistTrialName[] = "BrowserBlacklist"; | |
22 const char kBrowserBlacklistTrialDisabledGroupName[] = "NoBlacklist"; | |
23 | |
24 namespace { | |
25 | |
26 // How long to wait, in seconds, before reporting for the second (and last | |
27 // time), what dlls were blocked from the browser process. | |
28 const int kBlacklistReportingDelaySec = 600; | |
29 | |
30 // This enum is used to define the buckets for an enumerated UMA histogram. | |
31 // Hence, | |
32 // (a) existing enumerated constants should never be deleted or reordered, and | |
33 // (b) new constants should only be appended in front of | |
34 // BLACKLIST_SETUP_EVENT_MAX. | |
35 enum BlacklistSetupEventType { | |
36 // The blacklist beacon has placed to enable the browser blacklisting. | |
37 BLACKLIST_SETUP_ENABLED = 0, | |
38 | |
39 // The blacklist was successfully enabled. | |
40 BLACKLIST_SETUP_RAN_SUCCESSFULLY, | |
41 | |
42 // The blacklist setup code failed to execute. | |
43 BLACKLIST_SETUP_FAILED, | |
44 | |
45 // The blacklist thunk setup code failed. This is probably an indication | |
46 // that something else patched that code first. | |
47 BLACKLIST_THUNK_SETUP_FAILED, | |
48 | |
49 // Deprecated. The blacklist interception code failed to execute. | |
50 BLACKLIST_INTERCEPTION_FAILED, | |
51 | |
52 // The blacklist was disabled for this run (after it failed too many times). | |
53 BLACKLIST_SETUP_DISABLED, | |
54 | |
55 // Always keep this at the end. | |
56 BLACKLIST_SETUP_EVENT_MAX, | |
57 }; | |
58 | |
59 void RecordBlacklistSetupEvent(BlacklistSetupEventType blacklist_setup_event) { | |
60 UMA_HISTOGRAM_ENUMERATION("Blacklist.Setup", | |
61 blacklist_setup_event, | |
62 BLACKLIST_SETUP_EVENT_MAX); | |
63 } | |
64 | |
65 // Report which DLLs were prevented from being loaded. | |
66 void ReportSuccessfulBlocks() { | |
67 // Figure out how many dlls were blocked. | |
68 int num_blocked_dlls = 0; | |
69 blacklist::SuccessfullyBlocked(NULL, &num_blocked_dlls); | |
70 | |
71 if (num_blocked_dlls == 0) | |
72 return; | |
73 | |
74 // Now retrieve the list of blocked dlls. | |
75 std::vector<const wchar_t*> blocked_dlls(num_blocked_dlls); | |
76 blacklist::SuccessfullyBlocked(&blocked_dlls[0], &num_blocked_dlls); | |
77 | |
78 // Send up the hashes of the blocked dlls via UMA. | |
79 for (size_t i = 0; i < blocked_dlls.size(); ++i) { | |
80 std::string dll_name_utf8; | |
81 base::WideToUTF8(blocked_dlls[i], wcslen(blocked_dlls[i]), &dll_name_utf8); | |
82 int uma_hash = DllNameToHash(dll_name_utf8); | |
83 | |
84 UMA_HISTOGRAM_SPARSE_SLOWLY("Blacklist.Blocked", uma_hash); | |
85 } | |
86 } | |
87 | |
88 } // namespace | |
89 | |
90 void InitializeChromeElf() { | |
91 if (base::FieldTrialList::FindFullName(kBrowserBlacklistTrialName) == | |
92 kBrowserBlacklistTrialDisabledGroupName) { | |
93 // Disable the blacklist for all future runs by removing the beacon. | |
94 base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER); | |
95 blacklist_registry_key.DeleteKey(blacklist::kRegistryBeaconPath); | |
96 } else { | |
97 AddFinchBlacklistToRegistry(); | |
98 BrowserBlacklistBeaconSetup(); | |
99 } | |
100 | |
101 // Report all successful blacklist interceptions. | |
102 ReportSuccessfulBlocks(); | |
103 | |
104 // Schedule another task to report all successful interceptions later. | |
105 // This time delay should be long enough to catch any dlls that attempt to | |
106 // inject after Chrome has started up. | |
107 content::BrowserThread::PostDelayedTask( | |
108 content::BrowserThread::UI, | |
109 FROM_HERE, | |
110 base::Bind(&ReportSuccessfulBlocks), | |
111 base::TimeDelta::FromSeconds(kBlacklistReportingDelaySec)); | |
112 } | |
113 | |
114 // Note that running multiple chrome instances with distinct user data | |
115 // directories could lead to deletion (and/or replacement) of the finch | |
116 // blacklist registry data in one instance before the second has a chance to | |
117 // read those values. | |
118 void AddFinchBlacklistToRegistry() { | |
119 base::win::RegKey finch_blacklist_registry_key( | |
120 HKEY_CURRENT_USER, blacklist::kRegistryFinchListPath, KEY_SET_VALUE); | |
121 | |
122 // No point in trying to continue if the registry key isn't valid. | |
123 if (!finch_blacklist_registry_key.Valid()) | |
124 return; | |
125 | |
126 // Delete and recreate the key to clear the registry. | |
127 finch_blacklist_registry_key.DeleteKey(L""); | |
128 finch_blacklist_registry_key.Create( | |
129 HKEY_CURRENT_USER, blacklist::kRegistryFinchListPath, KEY_SET_VALUE); | |
130 | |
131 std::map<std::string, std::string> params; | |
132 variations::GetVariationParams(kBrowserBlacklistTrialName, ¶ms); | |
133 | |
134 for (std::map<std::string, std::string>::iterator it = params.begin(); | |
135 it != params.end(); | |
136 ++it) { | |
137 std::wstring name = base::UTF8ToWide(it->first); | |
138 std::wstring val = base::UTF8ToWide(it->second); | |
139 | |
140 finch_blacklist_registry_key.WriteValue(name.c_str(), val.c_str()); | |
141 } | |
142 } | |
143 | |
144 void BrowserBlacklistBeaconSetup() { | |
145 base::win::RegKey blacklist_registry_key(HKEY_CURRENT_USER, | |
146 blacklist::kRegistryBeaconPath, | |
147 KEY_QUERY_VALUE | KEY_SET_VALUE); | |
148 | |
149 // No point in trying to continue if the registry key isn't valid. | |
150 if (!blacklist_registry_key.Valid()) | |
151 return; | |
152 | |
153 // Record the results of the last blacklist setup. | |
154 DWORD blacklist_state = blacklist::BLACKLIST_STATE_MAX; | |
155 blacklist_registry_key.ReadValueDW(blacklist::kBeaconState, &blacklist_state); | |
156 | |
157 if (blacklist_state == blacklist::BLACKLIST_ENABLED) { | |
158 // The blacklist setup didn't crash, so we report if it was enabled or not. | |
159 if (blacklist::IsBlacklistInitialized()) { | |
160 RecordBlacklistSetupEvent(BLACKLIST_SETUP_RAN_SUCCESSFULLY); | |
161 } else { | |
162 // The only way for the blacklist to be enabled, but not fully | |
163 // initialized is if the thunk setup failed. See blacklist.cc | |
164 // for more details. | |
165 RecordBlacklistSetupEvent(BLACKLIST_THUNK_SETUP_FAILED); | |
166 } | |
167 | |
168 // Regardless of if the blacklist was fully enabled or not, report how many | |
169 // times we had to try to set it up. | |
170 DWORD attempt_count = 0; | |
171 blacklist_registry_key.ReadValueDW(blacklist::kBeaconAttemptCount, | |
172 &attempt_count); | |
173 UMA_HISTOGRAM_COUNTS_100("Blacklist.RetryAttempts.Success", attempt_count); | |
174 } else if (blacklist_state == blacklist::BLACKLIST_SETUP_FAILED) { | |
175 // We can set the state to disabled without checking that the maximum number | |
176 // of attempts was exceeded because blacklist.cc has already done this. | |
177 RecordBlacklistSetupEvent(BLACKLIST_SETUP_FAILED); | |
178 blacklist_registry_key.WriteValue(blacklist::kBeaconState, | |
179 blacklist::BLACKLIST_DISABLED); | |
180 } else if (blacklist_state == blacklist::BLACKLIST_DISABLED) { | |
181 RecordBlacklistSetupEvent(BLACKLIST_SETUP_DISABLED); | |
182 } | |
183 | |
184 // Find the last recorded blacklist version. | |
185 base::string16 blacklist_version; | |
186 blacklist_registry_key.ReadValue(blacklist::kBeaconVersion, | |
187 &blacklist_version); | |
188 | |
189 if (blacklist_version != TEXT(CHROME_VERSION_STRING)) { | |
190 // The blacklist hasn't been enabled for this version yet, so enable it | |
191 // and reset the failure count to zero. | |
192 LONG set_version = blacklist_registry_key.WriteValue( | |
193 blacklist::kBeaconVersion, | |
194 TEXT(CHROME_VERSION_STRING)); | |
195 | |
196 LONG set_state = blacklist_registry_key.WriteValue( | |
197 blacklist::kBeaconState, | |
198 blacklist::BLACKLIST_ENABLED); | |
199 | |
200 blacklist_registry_key.WriteValue(blacklist::kBeaconAttemptCount, | |
201 static_cast<DWORD>(0)); | |
202 | |
203 // Only report the blacklist as getting setup when both registry writes | |
204 // succeed, since otherwise the blacklist wasn't properly setup. | |
205 if (set_version == ERROR_SUCCESS && set_state == ERROR_SUCCESS) | |
206 RecordBlacklistSetupEvent(BLACKLIST_SETUP_ENABLED); | |
207 } | |
208 } | |
OLD | NEW |