1 // Copyright 2018 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 "chrome/credential_provider/gaiacp/gcp_utils.h"
6
7 #include <iphlpapi.h>
8 #include <wincred.h> // For <ntsecapi.h>
9 #include <windows.h>
10 #include <winsock2.h>
11 #include <winternl.h>
12
13 #define _NTDEF_ // Prevent redefition errors, must come after <winternl.h>
14 #include <ntsecapi.h> // For LsaLookupAuthenticationPackage()
15 #include <sddl.h> // For ConvertSidToStringSid()
16 #include <security.h> // For NEGOSSP_NAME_A
17 #include <wbemidl.h>
18
19 #include <atlbase.h>
20 #include <atlcom.h>
21 #include <atlcomcli.h>
22
23 #include <malloc.h>
24 #include <memory.h>
25 #include <stdlib.h>
26
27 #include <iomanip>
28 #include <memory>
29
30 #include "base/base64.h"
31 #include "base/command_line.h"
32 #include "base/files/file.h"
33 #include "base/files/file_enumerator.h"
34 #include "base/files/file_path.h"
35 #include "base/files/file_util.h"
36 #include "base/json/json_reader.h"
37 #include "base/json/json_writer.h"
38 #include "base/macros.h"
39 #include "base/no_destructor.h"
40 #include "base/path_service.h"
41 #include "base/stl_util.h"
42 #include "base/strings/string_util.h"
43 #include "base/strings/stringprintf.h"
44 #include "base/strings/utf_string_conversions.h"
45 #include "base/win/current_module.h"
46 #include "base/win/embedded_i18n/language_selector.h"
47 #include "base/win/win_util.h"
48 #include "base/win/wmi.h"
49 #include "build/branding_buildflags.h"
50 #include "chrome/common/chrome_version.h"
51 #include "chrome/credential_provider/common/gcp_strings.h"
52 #include "chrome/credential_provider/gaiacp/gaia_resources.h"
53 #include "chrome/credential_provider/gaiacp/gcpw_strings.h"
54 #include "chrome/credential_provider/gaiacp/logging.h"
55 #include "chrome/credential_provider/gaiacp/reg_utils.h"
56 #include "chrome/credential_provider/gaiacp/token_generator.h"
57 #include "chrome/installer/launcher_support/chrome_launcher_support.h"
58 #include "google_apis/gaia/gaia_auth_util.h"
59 #include "google_apis/gaia/gaia_switches.h"
60 #include "google_apis/gaia/gaia_urls.h"
61 #include "third_party/re2/src/re2/re2.h"
62
63 namespace credential_provider {
64
65 const wchar_t kDefaultProfilePictureFileExtension[] = L".jpg";
66
67 const base::FilePath::CharType kCredentialProviderFolder[] =
68 L"Credential Provider";
69
70 // Overridden in tests to fake serial number extraction.
71 bool g_use_test_serial_number = false;
72 base::string16 g_test_serial_number = L"";
73
74 // Overridden in tests to fake mac address extraction.
75 bool g_use_test_mac_addresses = false;
76 std::vector<std::string> g_test_mac_addresses;
77
78 // Overriden in tests to fake os version.
79 bool g_use_test_os_version = false;
80 std::string g_test_os_version = "";
81
82 // Overridden in tests to fake installed chrome path.
83 bool g_use_test_chrome_path = false;
84 base::FilePath g_test_chrome_path(L"");
85
86 const wchar_t kKernelLibFile[] = L"kernel32.dll";
87 const int kVersionStringSize = 128;
88
89 constexpr wchar_t kDefaultMdmUrl[] =
90 L"https://deviceenrollmentforwindows.googleapis.com/v1/discovery";
91
92 constexpr int kMaxNumConsecutiveUploadDeviceFailures = 3;
93 const base::TimeDelta kMaxTimeDeltaSinceLastUserPolicyRefresh =
94 base::TimeDelta::FromDays(1);
95
96 namespace {
97
98 // Minimum supported version of Chrome for GCPW.
99 constexpr char kMinimumSupportedChromeVersionStr[] = "77.0.3865.65";
100
101 constexpr char kSentinelFilename[] = "gcpw_startup.sentinel";
102 constexpr int64_t kMaxConsecutiveCrashCount = 5;
103
104 // L$ prefix means this secret can only be accessed locally.
105 const wchar_t kLsaKeyDMTokenPrefix[] = L"L$GCPW-DM-Token-";
106
107 constexpr base::win::i18n::LanguageSelector::LangToOffset
108 kLanguageOffsetPairs[] = {
109 #define HANDLE_LANGUAGE(l_, o_) {L## #l_, o_},
110 DO_LANGUAGES
111 #undef HANDLE_LANGUAGE
112 };
113
GetStartupSentinelLocation(const base::string16 & version)114 base::FilePath GetStartupSentinelLocation(const base::string16& version) {
115 base::FilePath sentienal_path;
116 if (!base::PathService::Get(base::DIR_COMMON_APP_DATA, &sentienal_path)) {
117 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
118 LOGFN(ERROR) << "PathService::Get(DIR_COMMON_APP_DATA) hr=" << putHR(hr);
119 return base::FilePath();
120 }
121
122 sentienal_path = sentienal_path.Append(GetInstallParentDirectoryName())
123 .Append(kCredentialProviderFolder);
124
125 return sentienal_path.Append(version).AppendASCII(kSentinelFilename);
126 }
127
GetLanguageSelector()128 const base::win::i18n::LanguageSelector& GetLanguageSelector() {
129 static base::NoDestructor<base::win::i18n::LanguageSelector> instance(
130 base::string16(), kLanguageOffsetPairs);
131 return *instance;
132 }
133
134 // Opens |path| with options that prevent the file from being read or written
135 // via another handle. As long as the returned object is alive, it is guaranteed
136 // that |path| isn't in use. It can however be deleted.
GetFileLock(const base::FilePath & path)137 base::File GetFileLock(const base::FilePath& path) {
138 return base::File(path, base::File::FLAG_OPEN | base::File::FLAG_READ |
139 base::File::FLAG_EXCLUSIVE_READ |
140 base::File::FLAG_EXCLUSIVE_WRITE |
141 base::File::FLAG_SHARE_DELETE);
142 }
143
144 // Deletes a specific GCP version from the disk.
DeleteVersionDirectory(const base::FilePath & version_path)145 void DeleteVersionDirectory(const base::FilePath& version_path) {
146 // Lock all exes and dlls for exclusive access while allowing deletes. Mark
147 // the files for deletion and release them, causing them to actually be
148 // deleted. This allows the deletion of the version path itself.
149 std::vector<base::File> locks;
150 const int types = base::FileEnumerator::FILES;
151 base::FileEnumerator enumerator_version(version_path, false, types,
152 FILE_PATH_LITERAL("*"));
153 bool all_deletes_succeeded = true;
154 for (base::FilePath path = enumerator_version.Next(); !path.empty();
155 path = enumerator_version.Next()) {
156 if (!path.MatchesExtension(FILE_PATH_LITERAL(".exe")) &&
157 !path.MatchesExtension(FILE_PATH_LITERAL(".dll"))) {
158 continue;
159 }
160
161 // Open the file for exclusive access while allowing deletes.
162 locks.push_back(GetFileLock(path));
163 if (!locks.back().IsValid()) {
164 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
165 LOGFN(ERROR) << "Could not lock " << path << " hr=" << putHR(hr);
166 all_deletes_succeeded = false;
167 continue;
168 }
169
170 // Mark the file for deletion.
171 HRESULT hr = base::DeleteFile(path);
172 if (FAILED(hr)) {
173 LOGFN(ERROR) << "Could not delete " << path;
174 all_deletes_succeeded = false;
175 }
176 }
177
178 // Release the locks, actually deleting the files. It is now possible to
179 // delete the version path.
180 locks.clear();
181 if (all_deletes_succeeded && !base::DeletePathRecursively(version_path))
182 LOGFN(ERROR) << "Could not delete version " << version_path.BaseName();
183 }
184
185 // Reads the dm token for |sid| from lsa store and writes into |token| output
186 // parameter. If |refresh| is true, token is re-generated before returning.
GetGCPWDmTokenInternal(const base::string16 & sid,base::string16 * token,bool refresh)187 HRESULT GetGCPWDmTokenInternal(const base::string16& sid,
188 base::string16* token,
189 bool refresh) {
190 DCHECK(token);
191
192 base::string16 store_key = kLsaKeyDMTokenPrefix + sid;
193
194 auto policy = ScopedLsaPolicy::Create(POLICY_ALL_ACCESS);
195
196 if (!policy) {
197 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
198 LOGFN(ERROR) << "ScopedLsaPolicy::Create hr=" << putHR(hr);
199 return hr;
200 }
201
202 if (refresh) {
203 if (policy->PrivateDataExists(store_key.c_str())) {
204 HRESULT hr = policy->RemovePrivateData(store_key.c_str());
205 if (FAILED(hr)) {
206 LOGFN(ERROR) << "ScopedLsaPolicy::RemovePrivateData hr=" << putHR(hr);
207 return hr;
208 }
209 }
210
211 base::string16 new_token =
212 base::UTF8ToUTF16(TokenGenerator::Get()->GenerateToken());
213
214 HRESULT hr = policy->StorePrivateData(store_key.c_str(), new_token.c_str());
215 if (FAILED(hr)) {
216 LOGFN(ERROR) << "ScopedLsaPolicy::StorePrivateData hr=" << putHR(hr);
217 return hr;
218 }
219
220 *token = new_token;
221 } else {
222 wchar_t dm_token_lsa_data[1024];
223 HRESULT hr = policy->RetrievePrivateData(
224 store_key.c_str(), dm_token_lsa_data, base::size(dm_token_lsa_data));
225 if (FAILED(hr)) {
226 LOGFN(ERROR) << "ScopedLsaPolicy::RetrievePrivateData hr=" << putHR(hr);
227 return hr;
228 }
229
230 *token = dm_token_lsa_data;
231 }
232
233 return S_OK;
234 }
235
236 } // namespace
237
238 // GoogleRegistrationDataForTesting //////////////////////////////////////////
239
GoogleRegistrationDataForTesting(base::string16 serial_number)240 GoogleRegistrationDataForTesting::GoogleRegistrationDataForTesting(
241 base::string16 serial_number) {
242 g_use_test_serial_number = true;
243 g_test_serial_number = serial_number;
244 }
245
~GoogleRegistrationDataForTesting()246 GoogleRegistrationDataForTesting::~GoogleRegistrationDataForTesting() {
247 g_use_test_serial_number = false;
248 g_test_serial_number = L"";
249 }
250
251 // GoogleRegistrationDataForTesting //////////////////////////////////////////
252
253 // GemDeviceDetailsForTesting //////////////////////////////////////////
254
GemDeviceDetailsForTesting(std::vector<std::string> & mac_addresses,std::string os_version)255 GemDeviceDetailsForTesting::GemDeviceDetailsForTesting(
256 std::vector<std::string>& mac_addresses,
257 std::string os_version) {
258 g_use_test_mac_addresses = true;
259 g_use_test_os_version = true;
260 g_test_mac_addresses = mac_addresses;
261 g_test_os_version = os_version;
262 }
263
~GemDeviceDetailsForTesting()264 GemDeviceDetailsForTesting::~GemDeviceDetailsForTesting() {
265 g_use_test_mac_addresses = false;
266 g_use_test_os_version = false;
267 }
268
269 // GemDeviceDetailsForTesting //////////////////////////////////////////
270
271 // GoogleChromePathForTesting ////////////////////////////////////////////////
272
GoogleChromePathForTesting(base::FilePath file_path)273 GoogleChromePathForTesting::GoogleChromePathForTesting(
274 base::FilePath file_path) {
275 g_use_test_chrome_path = true;
276 g_test_chrome_path = file_path;
277 }
278
~GoogleChromePathForTesting()279 GoogleChromePathForTesting::~GoogleChromePathForTesting() {
280 g_use_test_chrome_path = false;
281 g_test_chrome_path = base::FilePath(L"");
282 }
283
284 // GoogleChromePathForTesting /////////////////////////////////////////////////
285
GetInstallDirectory()286 base::FilePath GetInstallDirectory() {
287 base::FilePath dest_path;
288 if (!base::PathService::Get(base::DIR_PROGRAM_FILES, &dest_path)) {
289 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
290 LOGFN(ERROR) << "PathService::Get(DIR_PROGRAM_FILES) hr=" << putHR(hr);
291 return base::FilePath();
292 }
293
294 dest_path = dest_path.Append(GetInstallParentDirectoryName())
295 .Append(kCredentialProviderFolder);
296
297 return dest_path;
298 }
299
DeleteVersionsExcept(const base::FilePath & gcp_path,const base::string16 & product_version)300 void DeleteVersionsExcept(const base::FilePath& gcp_path,
301 const base::string16& product_version) {
302 base::FilePath version = base::FilePath(product_version);
303 const int types = base::FileEnumerator::DIRECTORIES;
304 base::FileEnumerator enumerator(gcp_path, false, types,
305 FILE_PATH_LITERAL("*"));
306 for (base::FilePath name = enumerator.Next(); !name.empty();
307 name = enumerator.Next()) {
308 base::FilePath basename = name.BaseName();
309 if (version == basename)
310 continue;
311
312 // Found an older version on the machine that can be deleted. This is
313 // best effort only. If any errors occurred they are logged by
314 // DeleteVersionDirectory().
315 DeleteVersionDirectory(gcp_path.Append(basename));
316 DeleteStartupSentinelForVersion(basename.value());
317 }
318 }
319
320 // StdParentHandles ///////////////////////////////////////////////////////////
321
StdParentHandles()322 StdParentHandles::StdParentHandles() {}
323
~StdParentHandles()324 StdParentHandles::~StdParentHandles() {}
325
326 // ScopedStartupInfo //////////////////////////////////////////////////////////
327
ScopedStartupInfo()328 ScopedStartupInfo::ScopedStartupInfo() {
329 memset(&info_, 0, sizeof(info_));
330 info_.hStdInput = INVALID_HANDLE_VALUE;
331 info_.hStdOutput = INVALID_HANDLE_VALUE;
332 info_.hStdError = INVALID_HANDLE_VALUE;
333 info_.cb = sizeof(info_);
334 }
335
ScopedStartupInfo(const wchar_t * desktop)336 ScopedStartupInfo::ScopedStartupInfo(const wchar_t* desktop)
337 : ScopedStartupInfo() {
338 DCHECK(desktop);
339 desktop_.assign(desktop);
340 info_.lpDesktop = const_cast<wchar_t*>(desktop_.c_str());
341 }
342
~ScopedStartupInfo()343 ScopedStartupInfo::~ScopedStartupInfo() {
344 Shutdown();
345 }
346
SetStdHandles(base::win::ScopedHandle * hstdin,base::win::ScopedHandle * hstdout,base::win::ScopedHandle * hstderr)347 HRESULT ScopedStartupInfo::SetStdHandles(base::win::ScopedHandle* hstdin,
348 base::win::ScopedHandle* hstdout,
349 base::win::ScopedHandle* hstderr) {
350 if ((info_.dwFlags & STARTF_USESTDHANDLES) == STARTF_USESTDHANDLES) {
351 LOGFN(ERROR) << "Already set";
352 return E_UNEXPECTED;
353 }
354
355 // CreateProcessWithTokenW will fail if any of the std handles provided are
356 // invalid and the STARTF_USESTDHANDLES flag is set. So supply the default
357 // standard handle if no handle is given for some of the handles. This tells
358 // the process it can create its own local handles for these pipes as needed.
359 info_.dwFlags |= STARTF_USESTDHANDLES;
360 if (hstdin && hstdin->IsValid()) {
361 info_.hStdInput = hstdin->Take();
362 } else {
363 info_.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE);
364 }
365 if (hstdout && hstdout->IsValid()) {
366 info_.hStdOutput = hstdout->Take();
367 } else {
368 info_.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE);
369 }
370 if (hstderr && hstderr->IsValid()) {
371 info_.hStdError = hstderr->Take();
372 } else {
373 info_.hStdError = ::GetStdHandle(STD_ERROR_HANDLE);
374 }
375
376 return S_OK;
377 }
378
Shutdown()379 void ScopedStartupInfo::Shutdown() {
380 if ((info_.dwFlags & STARTF_USESTDHANDLES) == STARTF_USESTDHANDLES) {
381 info_.dwFlags &= ~STARTF_USESTDHANDLES;
382
383 if (info_.hStdInput != ::GetStdHandle(STD_INPUT_HANDLE))
384 ::CloseHandle(info_.hStdInput);
385 if (info_.hStdOutput != ::GetStdHandle(STD_OUTPUT_HANDLE))
386 ::CloseHandle(info_.hStdOutput);
387 if (info_.hStdError != ::GetStdHandle(STD_ERROR_HANDLE))
388 ::CloseHandle(info_.hStdError);
389 info_.hStdInput = INVALID_HANDLE_VALUE;
390 info_.hStdOutput = INVALID_HANDLE_VALUE;
391 info_.hStdError = INVALID_HANDLE_VALUE;
392 }
393 }
394
395 // Waits for a process to terminate while capturing output from |output_handle|
396 // to the buffer |output_buffer| of length |buffer_size|. The buffer is expected
397 // to be relatively small. The exit code of the process is written to
398 // |exit_code|.
WaitForProcess(base::win::ScopedHandle::Handle process_handle,const StdParentHandles & parent_handles,DWORD * exit_code,char * output_buffer,int buffer_size)399 HRESULT WaitForProcess(base::win::ScopedHandle::Handle process_handle,
400 const StdParentHandles& parent_handles,
401 DWORD* exit_code,
402 char* output_buffer,
403 int buffer_size) {
404 LOGFN(VERBOSE);
405 DCHECK(exit_code);
406 DCHECK_GT(buffer_size, 0);
407
408 output_buffer[0] = 0;
409
410 HANDLE output_handle = parent_handles.hstdout_read.Get();
411
412 for (bool is_done = false; !is_done;) {
413 char buffer[80];
414 DWORD length = base::size(buffer) - 1;
415 HRESULT hr = S_OK;
416
417 const DWORD kThreeMinutesInMs = 3 * 60 * 1000;
418 DWORD ret = ::WaitForSingleObject(output_handle,
419 kThreeMinutesInMs); // timeout ms
420 switch (ret) {
421 case WAIT_OBJECT_0: {
422 int index = ret - WAIT_OBJECT_0;
423 LOGFN(VERBOSE) << "WAIT_OBJECT_" << index;
424 if (!::ReadFile(output_handle, buffer, length, &length, nullptr)) {
425 hr = HRESULT_FROM_WIN32(::GetLastError());
426 if (hr != HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE))
427 LOGFN(ERROR) << "ReadFile(" << index << ") hr=" << putHR(hr);
428 } else {
429 LOGFN(VERBOSE) << "ReadFile(" << index << ") length=" << length;
430 buffer[length] = 0;
431 }
432 break;
433 }
434 case WAIT_IO_COMPLETION:
435 // This is normal. Just ignore.
436 LOGFN(VERBOSE) << "WaitForMultipleObjectsEx WAIT_IO_COMPLETION";
437 break;
438 case WAIT_TIMEOUT: {
439 // User took too long to log in, so kill UI process.
440 LOGFN(VERBOSE) << "WaitForMultipleObjectsEx WAIT_TIMEOUT, killing UI";
441 ::TerminateProcess(process_handle, kUiecTimeout);
442 is_done = true;
443 break;
444 }
445 case WAIT_FAILED:
446 default: {
447 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
448 LOGFN(ERROR) << "WaitForMultipleObjectsEx hr=" << putHR(hr);
449 is_done = true;
450 break;
451 }
452 }
453
454 // Copy the read buffer to the output buffer. If the pipe was broken,
455 // we can break our loop and wait for the process to die.
456 if (hr == HRESULT_FROM_WIN32(ERROR_BROKEN_PIPE)) {
457 LOGFN(VERBOSE) << "Stop waiting for output buffer";
458 break;
459 } else {
460 strcat_s(output_buffer, buffer_size, buffer);
461 }
462 }
463
464 // At this point both stdout and stderr have been closed. Wait on the process
465 // handle for the process to terminate, getting the exit code. If the
466 // process does not terminate gracefully, kill it before returning.
467 DWORD ret = ::WaitForSingleObject(process_handle, 10000);
468 if (ret == 0) {
469 if (::GetExitCodeProcess(process_handle, exit_code)) {
470 LOGFN(VERBOSE) << "Process terminated with exit code " << *exit_code;
471 } else {
472 LOGFN(WARNING) << "Process terminated without exit code";
473 *exit_code = kUiecAbort;
474 }
475 } else {
476 LOGFN(WARNING) << "UI did not terminiate within 10 seconds, killing now";
477 ::TerminateProcess(process_handle, kUiecKilled);
478 *exit_code = kUiecKilled;
479 }
480
481 return S_OK;
482 }
483
CreateLogonToken(const wchar_t * domain,const wchar_t * username,const wchar_t * password,bool interactive,base::win::ScopedHandle * token)484 HRESULT CreateLogonToken(const wchar_t* domain,
485 const wchar_t* username,
486 const wchar_t* password,
487 bool interactive,
488 base::win::ScopedHandle* token) {
489 DCHECK(domain);
490 DCHECK(username);
491 DCHECK(password);
492 DCHECK(token);
493
494 DWORD logon_type =
495 interactive ? LOGON32_LOGON_INTERACTIVE : LOGON32_LOGON_BATCH;
496 base::win::ScopedHandle::Handle handle;
497
498 if (!::LogonUserW(username, domain, password, logon_type,
499 LOGON32_PROVIDER_DEFAULT, &handle)) {
500 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
501 LOGFN(ERROR) << "LogonUserW hr=" << putHR(hr);
502 return hr;
503 }
504 base::win::ScopedHandle primary_token(handle);
505
506 if (!::CreateRestrictedToken(primary_token.Get(), DISABLE_MAX_PRIVILEGE, 0,
507 nullptr, 0, nullptr, 0, nullptr, &handle)) {
508 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
509 LOGFN(ERROR) << "CreateRestrictedToken hr=" << putHR(hr);
510 return hr;
511 }
512 token->Set(handle);
513 return S_OK;
514 }
515
CreateJobForSignin(base::win::ScopedHandle * job)516 HRESULT CreateJobForSignin(base::win::ScopedHandle* job) {
517 LOGFN(VERBOSE);
518 DCHECK(job);
519
520 job->Set(::CreateJobObject(nullptr, nullptr));
521 if (!job->IsValid()) {
522 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
523 LOGFN(ERROR) << "CreateJobObject hr=" << putHR(hr);
524 return hr;
525 }
526
527 JOBOBJECT_BASIC_UI_RESTRICTIONS ui;
528 ui.UIRestrictionsClass =
529 JOB_OBJECT_UILIMIT_DESKTOP | // Create/switch desktops.
530 JOB_OBJECT_UILIMIT_HANDLES | // Only access own handles.
531 JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS | // Cannot set sys params.
532 JOB_OBJECT_UILIMIT_WRITECLIPBOARD; // Cannot write to clipboard.
533 if (!::SetInformationJobObject(job->Get(), JobObjectBasicUIRestrictions, &ui,
534 sizeof(ui))) {
535 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
536 LOGFN(ERROR) << "SetInformationJobObject hr=" << putHR(hr);
537 return hr;
538 }
539
540 return S_OK;
541 }
542
CreatePipeForChildProcess(bool child_reads,bool use_nul,base::win::ScopedHandle * reading,base::win::ScopedHandle * writing)543 HRESULT CreatePipeForChildProcess(bool child_reads,
544 bool use_nul,
545 base::win::ScopedHandle* reading,
546 base::win::ScopedHandle* writing) {
547 // Make sure that all handles created here are inheritable. It is important
548 // that the child side handle is inherited.
549 SECURITY_ATTRIBUTES sa;
550 sa.nLength = sizeof(sa);
551 sa.bInheritHandle = TRUE;
552 sa.lpSecurityDescriptor = nullptr;
553
554 if (use_nul) {
555 base::win::ScopedHandle h(
556 ::CreateFileW(L"nul:", FILE_GENERIC_READ | FILE_GENERIC_WRITE,
557 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
558 &sa, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
559 if (!h.IsValid()) {
560 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
561 LOGFN(ERROR) << "CreateFile(nul) hr=" << putHR(hr);
562 return hr;
563 }
564
565 if (child_reads) {
566 reading->Set(h.Take());
567 } else {
568 writing->Set(h.Take());
569 }
570 } else {
571 base::win::ScopedHandle::Handle temp_handle1;
572 base::win::ScopedHandle::Handle temp_handle2;
573 if (!::CreatePipe(&temp_handle1, &temp_handle2, &sa, 0)) {
574 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
575 LOGFN(ERROR) << "CreatePipe(reading) hr=" << putHR(hr);
576 return hr;
577 }
578 reading->Set(temp_handle1);
579 writing->Set(temp_handle2);
580
581 // Make sure parent side is not inherited.
582 if (!::SetHandleInformation(child_reads ? writing->Get() : reading->Get(),
583 HANDLE_FLAG_INHERIT, 0)) {
584 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
585 LOGFN(ERROR) << "SetHandleInformation(parent) hr=" << putHR(hr);
586 return hr;
587 }
588 }
589
590 return S_OK;
591 }
592
InitializeStdHandles(CommDirection direction,StdHandlesToCreate to_create,ScopedStartupInfo * startupinfo,StdParentHandles * parent_handles)593 HRESULT InitializeStdHandles(CommDirection direction,
594 StdHandlesToCreate to_create,
595 ScopedStartupInfo* startupinfo,
596 StdParentHandles* parent_handles) {
597 LOGFN(VERBOSE);
598 DCHECK(startupinfo);
599 DCHECK(parent_handles);
600
601 base::win::ScopedHandle hstdin_read;
602 base::win::ScopedHandle hstdin_write;
603 if ((to_create & kStdInput) != 0) {
604 HRESULT hr = CreatePipeForChildProcess(
605 true, // child reads
606 direction == CommDirection::kChildToParentOnly, // use nul
607 &hstdin_read, &hstdin_write);
608 if (FAILED(hr)) {
609 LOGFN(ERROR) << "CreatePipeForChildProcess(stdin) hr=" << putHR(hr);
610 return hr;
611 }
612 }
613
614 base::win::ScopedHandle hstdout_read;
615 base::win::ScopedHandle hstdout_write;
616 if ((to_create & kStdOutput) != 0) {
617 HRESULT hr = CreatePipeForChildProcess(
618 false, // child reads
619 direction == CommDirection::kParentToChildOnly, // use nul
620 &hstdout_read, &hstdout_write);
621 if (FAILED(hr)) {
622 LOGFN(ERROR) << "CreatePipeForChildProcess(stdout) hr=" << putHR(hr);
623 return hr;
624 }
625 }
626
627 base::win::ScopedHandle hstderr_read;
628 base::win::ScopedHandle hstderr_write;
629 if ((to_create & kStdError) != 0) {
630 HRESULT hr = CreatePipeForChildProcess(
631 false, // child reads
632 direction == CommDirection::kParentToChildOnly, // use nul
633 &hstderr_read, &hstderr_write);
634 if (FAILED(hr)) {
635 LOGFN(ERROR) << "CreatePipeForChildProcess(stderr) hr=" << putHR(hr);
636 return hr;
637 }
638 }
639
640 HRESULT hr =
641 startupinfo->SetStdHandles(&hstdin_read, &hstdout_write, &hstderr_write);
642 if (FAILED(hr)) {
643 LOGFN(ERROR) << "startupinfo->SetStdHandles hr=" << putHR(hr);
644 return hr;
645 }
646
647 parent_handles->hstdin_write.Set(hstdin_write.Take());
648 parent_handles->hstdout_read.Set(hstdout_read.Take());
649 parent_handles->hstderr_read.Set(hstderr_read.Take());
650 return S_OK;
651 }
652
GetPathToDllFromHandle(HINSTANCE dll_handle,base::FilePath * path_to_dll)653 HRESULT GetPathToDllFromHandle(HINSTANCE dll_handle,
654 base::FilePath* path_to_dll) {
655 wchar_t path[MAX_PATH];
656 DWORD length = base::size(path);
657 length = ::GetModuleFileName(dll_handle, path, length);
658 if (length == 0 || length >= base::size(path)) {
659 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
660 LOGFN(ERROR) << "GetModuleFileNameW hr=" << putHR(hr);
661 return hr;
662 }
663
664 *path_to_dll = base::FilePath(base::StringPiece16(path, length));
665 return S_OK;
666 }
667
GetEntryPointArgumentForRunDll(HINSTANCE dll_handle,const wchar_t * entrypoint,base::string16 * entrypoint_arg)668 HRESULT GetEntryPointArgumentForRunDll(HINSTANCE dll_handle,
669 const wchar_t* entrypoint,
670 base::string16* entrypoint_arg) {
671 DCHECK(entrypoint);
672 DCHECK(entrypoint_arg);
673
674 entrypoint_arg->clear();
675
676 // rundll32 expects the first command line argument to be the path to the
677 // DLL, followed by a comma and the name of the function to call. There can
678 // be no spaces around the comma. The dll path is quoted because short names
679 // may be disabled in the system and path can not have space otherwise. It is
680 // recommended to use the short path name of the DLL.
681 base::FilePath path_to_dll;
682 HRESULT hr = GetPathToDllFromHandle(dll_handle, &path_to_dll);
683 if (FAILED(hr))
684 return hr;
685
686 wchar_t short_path[MAX_PATH];
687 DWORD short_length = base::size(short_path);
688 short_length =
689 ::GetShortPathName(path_to_dll.value().c_str(), short_path, short_length);
690 if (short_length >= base::size(short_path)) {
691 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
692 LOGFN(ERROR) << "GetShortPathNameW hr=" << putHR(hr);
693 return hr;
694 }
695
696 *entrypoint_arg = base::string16(
697 base::StringPrintf(L"\"%ls\",%ls", short_path, entrypoint));
698
699 // In tests, the current module is the unittest exe, not the real dll.
700 // The unittest exe does not expose entrypoints, so return S_FALSE as a hint
701 // that this will not work. The command line is built anyway though so
702 // tests of the command line construction can be written.
703 return wcsicmp(wcsrchr(path_to_dll.value().c_str(), L'.'), L".dll") == 0
704 ? S_OK
705 : S_FALSE;
706 }
707
GetCommandLineForEntrypoint(HINSTANCE dll_handle,const wchar_t * entrypoint,base::CommandLine * command_line)708 HRESULT GetCommandLineForEntrypoint(HINSTANCE dll_handle,
709 const wchar_t* entrypoint,
710 base::CommandLine* command_line) {
711 DCHECK(entrypoint);
712 DCHECK(command_line);
713
714 // Build the full path to rundll32.
715 base::FilePath system_dir;
716 if (!base::PathService::Get(base::DIR_SYSTEM, &system_dir))
717 return HRESULT_FROM_WIN32(::GetLastError());
718
719 command_line->SetProgram(
720 system_dir.Append(FILE_PATH_LITERAL("rundll32.exe")));
721
722 base::string16 entrypoint_arg;
723 HRESULT hr =
724 GetEntryPointArgumentForRunDll(dll_handle, entrypoint, &entrypoint_arg);
725 if (SUCCEEDED(hr))
726 command_line->AppendArgNative(entrypoint_arg);
727
728 return hr;
729 }
730
731 // Gets localized name for builtin administrator account. Extracting
732 // localized name for builtin administrator account requires DomainSid
733 // to be passed onto the CreateWellKnownSid function unlike any other
734 // WellKnownSid as per microsoft documentation. That's why we need to
735 // first extract the DomainSid (even for local accounts) and pass it as
736 // a parameter to the CreateWellKnownSid function call.
GetLocalizedNameBuiltinAdministratorAccount(base::string16 * builtin_localized_admin_name)737 HRESULT GetLocalizedNameBuiltinAdministratorAccount(
738 base::string16* builtin_localized_admin_name) {
739 LSA_HANDLE PolicyHandle;
740 LSA_OBJECT_ATTRIBUTES oa = {sizeof(oa)};
741 NTSTATUS status =
742 LsaOpenPolicy(0, &oa, POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle);
743 if (status >= 0) {
744 PPOLICY_ACCOUNT_DOMAIN_INFO ppadi;
745 status = LsaQueryInformationPolicy(
746 PolicyHandle, PolicyAccountDomainInformation, (void**)&ppadi);
747 if (status >= 0) {
748 BYTE well_known_sid[SECURITY_MAX_SID_SIZE];
749 DWORD size_local_users_group_sid = base::size(well_known_sid);
750 if (CreateWellKnownSid(::WinAccountAdministratorSid, ppadi->DomainSid,
751 well_known_sid, &size_local_users_group_sid)) {
752 return LookupLocalizedNameBySid(well_known_sid,
753 builtin_localized_admin_name);
754 } else {
755 status = GetLastError();
756 }
757 LsaFreeMemory(ppadi);
758 }
759 LsaClose(PolicyHandle);
760 }
761 return status >= 0 ? S_OK : E_FAIL;
762 }
763
LookupLocalizedNameBySid(PSID sid,base::string16 * localized_name)764 HRESULT LookupLocalizedNameBySid(PSID sid, base::string16* localized_name) {
765 DCHECK(localized_name);
766 std::vector<wchar_t> localized_name_buffer;
767 DWORD group_name_size = 0;
768 std::vector<wchar_t> domain_buffer;
769 DWORD domain_size = 0;
770 SID_NAME_USE use;
771
772 // Get the localized name of the local users group. The function
773 // NetLocalGroupAddMembers only accepts the name of the group and it
774 // may be localized on the system.
775 if (!::LookupAccountSidW(nullptr, sid, nullptr, &group_name_size, nullptr,
776 &domain_size, &use)) {
777 if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
778 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
779 LOGFN(ERROR) << "LookupAccountSidW hr=" << putHR(hr);
780 return hr;
781 }
782
783 localized_name_buffer.resize(group_name_size);
784 domain_buffer.resize(domain_size);
785 if (!::LookupAccountSidW(nullptr, sid, localized_name_buffer.data(),
786 &group_name_size, domain_buffer.data(),
787 &domain_size, &use)) {
788 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
789 LOGFN(ERROR) << "LookupAccountSidW hr=" << putHR(hr);
790 return hr;
791 }
792 }
793
794 if (localized_name_buffer.empty()) {
795 LOGFN(ERROR) << "Empty localized name";
796 return E_UNEXPECTED;
797 }
798 *localized_name = base::string16(localized_name_buffer.data(),
799 localized_name_buffer.size() - 1);
800
801 return S_OK;
802 }
803
LookupLocalizedNameForWellKnownSid(WELL_KNOWN_SID_TYPE sid_type,base::string16 * localized_name)804 HRESULT LookupLocalizedNameForWellKnownSid(WELL_KNOWN_SID_TYPE sid_type,
805 base::string16* localized_name) {
806 BYTE well_known_sid[SECURITY_MAX_SID_SIZE];
807 DWORD size_local_users_group_sid = base::size(well_known_sid);
808
809 // Get the sid for the well known local users group.
810 if (!::CreateWellKnownSid(sid_type, nullptr, well_known_sid,
811 &size_local_users_group_sid)) {
812 HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
813 LOGFN(ERROR) << "CreateWellKnownSid hr=" << putHR(hr);
814 return hr;
815 }
816
817 return LookupLocalizedNameBySid(well_known_sid, localized_name);
818 }
819
WriteToStartupSentinel()820 bool WriteToStartupSentinel() {
821 // Always try to write to the startup sentinel file. If writing or opening
822 // fails for any reason (file locked, no access etc) consider this a failure.
823 // If no sentinel file path can be found this probably means that we are
824 // running in a unit test so just let the verification pass in this case.
825 // Each process will only write once to startup sentinel file.
826
827 static volatile long sentinel_initialized = 0;
828 if (::InterlockedCompareExchange(&sentinel_initialized, 1, 0))
829 return true;
830
831 base::FilePath startup_sentinel_path =
832 GetStartupSentinelLocation(TEXT(CHROME_VERSION_STRING));
833 if (!startup_sentinel_path.empty()) {
834 base::FilePath startup_sentinel_directory = startup_sentinel_path.DirName();
835 if (!base::DirectoryExists(startup_sentinel_directory)) {
836 base::File::Error error;
837 if (!base::CreateDirectoryAndGetError(startup_sentinel_directory,
838 &error)) {
839 LOGFN(ERROR) << "Could not create sentinel directory='"
840 << startup_sentinel_directory << "' error=" << error;
841 return false;
842 }
843 }
844 base::File startup_sentinel(
845 startup_sentinel_path,
846 base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
847
848 // Keep writing to the sentinel file until we have reached
849 // |kMaxConsecutiveCrashCount| at which point it is assumed that GCPW
850 // is crashing continuously and should be disabled.
851 if (!startup_sentinel.IsValid()) {
852 LOGFN(ERROR) << "Could not open the sentinel path "
853 << startup_sentinel_path.value();
854 return false;
855 }
856
857 if (startup_sentinel.GetLength() >= kMaxConsecutiveCrashCount) {
858 LOGFN(ERROR) << "Sentinel file length indicates "
859 << startup_sentinel.GetLength() << " possible crashes";
860 return false;
861 }
862
863 return startup_sentinel.WriteAtCurrentPos("0", 1) == 1;
864 }
865
866 return true;
867 }
868
DeleteStartupSentinel()869 void DeleteStartupSentinel() {
870 DeleteStartupSentinelForVersion(TEXT(CHROME_VERSION_STRING));
871 }
872
DeleteStartupSentinelForVersion(const base::string16 & version)873 void DeleteStartupSentinelForVersion(const base::string16& version) {
874 base::FilePath startup_sentinel_path = GetStartupSentinelLocation(version);
875 if (base::PathExists(startup_sentinel_path) &&
876 !base::DeleteFile(startup_sentinel_path)) {
877 LOGFN(ERROR) << "Failed to delete sentinel file: " << startup_sentinel_path;
878 }
879 }
880
GetStringResource(int base_message_id)881 base::string16 GetStringResource(int base_message_id) {
882 base::string16 localized_string;
883
884 int message_id = base_message_id + GetLanguageSelector().offset();
885 const ATLSTRINGRESOURCEIMAGE* image =
886 AtlGetStringResourceImage(_AtlBaseModule.GetModuleInstance(), message_id);
887 if (image) {
888 localized_string = base::string16(image->achString, image->nLength);
889 } else {
890 NOTREACHED() << "Unable to find resource id " << message_id;
891 }
892
893 return localized_string;
894 }
895
GetStringResource(int base_message_id,const std::vector<base::string16> & subst)896 base::string16 GetStringResource(int base_message_id,
897 const std::vector<base::string16>& subst) {
898 base::string16 format_string = GetStringResource(base_message_id);
899 base::string16 formatted =
900 base::ReplaceStringPlaceholders(format_string, subst, nullptr);
901
902 return formatted;
903 }
904
GetSelectedLanguage()905 base::string16 GetSelectedLanguage() {
906 return GetLanguageSelector().matched_candidate();
907 }
908
SecurelyClearDictionaryValue(base::Optional<base::Value> * value)909 void SecurelyClearDictionaryValue(base::Optional<base::Value>* value) {
910 SecurelyClearDictionaryValueWithKey(value, kKeyPassword);
911 }
912
SecurelyClearDictionaryValueWithKey(base::Optional<base::Value> * value,const std::string & password_key)913 void SecurelyClearDictionaryValueWithKey(base::Optional<base::Value>* value,
914 const std::string& password_key) {
915 if (!value || !(*value) || !((*value)->is_dict()))
916 return;
917
918 const std::string* password_value = (*value)->FindStringKey(password_key);
919 if (password_value) {
920 SecurelyClearString(*const_cast<std::string*>(password_value));
921 }
922
923 (*value).reset();
924 }
925
SecurelyClearString(base::string16 & str)926 void SecurelyClearString(base::string16& str) {
927 SecurelyClearBuffer(const_cast<wchar_t*>(str.data()),
928 str.size() * sizeof(decltype(str[0])));
929 }
930
SecurelyClearString(std::string & str)931 void SecurelyClearString(std::string& str) {
932 SecurelyClearBuffer(const_cast<char*>(str.data()), str.size());
933 }
934
SecurelyClearBuffer(void * buffer,size_t length)935 void SecurelyClearBuffer(void* buffer, size_t length) {
936 if (buffer)
937 ::RtlSecureZeroMemory(buffer, length);
938 }
939
SearchForKeyInStringDictUTF8(const std::string & json_string,const std::initializer_list<base::StringPiece> & path)940 std::string SearchForKeyInStringDictUTF8(
941 const std::string& json_string,
942 const std::initializer_list<base::StringPiece>& path) {
943 DCHECK(path.size() > 0);
944
945 base::Optional<base::Value> json_obj =
946 base::JSONReader::Read(json_string, base::JSON_ALLOW_TRAILING_COMMAS);
947 if (!json_obj || !json_obj->is_dict()) {
948 LOGFN(ERROR) << "base::JSONReader::Read failed to translate to JSON";
949 return std::string();
950 }
951 const std::string* value =
952 json_obj->FindStringPath(base::JoinString(path, "."));
953 return value ? *value : std::string();
954 }
955
GetDictString(const base::Value & dict,const char * name)956 base::string16 GetDictString(const base::Value& dict, const char* name) {
957 DCHECK(name);
958 DCHECK(dict.is_dict());
959 auto* value = dict.FindKey(name);
960 return value && value->is_string() ? base::UTF8ToUTF16(value->GetString())
961 : base::string16();
962 }
963
GetDictString(const std::unique_ptr<base::Value> & dict,const char * name)964 base::string16 GetDictString(const std::unique_ptr<base::Value>& dict,
965 const char* name) {
966 return GetDictString(*dict, name);
967 }
968
GetDictStringUTF8(const base::Value & dict,const char * name)969 std::string GetDictStringUTF8(const base::Value& dict, const char* name) {
970 DCHECK(name);
971 DCHECK(dict.is_dict());
972 auto* value = dict.FindKey(name);
973 return value && value->is_string() ? value->GetString() : std::string();
974 }
975
SearchForListInStringDictUTF8(const std::string & list_key,const std::string & json_string,const std::initializer_list<base::StringPiece> & path,std::vector<std::string> * output)976 HRESULT SearchForListInStringDictUTF8(
977 const std::string& list_key,
978 const std::string& json_string,
979 const std::initializer_list<base::StringPiece>& path,
980 std::vector<std::string>* output) {
981 DCHECK(path.size() > 0);
982
983 base::Optional<base::Value> json_obj =
984 base::JSONReader::Read(json_string, base::JSON_ALLOW_TRAILING_COMMAS);
985 if (!json_obj || !json_obj->is_dict()) {
986 LOGFN(ERROR) << "base::JSONReader::Read failed to translate to JSON";
987 return E_FAIL;
988 }
989
990 auto* value = json_obj->FindListPath(base::JoinString(path, "."));
991 if (value && value->is_list()) {
992 for (const base::Value& entry : value->GetList()) {
993 if (entry.FindKey(list_key) && entry.FindKey(list_key)->is_string()) {
994 std::string value = entry.FindKey(list_key)->GetString();
995 output->push_back(value);
996 } else {
997 return E_FAIL;
998 }
999 }
1000 }
1001 return S_OK;
1002 }
1003
GetDictStringUTF8(const std::unique_ptr<base::Value> & dict,const char * name)1004 std::string GetDictStringUTF8(const std::unique_ptr<base::Value>& dict,
1005 const char* name) {
1006 return GetDictStringUTF8(*dict, name);
1007 }
1008
GetInstallParentDirectoryName()1009 base::FilePath::StringType GetInstallParentDirectoryName() {
1010 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
1011 return FILE_PATH_LITERAL("Google");
1012 #else
1013 return FILE_PATH_LITERAL("Chromium");
1014 #endif
1015 }
1016
GetWindowsVersion()1017 base::string16 GetWindowsVersion() {
1018 wchar_t release_id[32];
1019 ULONG length = base::size(release_id);
1020 HRESULT hr =
1021 GetMachineRegString(L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
1022 L"ReleaseId", release_id, &length);
1023 if (SUCCEEDED(hr))
1024 return release_id;
1025
1026 return L"Unknown";
1027 }
1028
GetMinimumSupportedChromeVersion()1029 base::Version GetMinimumSupportedChromeVersion() {
1030 return base::Version(kMinimumSupportedChromeVersionStr);
1031 }
1032
ExtractKeysFromDict(const base::Value & dict,const std::vector<std::pair<std::string,std::string * >> & needed_outputs)1033 bool ExtractKeysFromDict(
1034 const base::Value& dict,
1035 const std::vector<std::pair<std::string, std::string*>>& needed_outputs) {
1036 if (!dict.is_dict())
1037 return false;
1038
1039 for (const std::pair<std::string, std::string*>& output : needed_outputs) {
1040 const std::string* output_value = dict.FindStringKey(output.first);
1041 if (!output_value) {
1042 LOGFN(ERROR) << "Could not extract value '" << output.first
1043 << "' from server response";
1044 return false;
1045 }
1046 DCHECK(output.second);
1047 *output.second = *output_value;
1048 }
1049 return true;
1050 }
1051
GetSerialNumber()1052 base::string16 GetSerialNumber() {
1053 if (g_use_test_serial_number)
1054 return g_test_serial_number;
1055 return base::win::WmiComputerSystemInfo::Get().serial_number();
1056 }
1057
1058 // This approach was inspired by:
1059 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365917(v=vs.85).aspx
GetMacAddresses()1060 std::vector<std::string> GetMacAddresses() {
1061 // Used for unit tests.
1062 if (g_use_test_mac_addresses)
1063 return g_test_mac_addresses;
1064
1065 PIP_ADAPTER_INFO pAdapter;
1066 ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
1067 IP_ADAPTER_INFO* pAdapterInfo =
1068 new IP_ADAPTER_INFO[ulOutBufLen / sizeof(IP_ADAPTER_INFO)];
1069 // Get the right buffer size in case of overflow.
1070 if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW) {
1071 delete[] pAdapterInfo;
1072 pAdapterInfo =
1073 new IP_ADAPTER_INFO[ulOutBufLen / sizeof(IP_ADAPTER_INFO) + 1];
1074 }
1075 std::vector<std::string> mac_addresses;
1076 if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_SUCCESS) {
1077 pAdapter = pAdapterInfo;
1078 while (pAdapter) {
1079 if (pAdapter->AddressLength == 6) {
1080 char mac_address[17 + 1];
1081 snprintf(mac_address, sizeof(mac_address),
1082 "%02X-%02X-%02X-%02X-%02X-%02X",
1083 static_cast<unsigned int>(pAdapter->Address[0]),
1084 static_cast<unsigned int>(pAdapter->Address[1]),
1085 static_cast<unsigned int>(pAdapter->Address[2]),
1086 static_cast<unsigned int>(pAdapter->Address[3]),
1087 static_cast<unsigned int>(pAdapter->Address[4]),
1088 static_cast<unsigned int>(pAdapter->Address[5]));
1089 mac_addresses.push_back(mac_address);
1090 }
1091 pAdapter = pAdapter->Next;
1092 }
1093 }
1094 delete[] pAdapterInfo;
1095 return mac_addresses;
1096 }
1097
1098 // The current solution is based on the version of the "kernel32.dll" file. A
1099 // cleaner alternative would be to use the GetVersionEx API. However, since
1100 // Windows 8.1 the values returned by that API are dependent on how
1101 // the application is manifested, and might not be the actual OS version.
GetOsVersion(std::string * version)1102 void GetOsVersion(std::string* version) {
1103 if (g_use_test_os_version) {
1104 *version = g_test_os_version;
1105 return;
1106 }
1107 int buffer_size = GetFileVersionInfoSize(kKernelLibFile, nullptr);
1108 if (buffer_size) {
1109 std::vector<wchar_t> buffer(buffer_size, 0);
1110 if (GetFileVersionInfo(kKernelLibFile, 0, buffer_size, buffer.data())) {
1111 UINT size;
1112 void* fixed_version_info_raw;
1113 if (VerQueryValue(buffer.data(), L"\\", &fixed_version_info_raw, &size)) {
1114 VS_FIXEDFILEINFO* fixed_version_info =
1115 static_cast<VS_FIXEDFILEINFO*>(fixed_version_info_raw);
1116 // https://stackoverflow.com/questions/38068477
1117 int major = HIWORD(fixed_version_info->dwProductVersionMS);
1118 int minor = LOWORD(fixed_version_info->dwProductVersionMS);
1119 int build = HIWORD(fixed_version_info->dwProductVersionLS);
1120 char version_buffer[kVersionStringSize];
1121 snprintf(version_buffer, kVersionStringSize, "%d.%d.%d", major, minor,
1122 build);
1123 *version = version_buffer;
1124 }
1125 }
1126 }
1127 }
1128
GenerateDeviceId(std::string * device_id)1129 HRESULT GenerateDeviceId(std::string* device_id) {
1130 // Build the json data encapsulating different device ids.
1131 base::Value device_ids_dict(base::Value::Type::DICTIONARY);
1132
1133 // Add the serial number to the dictionary.
1134 base::string16 serial_number = GetSerialNumber();
1135 if (!serial_number.empty())
1136 device_ids_dict.SetStringKey("serial_number", serial_number);
1137
1138 // Add machine_guid to the dictionary.
1139 base::string16 machine_guid;
1140 HRESULT hr = GetMachineGuid(&machine_guid);
1141 if (SUCCEEDED(hr) && !machine_guid.empty())
1142 device_ids_dict.SetStringKey("machine_guid", machine_guid);
1143
1144 std::string device_id_str;
1145 bool json_write_result =
1146 base::JSONWriter::Write(device_ids_dict, &device_id_str);
1147 if (!json_write_result) {
1148 LOGFN(ERROR) << "JSONWriter::Write(device_ids_dict)";
1149 return E_FAIL;
1150 }
1151
1152 // Store the base64encoded device id json blob in the output.
1153 base::Base64Encode(device_id_str, device_id);
1154 return S_OK;
1155 }
1156
SetGaiaEndpointCommandLineIfNeeded(const wchar_t * override_registry_key,const std::string & default_endpoint,bool provide_deviceid,bool show_tos,base::CommandLine * command_line)1157 HRESULT SetGaiaEndpointCommandLineIfNeeded(const wchar_t* override_registry_key,
1158 const std::string& default_endpoint,
1159 bool provide_deviceid,
1160 bool show_tos,
1161 base::CommandLine* command_line) {
1162 // Registry specified endpoint.
1163 wchar_t endpoint_url_setting[256];
1164 ULONG endpoint_url_length = base::size(endpoint_url_setting);
1165 HRESULT hr = GetGlobalFlag(override_registry_key, endpoint_url_setting,
1166 &endpoint_url_length);
1167 if (SUCCEEDED(hr) && endpoint_url_setting[0]) {
1168 GURL endpoint_url(endpoint_url_setting);
1169 if (endpoint_url.is_valid()) {
1170 command_line->AppendSwitchASCII(switches::kGaiaUrl,
1171 endpoint_url.GetWithEmptyPath().spec());
1172 command_line->AppendSwitchASCII(kGcpwEndpointPathSwitch,
1173 endpoint_url.path().substr(1));
1174 }
1175 return S_OK;
1176 }
1177
1178 if (provide_deviceid || show_tos) {
1179 std::string device_id;
1180 hr = GenerateDeviceId(&device_id);
1181 if (SUCCEEDED(hr)) {
1182 command_line->AppendSwitchASCII(
1183 kGcpwEndpointPathSwitch,
1184 base::StringPrintf("%s?device_id=%s&show_tos=%d",
1185 default_endpoint.c_str(), device_id.c_str(),
1186 show_tos ? 1 : 0));
1187 } else if (show_tos) {
1188 command_line->AppendSwitchASCII(
1189 kGcpwEndpointPathSwitch,
1190 base::StringPrintf("%s?show_tos=1", default_endpoint.c_str()));
1191 }
1192 }
1193 return S_OK;
1194 }
1195
GetChromePath()1196 base::FilePath GetChromePath() {
1197 base::FilePath gls_path = GetSystemChromePath();
1198
1199 wchar_t custom_gls_path_value[MAX_PATH];
1200 ULONG path_len = base::size(custom_gls_path_value);
1201 HRESULT hr = GetGlobalFlag(kRegGlsPath, custom_gls_path_value, &path_len);
1202 if (SUCCEEDED(hr)) {
1203 base::FilePath custom_gls_path(custom_gls_path_value);
1204 if (base::PathExists(custom_gls_path)) {
1205 gls_path = custom_gls_path;
1206 } else {
1207 LOGFN(ERROR) << "Specified gls path ('" << custom_gls_path.value()
1208 << "') does not exist, using default gls path.";
1209 }
1210 }
1211
1212 return gls_path;
1213 }
1214
GetSystemChromePath()1215 base::FilePath GetSystemChromePath() {
1216 if (g_use_test_chrome_path)
1217 return g_test_chrome_path;
1218
1219 return chrome_launcher_support::GetChromePathForInstallationLevel(
1220 chrome_launcher_support::SYSTEM_LEVEL_INSTALLATION, false);
1221 }
1222
GenerateGCPWDmToken(const base::string16 & sid)1223 HRESULT GenerateGCPWDmToken(const base::string16& sid) {
1224 base::string16 dm_token;
1225 return GetGCPWDmTokenInternal(sid, &dm_token, true);
1226 }
1227
GetGCPWDmToken(const base::string16 & sid,base::string16 * token)1228 HRESULT GetGCPWDmToken(const base::string16& sid, base::string16* token) {
1229 return GetGCPWDmTokenInternal(sid, token, false);
1230 }
1231
FakesForTesting()1232 FakesForTesting::FakesForTesting() {}
1233
~FakesForTesting()1234 FakesForTesting::~FakesForTesting() {}
1235
GetGcpwServiceUrl()1236 GURL GetGcpwServiceUrl() {
1237 base::string16 dev = GetGlobalFlagOrDefault(kRegDeveloperMode, L"");
1238 if (!dev.empty())
1239 return GURL(GetDevelopmentUrl(kDefaultGcpwServiceUrl, dev));
1240
1241 return GURL(kDefaultGcpwServiceUrl);
1242 }
1243
GetDevelopmentUrl(const base::string16 & url,const base::string16 & dev)1244 base::string16 GetDevelopmentUrl(const base::string16& url,
1245 const base::string16& dev) {
1246 std::string project;
1247 std::string final_part;
1248 if (re2::RE2::FullMatch(base::UTF16ToUTF8(url),
1249 "https://(.*).(googleapis.com.*)", &project,
1250 &final_part)) {
1251 std::string url_prefix = "https://" + base::UTF16ToUTF8(dev) + "-";
1252 return base::UTF8ToUTF16(
1253 base::JoinString({url_prefix + project, "sandbox", final_part}, "."));
1254 }
1255 return url;
1256 }
1257
1258 } // namespace credential_provider
1259