1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/install_static/install_util.h"
6 
7 #include <assert.h>
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include <algorithm>
12 #include <iterator>
13 #include <limits>
14 #include <memory>
15 #include <sstream>
16 
17 #include "build/branding_buildflags.h"
18 #include "chrome/chrome_elf/nt_registry/nt_registry.h"
19 #include "chrome/install_static/buildflags.h"
20 #include "chrome/install_static/install_details.h"
21 #include "chrome/install_static/install_modes.h"
22 #include "chrome/install_static/policy_path_parser.h"
23 #include "chrome/install_static/user_data_dir.h"
24 #include "components/nacl/common/buildflags.h"
25 #include "components/version_info/channel.h"
26 
27 namespace install_static {
28 
29 enum class ProcessType {
30   UNINITIALIZED,
31   OTHER_PROCESS,
32   BROWSER_PROCESS,
33   CLOUD_PRINT_SERVICE_PROCESS,
34 #if BUILDFLAG(ENABLE_NACL)
35   NACL_BROKER_PROCESS,
36   NACL_LOADER_PROCESS,
37 #endif
38   CRASHPAD_HANDLER_PROCESS,
39 };
40 
41 // Caches the |ProcessType| of the current process.
42 ProcessType g_process_type = ProcessType::UNINITIALIZED;
43 
44 const wchar_t kRegValueChromeStatsSample[] = L"UsageStatsInSample";
45 
46 // TODO(ananta)
47 // http://crbug.com/604923
48 // The constants defined in this file are also defined in chrome/installer and
49 // other places. we need to unify them.
50 const wchar_t kHeadless[] = L"CHROME_HEADLESS";
51 const wchar_t kShowRestart[] = L"CHROME_CRASHED";
52 const wchar_t kRestartInfo[] = L"CHROME_RESTART";
53 const wchar_t kRtlLocale[] = L"RIGHT_TO_LEFT";
54 
55 const wchar_t kCrashpadHandler[] = L"crashpad-handler";
56 const wchar_t kFallbackHandler[] = L"fallback-handler";
57 
58 const wchar_t kProcessType[] = L"type";
59 const wchar_t kUserDataDirSwitch[] = L"user-data-dir";
60 const wchar_t kUtilityProcess[] = L"utility";
61 
62 namespace {
63 
64 #if BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
65 // TODO(ananta)
66 // http://crbug.com/604923
67 // The constants defined in this file are also defined in chrome/installer and
68 // other places. we need to unify them.
69 // Chrome channel display names.
70 constexpr wchar_t kChromeChannelDev[] = L"dev";
71 constexpr wchar_t kChromeChannelBeta[] = L"beta";
72 constexpr wchar_t kChromeChannelStableExplicit[] = L"stable";
73 #endif
74 
75 // TODO(ananta)
76 // http://crbug.com/604923
77 // These constants are defined in the chrome/installer directory as well. We
78 // need to unify them.
79 #if BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
80 constexpr wchar_t kRegValueAp[] = L"ap";
81 constexpr wchar_t kRegValueName[] = L"name";
82 #endif
83 constexpr wchar_t kRegValueUsageStats[] = L"usagestats";
84 constexpr wchar_t kMetricsReportingEnabled[] = L"MetricsReportingEnabled";
85 
86 constexpr wchar_t kCloudPrintServiceProcess[] = L"service";
87 #if BUILDFLAG(ENABLE_NACL)
88 constexpr wchar_t kNaClBrokerProcess[] = L"nacl-broker";
89 constexpr wchar_t kNaClLoaderProcess[] = L"nacl-loader";
90 #endif
91 
Trace(const wchar_t * format_string,...)92 void Trace(const wchar_t* format_string, ...) {
93   static const int kMaxLogBufferSize = 1024;
94   static wchar_t buffer[kMaxLogBufferSize] = {};
95 
96   va_list args = {};
97 
98   va_start(args, format_string);
99   vswprintf(buffer, kMaxLogBufferSize, format_string, args);
100   OutputDebugStringW(buffer);
101   va_end(args);
102 }
103 
GetLanguageAndCodePageFromVersionResource(const char * version_resource,WORD * language,WORD * code_page)104 bool GetLanguageAndCodePageFromVersionResource(const char* version_resource,
105                                                WORD* language,
106                                                WORD* code_page) {
107   if (!version_resource)
108     return false;
109 
110   struct LanguageAndCodePage {
111     WORD language;
112     WORD code_page;
113   };
114 
115   LanguageAndCodePage* translation_info = nullptr;
116   uint32_t data_size_in_bytes = 0;
117   BOOL query_result = VerQueryValueW(
118       version_resource, L"\\VarFileInfo\\Translation",
119       reinterpret_cast<void**>(&translation_info), &data_size_in_bytes);
120   if (!query_result)
121     return false;
122 
123   *language = translation_info->language;
124   *code_page = translation_info->code_page;
125   return true;
126 }
127 
GetValueFromVersionResource(const char * version_resource,const std::wstring & name,std::wstring * value_str)128 bool GetValueFromVersionResource(const char* version_resource,
129                                  const std::wstring& name,
130                                  std::wstring* value_str) {
131   assert(value_str);
132   value_str->clear();
133 
134   // TODO(ananta)
135   // It may be better in the long run to enumerate the languages and code pages
136   // in the version resource and return the value from the first match.
137   WORD language = 0;
138   WORD code_page = 0;
139   if (!GetLanguageAndCodePageFromVersionResource(version_resource, &language,
140                                                  &code_page)) {
141     return false;
142   }
143 
144   const size_t array_size = 8;
145   WORD lang_codepage[array_size] = {};
146   size_t i = 0;
147   // Use the language and codepage
148   lang_codepage[i++] = language;
149   lang_codepage[i++] = code_page;
150   // Use the default language and codepage from the resource.
151   lang_codepage[i++] = ::GetUserDefaultLangID();
152   lang_codepage[i++] = code_page;
153   // Use the language from the resource and Latin codepage (most common).
154   lang_codepage[i++] = language;
155   lang_codepage[i++] = 1252;
156   // Use the default language and Latin codepage (most common).
157   lang_codepage[i++] = ::GetUserDefaultLangID();
158   lang_codepage[i++] = 1252;
159 
160   static_assert((array_size % 2) == 0,
161                 "Language code page size should be a multiple of 2");
162   assert(array_size == i);
163 
164   for (i = 0; i < array_size;) {
165     wchar_t sub_block[MAX_PATH];
166     WORD language = lang_codepage[i++];
167     WORD code_page = lang_codepage[i++];
168     _snwprintf_s(sub_block, MAX_PATH, MAX_PATH,
169                  L"\\StringFileInfo\\%04hx%04hx\\%ls", language, code_page,
170                  name.c_str());
171     void* value = nullptr;
172     uint32_t size = 0;
173     BOOL r = ::VerQueryValueW(version_resource, sub_block, &value, &size);
174     if (r && value) {
175       value_str->assign(static_cast<wchar_t*>(value));
176       return true;
177     }
178   }
179   return false;
180 }
181 
DirectoryExists(const std::wstring & path)182 bool DirectoryExists(const std::wstring& path) {
183   DWORD file_attributes = ::GetFileAttributes(path.c_str());
184   if (file_attributes == INVALID_FILE_ATTRIBUTES)
185     return false;
186   return (file_attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
187 }
188 
189 // Returns true if the |source| string matches the |pattern|. The pattern
190 // may contain wildcards like '?' which matches one character or a '*'
191 // which matches 0 or more characters.
192 // Please note that pattern matches the whole string. If you want to find
193 // something in the middle of the string then you need to specify the pattern
194 // as '*xyz*'.
195 // |source_index| is the index of the current character being matched in
196 // |source|.
197 // |pattern_index| is the index of the current pattern character in |pattern|
198 // which is matched with source.
MatchPatternImpl(const std::wstring & source,const std::wstring & pattern,size_t source_index,size_t pattern_index)199 bool MatchPatternImpl(const std::wstring& source,
200                       const std::wstring& pattern,
201                       size_t source_index,
202                       size_t pattern_index) {
203   if (source.empty() && pattern.empty())
204     return true;
205 
206   if (source_index > source.length() || pattern_index > pattern.length())
207     return false;
208 
209   // If we reached the end of both strings, then we are done.
210   if ((source_index == source.length()) &&
211       (pattern_index == pattern.length())) {
212     return true;
213   }
214 
215   // If the current character in the pattern is a '*' then make sure that
216   // characters after the pattern are present in the source string. This
217   // assumes that you won't have two consecutive '*' characters in the pattern.
218   if ((pattern[pattern_index] == L'*') &&
219       (pattern_index + 1 < pattern.length()) &&
220       (source_index >= source.length())) {
221     return false;
222   }
223 
224   // If the pattern contains wildcard characters '?' or '.' or there is a match
225   // then move ahead in both strings.
226   if ((pattern[pattern_index] == L'?') ||
227       (pattern[pattern_index] == source[source_index])) {
228     return MatchPatternImpl(source, pattern, source_index + 1,
229                             pattern_index + 1);
230   }
231 
232   // If we have a '*' then there are two possibilities
233   // 1. We consider current character of source.
234   // 2. We ignore current character of source.
235   if (pattern[pattern_index] == L'*') {
236     return MatchPatternImpl(source, pattern, source_index + 1, pattern_index) ||
237            MatchPatternImpl(source, pattern, source_index, pattern_index + 1);
238   }
239   return false;
240 }
241 
242 // Defines the type of whitespace characters typically found in strings.
243 constexpr char kWhiteSpaces[] = " \t\n\r\f\v";
244 constexpr wchar_t kWhiteSpaces16[] = L" \t\n\r\f\v";
245 
246 // Define specializations for white spaces based on the type of the string.
247 template <class StringType>
248 StringType GetWhiteSpacesForType();
249 template <>
GetWhiteSpacesForType()250 std::wstring GetWhiteSpacesForType() {
251   return kWhiteSpaces16;
252 }
253 template <>
GetWhiteSpacesForType()254 std::string GetWhiteSpacesForType() {
255   return kWhiteSpaces;
256 }
257 
258 // Trim whitespaces from left & right
259 template <class StringType>
TrimT(StringType * str)260 void TrimT(StringType* str) {
261   str->erase(str->find_last_not_of(GetWhiteSpacesForType<StringType>()) + 1);
262   str->erase(0, str->find_first_not_of(GetWhiteSpacesForType<StringType>()));
263 }
264 
265 // Tokenizes a string based on a single character delimiter.
266 template <class StringType>
TokenizeStringT(const StringType & str,typename StringType::value_type delimiter,bool trim_spaces)267 std::vector<StringType> TokenizeStringT(
268     const StringType& str,
269     typename StringType::value_type delimiter,
270     bool trim_spaces) {
271   std::vector<StringType> tokens;
272   std::basic_istringstream<typename StringType::value_type> buffer(str);
273   for (StringType token; std::getline(buffer, token, delimiter);) {
274     if (trim_spaces)
275       TrimT<StringType>(&token);
276     tokens.push_back(token);
277   }
278   return tokens;
279 }
280 
281 #if BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
282 // Returns Chrome's update channel name based on the contents of the given "ap"
283 // value from Chrome's ClientState key.
ChannelFromAdditionalParameters(const InstallConstants & mode,const std::wstring & ap_value)284 std::wstring ChannelFromAdditionalParameters(const InstallConstants& mode,
285                                              const std::wstring& ap_value) {
286   static constexpr wchar_t kChromeChannelBetaPattern[] = L"1?1-*";
287   static constexpr wchar_t kChromeChannelBetaX64Pattern[] = L"*x64-beta*";
288   static constexpr wchar_t kChromeChannelDevPattern[] = L"2?0-d*";
289   static constexpr wchar_t kChromeChannelDevX64Pattern[] = L"*x64-dev*";
290 
291   std::wstring value;
292   value.reserve(ap_value.size());
293   std::transform(ap_value.begin(), ap_value.end(), std::back_inserter(value),
294                  ::tolower);
295 
296   // Empty channel names or those containing "stable" should be reported as
297   // an empty string.
298   if (value.empty() ||
299       (value.find(kChromeChannelStableExplicit) != std::wstring::npos)) {
300     return std::wstring();
301   }
302   if (MatchPattern(value, kChromeChannelDevPattern) ||
303       MatchPattern(value, kChromeChannelDevX64Pattern)) {
304     return kChromeChannelDev;
305   }
306   if (MatchPattern(value, kChromeChannelBetaPattern) ||
307       MatchPattern(value, kChromeChannelBetaX64Pattern)) {
308     return kChromeChannelBeta;
309   }
310   // Else report values with garbage as stable since they will match the stable
311   // rules in the update configs.
312   return std::wstring();
313 }
314 
GetChromeChannelNameFromString(const wchar_t * channel_test,std::wstring & channel)315 bool GetChromeChannelNameFromString(const wchar_t* channel_test,
316                                     std::wstring& channel) {
317   if (!channel_test)
318     return false;
319   if (!*channel_test || !lstrcmpiW(channel_test, kChromeChannelStableExplicit))
320     channel = std::wstring();
321   else if (!lstrcmpiW(channel_test, kChromeChannelBeta))
322     channel = kChromeChannelBeta;
323   else if (!lstrcmpiW(channel_test, kChromeChannelDev))
324     channel = kChromeChannelDev;
325   else
326     return false;
327   return true;
328 }
329 
330 #endif  // BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
331 
332 // Converts a process type specified as a string to the ProcessType enum.
GetProcessType(const std::wstring & process_type)333 ProcessType GetProcessType(const std::wstring& process_type) {
334   if (process_type.empty())
335     return ProcessType::BROWSER_PROCESS;
336   if (process_type == kCloudPrintServiceProcess)
337     return ProcessType::CLOUD_PRINT_SERVICE_PROCESS;
338 #if BUILDFLAG(ENABLE_NACL)
339   if (process_type == kNaClBrokerProcess)
340     return ProcessType::NACL_BROKER_PROCESS;
341   if (process_type == kNaClLoaderProcess)
342     return ProcessType::NACL_LOADER_PROCESS;
343 #endif
344   if (process_type == kCrashpadHandler)
345     return ProcessType::CRASHPAD_HANDLER_PROCESS;
346   return ProcessType::OTHER_PROCESS;
347 }
348 
349 // Returns whether |process_type| needs the profile directory.
ProcessNeedsProfileDir(ProcessType process_type)350 bool ProcessNeedsProfileDir(ProcessType process_type) {
351   // On Windows we don't want subprocesses other than the browser process and
352   // service processes to be able to use the profile directory because if it
353   // lies on a network share the sandbox will prevent us from accessing it.
354   switch (process_type) {
355     case ProcessType::BROWSER_PROCESS:
356     case ProcessType::CLOUD_PRINT_SERVICE_PROCESS:
357 #if BUILDFLAG(ENABLE_NACL)
358     case ProcessType::NACL_BROKER_PROCESS:
359     case ProcessType::NACL_LOADER_PROCESS:
360 #endif
361       return true;
362     case ProcessType::OTHER_PROCESS:
363       return false;
364     case ProcessType::CRASHPAD_HANDLER_PROCESS:
365       return false;
366     case ProcessType::UNINITIALIZED:
367       assert(false);
368       return false;
369   }
370   assert(false);
371   return false;
372 }
373 
374 }  // namespace
375 
IsSystemInstall()376 bool IsSystemInstall() {
377   return InstallDetails::Get().system_level();
378 }
379 
GetChromeInstallSubDirectory()380 std::wstring GetChromeInstallSubDirectory() {
381   std::wstring result;
382   AppendChromeInstallSubDirectory(InstallDetails::Get().mode(),
383                                   true /* include_suffix */, &result);
384   return result;
385 }
386 
GetRegistryPath()387 std::wstring GetRegistryPath() {
388   std::wstring result(L"Software\\");
389   AppendChromeInstallSubDirectory(InstallDetails::Get().mode(),
390                                   true /* include_suffix */, &result);
391   return result;
392 }
393 
GetClientsKeyPath()394 std::wstring GetClientsKeyPath() {
395   return GetClientsKeyPath(GetAppGuid());
396 }
397 
GetClientStateKeyPath()398 std::wstring GetClientStateKeyPath() {
399   return GetClientStateKeyPath(GetAppGuid());
400 }
401 
GetClientStateMediumKeyPath()402 std::wstring GetClientStateMediumKeyPath() {
403   return GetClientStateMediumKeyPath(GetAppGuid());
404 }
405 
GetUninstallRegistryPath()406 std::wstring GetUninstallRegistryPath() {
407   std::wstring result(
408       L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\");
409   if (*kCompanyPathName)
410     result.append(kCompanyPathName).append(1, L' ');
411   result.append(kProductPathName, kProductPathNameLength);
412   return result.append(InstallDetails::Get().mode().install_suffix);
413 }
414 
GetAppGuid()415 const wchar_t* GetAppGuid() {
416   return InstallDetails::Get().app_guid();
417 }
418 
GetToastActivatorClsid()419 const CLSID& GetToastActivatorClsid() {
420   return InstallDetails::Get().toast_activator_clsid();
421 }
422 
GetElevatorClsid()423 const CLSID& GetElevatorClsid() {
424   return InstallDetails::Get().elevator_clsid();
425 }
426 
GetElevatorIid()427 const CLSID& GetElevatorIid() {
428   return InstallDetails::Get().elevator_iid();
429 }
430 
GetElevationServiceName()431 std::wstring GetElevationServiceName() {
432   std::wstring name = GetElevationServiceDisplayName();
433   name.erase(std::remove_if(name.begin(), name.end(), isspace), name.end());
434   return name;
435 }
436 
GetElevationServiceDisplayName()437 std::wstring GetElevationServiceDisplayName() {
438   static constexpr wchar_t kElevationServiceDisplayName[] =
439       L" Elevation Service";
440   return GetBaseAppName() + kElevationServiceDisplayName;
441 }
442 
GetBaseAppName()443 std::wstring GetBaseAppName() {
444   return InstallDetails::Get().mode().base_app_name;
445 }
446 
GetBaseAppId()447 const wchar_t* GetBaseAppId() {
448   return InstallDetails::Get().base_app_id();
449 }
450 
GetProgIdPrefix()451 const wchar_t* GetProgIdPrefix() {
452   return InstallDetails::Get().mode().prog_id_prefix;
453 }
454 
GetProgIdDescription()455 const wchar_t* GetProgIdDescription() {
456   return InstallDetails::Get().mode().prog_id_description;
457 }
458 
GetActiveSetupPath()459 std::wstring GetActiveSetupPath() {
460   return std::wstring(
461              L"Software\\Microsoft\\Active Setup\\Installed Components\\")
462       .append(InstallDetails::Get().mode().active_setup_guid);
463 }
464 
GetLegacyCommandExecuteImplClsid()465 std::wstring GetLegacyCommandExecuteImplClsid() {
466   return InstallDetails::Get().mode().legacy_command_execute_clsid;
467 }
468 
SupportsSetAsDefaultBrowser()469 bool SupportsSetAsDefaultBrowser() {
470   return InstallDetails::Get().mode().supports_set_as_default_browser;
471 }
472 
SupportsRetentionExperiments()473 bool SupportsRetentionExperiments() {
474   return InstallDetails::Get().mode().supports_retention_experiments;
475 }
476 
GetIconResourceIndex()477 int GetIconResourceIndex() {
478   return InstallDetails::Get().mode().app_icon_resource_index;
479 }
480 
GetSandboxSidPrefix()481 const wchar_t* GetSandboxSidPrefix() {
482   return InstallDetails::Get().mode().sandbox_sid_prefix;
483 }
484 
GetSafeBrowsingName()485 std::string GetSafeBrowsingName() {
486   return kSafeBrowsingName;
487 }
488 
GetCollectStatsConsent()489 bool GetCollectStatsConsent() {
490   bool enabled = true;
491 
492   if (ReportingIsEnforcedByPolicy(&enabled))
493     return enabled;
494 
495   const bool system_install = IsSystemInstall();
496 
497   DWORD out_value = 0;
498 
499   // If system_install, first try ClientStateMedium in HKLM.
500   if (system_install &&
501       nt::QueryRegValueDWORD(
502           nt::HKLM, nt::WOW6432,
503           InstallDetails::Get().GetClientStateMediumKeyPath().c_str(),
504           kRegValueUsageStats, &out_value)) {
505     return (out_value == 1);
506   }
507 
508   // Second, try ClientState.
509   return (nt::QueryRegValueDWORD(
510               system_install ? nt::HKLM : nt::HKCU, nt::WOW6432,
511               InstallDetails::Get().GetClientStateKeyPath().c_str(),
512               kRegValueUsageStats, &out_value) &&
513           out_value == 1);
514 }
515 
GetCollectStatsInSample()516 bool GetCollectStatsInSample() {
517   std::wstring registry_path = GetRegistryPath();
518 
519   DWORD out_value = 0;
520   if (!nt::QueryRegValueDWORD(nt::HKCU, nt::WOW6432, registry_path.c_str(),
521                               kRegValueChromeStatsSample, &out_value)) {
522     // If reading the value failed, treat it as though sampling isn't in effect,
523     // implicitly meaning this install is in the sample.
524     return true;
525   }
526   return out_value == 1;
527 }
528 
SetCollectStatsInSample(bool in_sample)529 bool SetCollectStatsInSample(bool in_sample) {
530   std::wstring registry_path = GetRegistryPath();
531 
532   HANDLE key_handle = INVALID_HANDLE_VALUE;
533   if (!nt::CreateRegKey(nt::HKCU, registry_path.c_str(),
534                         KEY_SET_VALUE | KEY_WOW64_32KEY, &key_handle)) {
535     return false;
536   }
537 
538   bool success = nt::SetRegValueDWORD(key_handle, kRegValueChromeStatsSample,
539                                       in_sample ? 1 : 0);
540   nt::CloseRegKey(key_handle);
541   return success;
542 }
543 
544 // Appends "[kCompanyPathName\]kProductPathName[install_suffix]" to |path|,
545 // returning a reference to |path|.
AppendChromeInstallSubDirectory(const InstallConstants & mode,bool include_suffix,std::wstring * path)546 std::wstring& AppendChromeInstallSubDirectory(const InstallConstants& mode,
547                                               bool include_suffix,
548                                               std::wstring* path) {
549   if (*kCompanyPathName) {
550     path->append(kCompanyPathName);
551     path->push_back(L'\\');
552   }
553   path->append(kProductPathName, kProductPathNameLength);
554   if (!include_suffix)
555     return *path;
556   return path->append(mode.install_suffix);
557 }
558 
ReportingIsEnforcedByPolicy(bool * crash_reporting_enabled)559 bool ReportingIsEnforcedByPolicy(bool* crash_reporting_enabled) {
560   std::wstring policies_path = L"SOFTWARE\\Policies\\";
561   AppendChromeInstallSubDirectory(InstallDetails::Get().mode(),
562                                   false /* !include_suffix */, &policies_path);
563   DWORD value = 0;
564 
565   // First, try HKLM.
566   if (nt::QueryRegValueDWORD(nt::HKLM, nt::NONE, policies_path.c_str(),
567                              kMetricsReportingEnabled, &value)) {
568     *crash_reporting_enabled = (value != 0);
569     return true;
570   }
571 
572   // Second, try HKCU.
573   if (nt::QueryRegValueDWORD(nt::HKCU, nt::NONE, policies_path.c_str(),
574                              kMetricsReportingEnabled, &value)) {
575     *crash_reporting_enabled = (value != 0);
576     return true;
577   }
578 
579   return false;
580 }
581 
InitializeProcessType()582 void InitializeProcessType() {
583   assert(g_process_type == ProcessType::UNINITIALIZED);
584   std::wstring process_type =
585       GetSwitchValueFromCommandLine(::GetCommandLine(), kProcessType);
586   g_process_type = GetProcessType(process_type);
587 }
588 
IsProcessTypeInitialized()589 bool IsProcessTypeInitialized() {
590   return g_process_type != ProcessType::UNINITIALIZED;
591 }
592 
IsNonBrowserProcess()593 bool IsNonBrowserProcess() {
594   assert(g_process_type != ProcessType::UNINITIALIZED);
595   return g_process_type != ProcessType::BROWSER_PROCESS;
596 }
597 
IsCrashpadHandlerProcess()598 bool IsCrashpadHandlerProcess() {
599   assert(g_process_type != ProcessType::UNINITIALIZED);
600   return g_process_type == ProcessType::CRASHPAD_HANDLER_PROCESS;
601 }
602 
ProcessNeedsProfileDir(const std::string & process_type)603 bool ProcessNeedsProfileDir(const std::string& process_type) {
604   return ProcessNeedsProfileDir(GetProcessType(UTF8ToUTF16(process_type)));
605 }
606 
GetCrashDumpLocation()607 std::wstring GetCrashDumpLocation() {
608   // In order to be able to start crash handling very early and in chrome_elf,
609   // we cannot rely on chrome's PathService entries (for DIR_CRASH_DUMPS) being
610   // available on Windows. See https://crbug.com/564398.
611   std::wstring user_data_dir;
612   bool ret = GetUserDataDirectory(&user_data_dir, nullptr);
613   assert(ret);
614   IgnoreUnused(ret);
615   return user_data_dir.append(L"\\Crashpad");
616 }
617 
GetEnvironmentString(const std::string & variable_name)618 std::string GetEnvironmentString(const std::string& variable_name) {
619   return UTF16ToUTF8(
620       GetEnvironmentString16(UTF8ToUTF16(variable_name).c_str()));
621 }
622 
GetEnvironmentString16(const wchar_t * variable_name)623 std::wstring GetEnvironmentString16(const wchar_t* variable_name) {
624   DWORD value_length = ::GetEnvironmentVariableW(variable_name, nullptr, 0);
625   if (!value_length)
626     return std::wstring();
627   std::wstring value(value_length, L'\0');
628   value_length =
629       ::GetEnvironmentVariableW(variable_name, &value[0], value_length);
630   if (!value_length || value_length >= value.size())
631     return std::wstring();
632   value.resize(value_length);
633   return value;
634 }
635 
SetEnvironmentString(const std::string & variable_name,const std::string & new_value)636 bool SetEnvironmentString(const std::string& variable_name,
637                           const std::string& new_value) {
638   return SetEnvironmentString16(UTF8ToUTF16(variable_name),
639                                 UTF8ToUTF16(new_value));
640 }
641 
SetEnvironmentString16(const std::wstring & variable_name,const std::wstring & new_value)642 bool SetEnvironmentString16(const std::wstring& variable_name,
643                             const std::wstring& new_value) {
644   return !!SetEnvironmentVariable(variable_name.c_str(), new_value.c_str());
645 }
646 
HasEnvironmentVariable(const std::string & variable_name)647 bool HasEnvironmentVariable(const std::string& variable_name) {
648   return HasEnvironmentVariable16(UTF8ToUTF16(variable_name));
649 }
650 
HasEnvironmentVariable16(const std::wstring & variable_name)651 bool HasEnvironmentVariable16(const std::wstring& variable_name) {
652   return !!::GetEnvironmentVariable(variable_name.c_str(), nullptr, 0);
653 }
654 
GetExecutableVersionDetails(const std::wstring & exe_path,std::wstring * product_name,std::wstring * version,std::wstring * special_build,std::wstring * channel_name)655 void GetExecutableVersionDetails(const std::wstring& exe_path,
656                                  std::wstring* product_name,
657                                  std::wstring* version,
658                                  std::wstring* special_build,
659                                  std::wstring* channel_name) {
660   assert(product_name);
661   assert(version);
662   assert(special_build);
663   assert(channel_name);
664 
665   // Default values in case we don't find a version resource.
666   *product_name = L"Chrome";
667   *version = L"0.0.0.0-devel";
668   special_build->clear();
669 
670   DWORD dummy = 0;
671   DWORD length = ::GetFileVersionInfoSize(exe_path.c_str(), &dummy);
672   if (length) {
673     std::unique_ptr<char[]> data(new char[length]);
674     if (::GetFileVersionInfo(exe_path.c_str(), dummy, length, data.get())) {
675       GetValueFromVersionResource(data.get(), L"ProductVersion", version);
676 
677       std::wstring official_build;
678       GetValueFromVersionResource(data.get(), L"Official Build",
679                                   &official_build);
680       if (official_build != L"1")
681         version->append(L"-devel");
682       GetValueFromVersionResource(data.get(), L"ProductShortName",
683                                   product_name);
684       GetValueFromVersionResource(data.get(), L"SpecialBuild", special_build);
685     }
686   }
687   *channel_name = GetChromeChannelName();
688 }
689 
GetChromeChannel()690 version_info::Channel GetChromeChannel() {
691 #if BUILDFLAG(GOOGLE_CHROME_BRANDING)
692   std::wstring channel_name(GetChromeChannelName());
693   if (channel_name.empty()) {
694     return version_info::Channel::STABLE;
695   }
696   if (channel_name == L"beta") {
697     return version_info::Channel::BETA;
698   }
699   if (channel_name == L"dev") {
700     return version_info::Channel::DEV;
701   }
702   if (channel_name == L"canary") {
703     return version_info::Channel::CANARY;
704   }
705 #endif
706 
707   return version_info::Channel::UNKNOWN;
708 }
709 
GetChromeChannelName()710 std::wstring GetChromeChannelName() {
711   return InstallDetails::Get().channel();
712 }
713 
MatchPattern(const std::wstring & source,const std::wstring & pattern)714 bool MatchPattern(const std::wstring& source, const std::wstring& pattern) {
715   assert(pattern.find(L"**") == std::wstring::npos);
716   return MatchPatternImpl(source, pattern, 0, 0);
717 }
718 
UTF16ToUTF8(const std::wstring & source)719 std::string UTF16ToUTF8(const std::wstring& source) {
720   if (source.empty() ||
721       static_cast<int>(source.size()) > std::numeric_limits<int>::max()) {
722     return std::string();
723   }
724   int size = ::WideCharToMultiByte(CP_UTF8, 0, &source[0],
725                                    static_cast<int>(source.size()), nullptr, 0,
726                                    nullptr, nullptr);
727   std::string result(size, '\0');
728   if (::WideCharToMultiByte(CP_UTF8, 0, &source[0],
729                             static_cast<int>(source.size()), &result[0], size,
730                             nullptr, nullptr) != size) {
731     assert(false);
732     return std::string();
733   }
734   return result;
735 }
736 
UTF8ToUTF16(const std::string & source)737 std::wstring UTF8ToUTF16(const std::string& source) {
738   if (source.empty() ||
739       static_cast<int>(source.size()) > std::numeric_limits<int>::max()) {
740     return std::wstring();
741   }
742   int size = ::MultiByteToWideChar(CP_UTF8, 0, &source[0],
743                                    static_cast<int>(source.size()), nullptr, 0);
744   std::wstring result(size, L'\0');
745   if (::MultiByteToWideChar(CP_UTF8, 0, &source[0],
746                             static_cast<int>(source.size()), &result[0],
747                             size) != size) {
748     assert(false);
749     return std::wstring();
750   }
751   return result;
752 }
753 
TokenizeString(const std::string & str,char delimiter,bool trim_spaces)754 std::vector<std::string> TokenizeString(const std::string& str,
755                                         char delimiter,
756                                         bool trim_spaces) {
757   return TokenizeStringT<std::string>(str, delimiter, trim_spaces);
758 }
759 
TokenizeString16(const std::wstring & str,wchar_t delimiter,bool trim_spaces)760 std::vector<std::wstring> TokenizeString16(const std::wstring& str,
761                                            wchar_t delimiter,
762                                            bool trim_spaces) {
763   return TokenizeStringT<std::wstring>(str, delimiter, trim_spaces);
764 }
765 
TokenizeCommandLineToArray(const std::wstring & command_line)766 std::vector<std::wstring> TokenizeCommandLineToArray(
767     const std::wstring& command_line) {
768   // This is baroquely complex to do properly, see e.g.
769   // https://blogs.msdn.microsoft.com/oldnewthing/20100917-00/?p=12833
770   // http://www.windowsinspired.com/how-a-windows-programs-splits-its-command-line-into-individual-arguments/
771   // and many others. We cannot use CommandLineToArgvW() in chrome_elf, because
772   // it's in shell32.dll. Previously, __wgetmainargs() in the CRT was available,
773   // and it's still documented for VS 2015 at
774   // https://msdn.microsoft.com/en-us/library/ff770599.aspx but unfortunately,
775   // isn't actually available.
776   //
777   // This parsing matches CommandLineToArgvW()s for arguments, rather than the
778   // CRTs. These are different only in the most obscure of cases and will not
779   // matter in any practical situation. See the windowsinspired.com post above
780   // for details.
781   //
782   // Indicates whether or not space and tab are interpreted as token separators.
783   enum class SpecialChars {
784     // Space or tab, if encountered, delimit tokens.
785     kInterpret,
786 
787     // Space or tab, if encountered, are part of the current token.
788     kIgnore,
789   } state;
790 
791   static constexpr wchar_t kSpaceTab[] = L" \t";
792 
793   std::vector<std::wstring> result;
794   const wchar_t* p = command_line.c_str();
795 
796   // The first argument (the program) is delimited by whitespace or quotes based
797   // on its first character.
798   size_t argv0_length = 0;
799   if (p[0] == L'"') {
800     const wchar_t* closing = wcschr(++p, L'"');
801     if (!closing)
802       argv0_length = command_line.size() - 1;  // Skip the opening quote.
803     else
804       argv0_length = closing - (command_line.c_str() + 1);
805   } else {
806     argv0_length = wcscspn(p, kSpaceTab);
807   }
808   result.emplace_back(p, argv0_length);
809   if (p[argv0_length] == 0)
810     return result;
811   p += argv0_length + 1;
812 
813   std::wstring token;
814   // This loops the entire string, with a subloop for each argument.
815   for (;;) {
816     // Advance past leading whitespace (only space and tab are handled).
817     p += wcsspn(p, kSpaceTab);
818 
819     // End of arguments.
820     if (p[0] == 0)
821       break;
822 
823     state = SpecialChars::kInterpret;
824 
825     // Scan an argument.
826     for (;;) {
827       // Count and advance past collections of backslashes, which have special
828       // meaning when followed by a double quote.
829       int num_backslashes = wcsspn(p, L"\\");
830       p += num_backslashes;
831 
832       if (p[0] == L'"') {
833         // Emit a backslash for each pair of backslashes found. A non-paired
834         // "extra" backslash is handled below.
835         token.append(num_backslashes / 2, L'\\');
836 
837         if (num_backslashes % 2 == 1) {
838           // An odd number of backslashes followed by a quote is treated as
839           // pairs of protected backslashes, followed by the protected quote.
840           token += L'"';
841         } else if (p[1] == L'"' && state == SpecialChars::kIgnore) {
842           // Special case for consecutive double quotes within a quoted string:
843           // emit one for the pair, and switch back to interpreting special
844           // characters.
845           ++p;
846           token += L'"';
847           state = SpecialChars::kInterpret;
848         } else {
849           state = state == SpecialChars::kInterpret ? SpecialChars::kIgnore
850                                                     : SpecialChars::kInterpret;
851         }
852       } else {
853         // Emit backslashes that do not precede a quote verbatim.
854         token.append(num_backslashes, L'\\');
855         if (p[0] == 0 ||
856             (state == SpecialChars::kInterpret && wcschr(kSpaceTab, p[0]))) {
857           result.push_back(token);
858           token.clear();
859           break;
860         }
861 
862         token += *p;
863       }
864 
865       ++p;
866     }
867   }
868 
869   return result;
870 }
871 
GetSwitchValueFromCommandLine(const std::wstring & command_line,const std::wstring & switch_name)872 std::wstring GetSwitchValueFromCommandLine(const std::wstring& command_line,
873                                            const std::wstring& switch_name) {
874   static constexpr wchar_t kSwitchTerminator[] = L"--";
875   assert(!command_line.empty());
876   assert(!switch_name.empty());
877 
878   std::vector<std::wstring> as_array = TokenizeCommandLineToArray(command_line);
879   std::wstring switch_with_equal = L"--" + switch_name + L"=";
880   auto end = std::find(as_array.cbegin(), as_array.cend(), kSwitchTerminator);
881   for (auto scan = as_array.cbegin(); scan != end; ++scan) {
882     const std::wstring& arg = *scan;
883     if (arg.compare(0, switch_with_equal.size(), switch_with_equal) == 0)
884       return arg.substr(switch_with_equal.size());
885   }
886 
887   return std::wstring();
888 }
889 
RecursiveDirectoryCreate(const std::wstring & full_path)890 bool RecursiveDirectoryCreate(const std::wstring& full_path) {
891   // If the path exists, we've succeeded if it's a directory, failed otherwise.
892   const wchar_t* full_path_str = full_path.c_str();
893   DWORD file_attributes = ::GetFileAttributes(full_path_str);
894   if (file_attributes != INVALID_FILE_ATTRIBUTES) {
895     if ((file_attributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
896       Trace(L"%hs( %ls directory exists )\n", __func__, full_path_str);
897       return true;
898     }
899     Trace(L"%hs( %ls directory conflicts with an existing file. )\n", __func__,
900           full_path_str);
901     return false;
902   }
903 
904   // Invariant:  Path does not exist as file or directory.
905 
906   // Attempt to create the parent recursively.  This will immediately return
907   // true if it already exists, otherwise will create all required parent
908   // directories starting with the highest-level missing parent.
909   std::wstring parent_path;
910   std::size_t pos = full_path.find_last_of(L"/\\");
911   if (pos != std::wstring::npos) {
912     parent_path = full_path.substr(0, pos);
913     if (!RecursiveDirectoryCreate(parent_path)) {
914       Trace(L"Failed to create one of the parent directories");
915       return false;
916     }
917   }
918   if (!::CreateDirectory(full_path_str, nullptr)) {
919     DWORD error_code = ::GetLastError();
920     if (error_code == ERROR_ALREADY_EXISTS && DirectoryExists(full_path_str)) {
921       // This error code ERROR_ALREADY_EXISTS doesn't indicate whether we
922       // were racing with someone creating the same directory, or a file
923       // with the same path.  If the directory exists, we lost the
924       // race to create the same directory.
925       return true;
926     } else {
927       Trace(L"Failed to create directory %ls, last error is %d\n",
928             full_path_str, error_code);
929       return false;
930     }
931   }
932   return true;
933 }
934 
935 // This function takes these inputs rather than accessing the module's
936 // InstallDetails instance since it is used to bootstrap InstallDetails.
DetermineChannel(const InstallConstants & mode,bool system_level,const wchar_t * channel_override,std::wstring * update_ap,std::wstring * update_cohort_name)937 DetermineChannelResult DetermineChannel(const InstallConstants& mode,
938                                         bool system_level,
939                                         const wchar_t* channel_override,
940                                         std::wstring* update_ap,
941                                         std::wstring* update_cohort_name) {
942 #if !BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
943   return {std::wstring(), ChannelOrigin::kInstallMode};
944 #else
945   // Read the "ap" value and cache it if requested.
946   std::wstring client_state(GetClientStateKeyPath(mode.app_guid));
947   std::wstring ap_value;
948   // An empty |ap_value| is used in case of error.
949   nt::QueryRegValueSZ(system_level ? nt::HKLM : nt::HKCU, nt::WOW6432,
950                       client_state.c_str(), kRegValueAp, &ap_value);
951   if (update_ap)
952     *update_ap = ap_value;
953 
954   // Cache the cohort name if requested.
955   if (update_cohort_name) {
956     nt::QueryRegValueSZ(system_level ? nt::HKLM : nt::HKCU, nt::WOW6432,
957                         client_state.append(L"\\cohort").c_str(), kRegValueName,
958                         update_cohort_name);
959   }
960 
961   switch (mode.channel_strategy) {
962     case ChannelStrategy::UNSUPPORTED:
963       assert(false);
964       break;
965     case ChannelStrategy::ADDITIONAL_PARAMETERS: {
966       std::wstring channel_override_value;
967       if (channel_override && GetChromeChannelNameFromString(
968                                   channel_override, channel_override_value)) {
969         return {std::move(channel_override_value), ChannelOrigin::kPolicy};
970       }
971       return {ChannelFromAdditionalParameters(mode, ap_value),
972               ChannelOrigin::kAdditionalParameters};
973     }
974     case ChannelStrategy::FIXED:
975       return {mode.default_channel_name, ChannelOrigin::kInstallMode};
976   }
977   return {std::wstring(), ChannelOrigin::kInstallMode};
978 #endif
979 }
980 
981 }  // namespace install_static
982