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