Refactor WebsiteSettings to operate on a SecurityInfo
[chromium-blink-merge.git] / chrome / browser / safe_browsing / incident_reporting / environment_data_collection_win.cc
blob39d065e8a30e5a9a1fe3b54a992eaee6c93ba6ad
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.
5 #include "chrome/browser/safe_browsing/incident_reporting/environment_data_collection_win.h"
7 #include <set>
8 #include <string>
10 #include "base/i18n/case_conversion.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/strings/string16.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/win/registry.h"
17 #include "chrome/browser/install_verification/win/module_info.h"
18 #include "chrome/browser/install_verification/win/module_verification_common.h"
19 #include "chrome/browser/net/service_providers_win.h"
20 #include "chrome/browser/safe_browsing/incident_reporting/module_integrity_verifier_win.h"
21 #include "chrome/browser/safe_browsing/path_sanitizer.h"
22 #include "chrome/common/safe_browsing/binary_feature_extractor.h"
23 #include "chrome/common/safe_browsing/csd.pb.h"
24 #include "chrome_elf/chrome_elf_constants.h"
25 #include "components/variations/variations_associated_data.h"
27 namespace safe_browsing {
29 namespace {
31 const REGSAM kKeyReadNoNotify = (KEY_READ) & ~(KEY_NOTIFY);
33 // The modules on which we will run VerifyModule.
34 const wchar_t* const kModulesToVerify[] = {
35 L"chrome.dll",
36 L"chrome_elf.dll",
37 L"ntdll.dll",
40 // The registry keys to collect data from.
41 const RegistryKeyInfo kRegKeysToCollect[] = {
42 {HKEY_CURRENT_USER, L"Software\\CSAStats"},
45 // Helper function for expanding all environment variables in |path|.
46 std::wstring ExpandEnvironmentVariables(const std::wstring& path) {
47 static const DWORD kMaxBuffer = 32 * 1024; // Max according to MSDN.
48 std::wstring path_expanded;
49 DWORD path_len = MAX_PATH;
50 do {
51 DWORD result = ExpandEnvironmentStrings(
52 path.c_str(), base::WriteInto(&path_expanded, path_len), path_len);
53 if (!result) {
54 // Failed to expand variables. Return the original string.
55 DPLOG(ERROR) << path;
56 break;
58 if (result <= path_len)
59 return path_expanded.substr(0, result - 1);
60 path_len = result;
61 } while (path_len < kMaxBuffer);
63 return path;
66 // Helper function to convert HKEYs to strings.
67 base::string16 HKEYToString(HKEY key) {
68 DCHECK_EQ(HKEY_CURRENT_USER, key);
69 return L"HKEY_CURRENT_USER";
72 // Helper function to extract data from a single registry key (and all of its
73 // subkeys), recursively.
74 void CollectRegistryDataForKey(
75 const base::win::RegKey& key,
76 ClientIncidentReport_EnvironmentData_OS_RegistryKey* key_pb) {
77 using RegistryValueProto =
78 ClientIncidentReport_EnvironmentData_OS_RegistryValue;
79 using RegistryKeyProto = ClientIncidentReport_EnvironmentData_OS_RegistryKey;
81 DWORD num_subkeys = 0;
82 DWORD max_subkey_name_len = 0;
83 DWORD num_values = 0;
84 DWORD max_value_name_len = 0;
85 DWORD max_value_len = 0;
86 LONG result = RegQueryInfoKey(
87 key.Handle(), NULL, NULL, NULL, &num_subkeys, &max_subkey_name_len, NULL,
88 &num_values, &max_value_name_len, &max_value_len, NULL, NULL);
90 DWORD max_name_len = std::max(max_subkey_name_len, max_value_name_len) + 1;
91 std::vector<wchar_t> name_buffer(max_name_len);
92 // Read the values.
93 if (num_values != 0) {
94 std::vector<uint8> value_buffer(max_value_len != 0 ? max_value_len : 1);
95 DWORD name_size = 0;
96 DWORD value_type = REG_NONE;
97 DWORD value_size = 0;
99 std::wstring name_str;
100 RegistryValueProto* registry_value;
101 for (DWORD i = 0; i < num_values;) {
102 name_size = static_cast<DWORD>(name_buffer.size());
103 value_size = static_cast<DWORD>(value_buffer.size());
104 result = ::RegEnumValue(key.Handle(), i, &name_buffer[0], &name_size,
105 NULL, &value_type, &value_buffer[0], &value_size);
106 switch (result) {
107 case ERROR_NO_MORE_ITEMS:
108 i = num_values;
109 break;
110 case ERROR_SUCCESS:
111 registry_value = key_pb->add_value();
112 if (name_size) {
113 name_str.assign(&name_buffer[0], name_size);
114 registry_value->set_name(base::WideToUTF8(name_str));
116 if (value_size) {
117 registry_value->set_type(value_type);
118 registry_value->set_data(&value_buffer[0], value_size);
120 ++i;
121 break;
122 case ERROR_MORE_DATA:
123 if (value_size > value_buffer.size())
124 value_buffer.resize(value_size);
125 // |name_size| does not include space for the terminating NULL.
126 if (name_size + 1 > name_buffer.size())
127 name_buffer.resize(name_size + 1);
128 break;
129 default:
130 break;
135 // Read the subkeys.
136 if (num_subkeys != 0) {
137 DWORD name_size = 0;
138 std::vector<std::wstring> subkey_names;
140 // Get the names of them.
141 for (DWORD i = 0; i < num_subkeys;) {
142 name_size = static_cast<DWORD>(name_buffer.size());
143 result = RegEnumKeyEx(key.Handle(), i, &name_buffer[0], &name_size, NULL,
144 NULL, NULL, NULL);
145 switch (result) {
146 case ERROR_NO_MORE_ITEMS:
147 num_subkeys = i;
148 break;
149 case ERROR_SUCCESS:
150 subkey_names.push_back(std::wstring(&name_buffer[0], name_size));
151 ++i;
152 break;
153 case ERROR_MORE_DATA:
154 name_buffer.resize(name_size + 1);
155 break;
156 default:
157 break;
161 // Extract the data (if possible).
162 base::win::RegKey subkey;
163 for (const auto& subkey_name : subkey_names) {
164 if (subkey.Open(key.Handle(), subkey_name.c_str(), kKeyReadNoNotify) ==
165 ERROR_SUCCESS) {
166 RegistryKeyProto* subkey_pb = key_pb->add_key();
167 subkey_pb->set_name(base::WideToUTF8(subkey_name));
168 CollectRegistryDataForKey(subkey, subkey_pb);
174 } // namespace
176 bool CollectDlls(ClientIncidentReport_EnvironmentData_Process* process) {
177 // Retrieve the module list.
178 std::set<ModuleInfo> loaded_modules;
179 if (!GetLoadedModules(&loaded_modules))
180 return false;
182 // Sanitize path of each module and add it to the incident report along with
183 // its headers.
184 PathSanitizer path_sanitizer;
185 scoped_refptr<BinaryFeatureExtractor> feature_extractor(
186 new BinaryFeatureExtractor());
187 for (const auto& module : loaded_modules) {
188 base::FilePath dll_path(module.name);
189 base::FilePath sanitized_path(dll_path);
190 path_sanitizer.StripHomeDirectory(&sanitized_path);
192 ClientIncidentReport_EnvironmentData_Process_Dll* dll = process->add_dll();
193 dll->set_path(
194 base::WideToUTF8(base::i18n::ToLower(sanitized_path.value())));
195 dll->set_base_address(module.base_address);
196 dll->set_length(module.size);
197 // TODO(grt): Consider skipping this for valid system modules.
198 if (!feature_extractor->ExtractImageFeatures(
199 dll_path,
200 BinaryFeatureExtractor::kOmitExports,
201 dll->mutable_image_headers(),
202 nullptr /* signed_data */)) {
203 dll->clear_image_headers();
207 return true;
210 void RecordLspFeature(ClientIncidentReport_EnvironmentData_Process* process) {
211 WinsockLayeredServiceProviderList lsp_list;
212 GetWinsockLayeredServiceProviders(&lsp_list);
214 // For each LSP, we extract and sanitize the path.
215 PathSanitizer path_sanitizer;
216 std::set<std::wstring> lsp_paths;
217 for (size_t i = 0; i < lsp_list.size(); ++i) {
218 base::FilePath lsp_path(ExpandEnvironmentVariables(lsp_list[i].path));
219 path_sanitizer.StripHomeDirectory(&lsp_path);
220 lsp_paths.insert(base::i18n::ToLower(lsp_path.value()));
223 // Look for a match between LSPs and loaded dlls.
224 for (int i = 0; i < process->dll_size(); ++i) {
225 if (lsp_paths.count(base::UTF8ToWide(process->dll(i).path()))) {
226 process->mutable_dll(i)
227 ->add_feature(ClientIncidentReport_EnvironmentData_Process_Dll::LSP);
232 void CollectDllBlacklistData(
233 ClientIncidentReport_EnvironmentData_Process* process) {
234 PathSanitizer path_sanitizer;
235 base::win::RegistryValueIterator iter(HKEY_CURRENT_USER,
236 blacklist::kRegistryFinchListPath);
237 for (; iter.Valid(); ++iter) {
238 base::FilePath dll_name(iter.Value());
239 path_sanitizer.StripHomeDirectory(&dll_name);
240 process->add_blacklisted_dll(dll_name.AsUTF8Unsafe());
244 void CollectModuleVerificationData(
245 const wchar_t* const modules_to_verify[],
246 size_t num_modules_to_verify,
247 ClientIncidentReport_EnvironmentData_Process* process) {
248 #if !defined(_WIN64)
249 using ModuleState = ClientIncidentReport_EnvironmentData_Process_ModuleState;
251 for (size_t i = 0; i < num_modules_to_verify; ++i) {
252 scoped_ptr<ModuleState> module_state(new ModuleState());
254 int num_bytes_different = 0;
255 bool scan_complete = VerifyModule(modules_to_verify[i],
256 module_state.get(),
257 &num_bytes_different);
259 if (module_state->modified_state() == ModuleState::MODULE_STATE_UNMODIFIED)
260 continue;
262 if (module_state->modified_state() == ModuleState::MODULE_STATE_MODIFIED) {
263 UMA_HISTOGRAM_COUNTS_10000(
264 "ModuleIntegrityVerification.BytesModified.WithoutByteSet",
265 num_bytes_different);
268 if (!scan_complete) {
269 UMA_HISTOGRAM_ENUMERATION(
270 "ModuleIntegrityVerification.RelocationsUnordered", i,
271 num_modules_to_verify);
274 process->mutable_module_state()->AddAllocated(module_state.release());
276 #endif // _WIN64
279 void CollectRegistryData(
280 const RegistryKeyInfo* keys_to_collect,
281 size_t num_keys_to_collect,
282 google::protobuf::RepeatedPtrField<
283 ClientIncidentReport_EnvironmentData_OS_RegistryKey>* key_data) {
284 using RegistryKeyProto = ClientIncidentReport_EnvironmentData_OS_RegistryKey;
285 for (size_t i = 0; i < num_keys_to_collect; ++i) {
286 const RegistryKeyInfo& key_info = keys_to_collect[i];
287 base::win::RegKey reg_key(key_info.rootkey, key_info.subkey,
288 kKeyReadNoNotify);
289 if (reg_key.Valid()) {
290 RegistryKeyProto* regkey_pb = key_data->Add();
291 std::wstring rootkey_name = HKEYToString(key_info.rootkey);
292 rootkey_name += L"\\";
293 rootkey_name += key_info.subkey;
294 regkey_pb->set_name(base::WideToUTF8(rootkey_name));
295 CollectRegistryDataForKey(reg_key, regkey_pb);
300 void CollectPlatformProcessData(
301 ClientIncidentReport_EnvironmentData_Process* process) {
302 CollectDlls(process);
303 RecordLspFeature(process);
304 CollectDllBlacklistData(process);
305 CollectModuleVerificationData(
306 kModulesToVerify, arraysize(kModulesToVerify), process);
309 void CollectPlatformOSData(ClientIncidentReport_EnvironmentData_OS* os_data) {
310 const std::string reg_data_param_value = variations::GetVariationParamValue(
311 "SafeBrowsingIncidentReportingService", "collect_reg_data");
312 if (reg_data_param_value == "true") {
313 CollectRegistryData(kRegKeysToCollect, arraysize(kRegKeysToCollect),
314 os_data->mutable_registry_key());
317 } // namespace safe_browsing