| 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 |