1 // Copyright (c) 2012 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 // See the corresponding header file for description of the functions in this
6 // file.
7 
8 #include "chrome/installer/util/install_util.h"
9 
10 #include <shellapi.h>
11 #include <shlobj.h>
12 
13 #include <algorithm>
14 #include <iterator>
15 
16 #include "base/check.h"
17 #include "base/check_op.h"
18 #include "base/command_line.h"
19 #include "base/files/file_util.h"
20 #include "base/logging.h"
21 #include "base/metrics/histogram_macros.h"
22 #include "base/notreached.h"
23 #include "base/path_service.h"
24 #include "base/process/launch.h"
25 #include "base/stl_util.h"
26 #include "base/strings/string_util.h"
27 #include "base/strings/utf_string_conversions.h"
28 #include "base/system/sys_info.h"
29 #include "base/values.h"
30 #include "base/win/shlwapi.h"
31 #include "base/win/shortcut.h"
32 #include "base/win/win_util.h"
33 #include "base/win/windows_version.h"
34 #include "chrome/common/chrome_constants.h"
35 #include "chrome/common/chrome_paths.h"
36 #include "chrome/install_static/install_details.h"
37 #include "chrome/install_static/install_modes.h"
38 #include "chrome/install_static/install_util.h"
39 #include "chrome/installer/util/google_update_constants.h"
40 #include "chrome/installer/util/installation_state.h"
41 #include "chrome/installer/util/installer_util_strings.h"
42 #include "chrome/installer/util/l10n_string_util.h"
43 #include "chrome/installer/util/shell_util.h"
44 #include "chrome/installer/util/util_constants.h"
45 #include "chrome/installer/util/work_item_list.h"
46 
47 using base::win::RegKey;
48 using installer::ProductState;
49 
50 namespace {
51 
52 // DowngradeVersion holds the version from which Chrome was downgraded. In case
53 // of multiple downgrades (e.g., 75->74->73), it retains the highest version
54 // installed prior to any downgrades. DowngradeVersion is deleted on upgrade
55 // once Chrome reaches the version from which it was downgraded.
56 const wchar_t kRegDowngradeVersion[] = L"DowngradeVersion";
57 
58 // These values are persisted to logs. Entries should not be renumbered and
59 // numeric values should never be reused.
60 enum class StartMenuShortcutStatus {
61   kSuccess = 0,
62   kGetShortcutPathFailed = 1,
63   kShortcutMissing = 2,
64   kToastActivatorClsidIncorrect = 3,
65   kReadShortcutPropertyFailed = 4,
66   kMaxValue = kReadShortcutPropertyFailed,
67 };
68 
LogStartMenuShortcutStatus(StartMenuShortcutStatus status)69 void LogStartMenuShortcutStatus(StartMenuShortcutStatus status) {
70   UMA_HISTOGRAM_ENUMERATION("Notifications.Windows.StartMenuShortcutStatus",
71                             status);
72 }
73 
74 // Creates a zero-sized non-decorated foreground window that doesn't appear
75 // in the taskbar. This is used as a parent window for calls to ShellExecuteEx
76 // in order for the UAC dialog to appear in the foreground and for focus
77 // to be returned to this process once the UAC task is dismissed. Returns
78 // nullptr on failure, a handle to the UAC window on success.
CreateUACForegroundWindow()79 HWND CreateUACForegroundWindow() {
80   HWND foreground_window = ::CreateWindowEx(
81       WS_EX_TOOLWINDOW, L"STATIC", nullptr, WS_POPUP | WS_VISIBLE, 0, 0, 0, 0,
82       nullptr, nullptr, ::GetModuleHandle(nullptr), nullptr);
83   if (foreground_window) {
84     HMONITOR monitor =
85         ::MonitorFromWindow(foreground_window, MONITOR_DEFAULTTONEAREST);
86     if (monitor) {
87       MONITORINFO mi = {0};
88       mi.cbSize = sizeof(mi);
89       ::GetMonitorInfo(monitor, &mi);
90       RECT screen_rect = mi.rcWork;
91       int x_offset = (screen_rect.right - screen_rect.left) / 2;
92       int y_offset = (screen_rect.bottom - screen_rect.top) / 2;
93       ::MoveWindow(foreground_window, screen_rect.left + x_offset,
94                    screen_rect.top + y_offset, 0, 0, FALSE);
95     } else {
96       NOTREACHED() << "Unable to get default monitor";
97     }
98     ::SetForegroundWindow(foreground_window);
99   }
100   return foreground_window;
101 }
102 
103 // Returns Registry key path of Chrome policies. This is used by the policies
104 // that are shared between Chrome and installer.
GetChromePoliciesRegistryPath()105 std::wstring GetChromePoliciesRegistryPath() {
106   std::wstring key_path = L"SOFTWARE\\Policies\\";
107   install_static::AppendChromeInstallSubDirectory(
108       install_static::InstallDetails::Get().mode(), /*include_suffix=*/false,
109       &key_path);
110   return key_path;
111 }
112 
GetCloudManagementPoliciesRegistryPath()113 std::wstring GetCloudManagementPoliciesRegistryPath() {
114   std::wstring key_path = L"SOFTWARE\\Policies\\";
115   key_path.append(install_static::kCompanyPathName);
116   key_path.append(L"\\CloudManagement");
117   return key_path;
118 }
119 
120 // Reruns the registry key path and value name where the cloud management
121 // enrollment option is stored.
GetCloudManagementBlockOnFailureRegistryPath(base::string16 * key_path,base::string16 * value_name)122 void GetCloudManagementBlockOnFailureRegistryPath(base::string16* key_path,
123                                                   base::string16* value_name) {
124   *key_path = GetChromePoliciesRegistryPath();
125   *value_name = L"CloudManagementEnrollmentMandatory";
126 }
127 
128 }  // namespace
129 
TriggerActiveSetupCommand()130 void InstallUtil::TriggerActiveSetupCommand() {
131   base::string16 active_setup_reg(install_static::GetActiveSetupPath());
132   base::win::RegKey active_setup_key(HKEY_LOCAL_MACHINE,
133                                      active_setup_reg.c_str(), KEY_QUERY_VALUE);
134   base::string16 cmd_str;
135   LONG read_status = active_setup_key.ReadValue(L"StubPath", &cmd_str);
136   if (read_status != ERROR_SUCCESS) {
137     LOG(ERROR) << active_setup_reg << ", " << read_status;
138     // This should never fail if Chrome is registered at system-level, but if it
139     // does there is not much else to be done.
140     return;
141   }
142 
143   base::CommandLine cmd(base::CommandLine::FromString(cmd_str));
144   // Force creation of shortcuts as the First Run beacon might land between now
145   // and the time setup.exe checks for it.
146   cmd.AppendSwitch(installer::switches::kForceConfigureUserSettings);
147 
148   base::Process process =
149       base::LaunchProcess(cmd.GetCommandLineString(), base::LaunchOptions());
150   if (!process.IsValid())
151     PLOG(ERROR) << cmd.GetCommandLineString();
152 }
153 
ExecuteExeAsAdmin(const base::CommandLine & cmd,DWORD * exit_code)154 bool InstallUtil::ExecuteExeAsAdmin(const base::CommandLine& cmd,
155                                     DWORD* exit_code) {
156   base::FilePath::StringType program(cmd.GetProgram().value());
157   DCHECK(!program.empty());
158   DCHECK_NE(program[0], L'\"');
159 
160   base::CommandLine::StringType params(cmd.GetCommandLineString());
161   if (params[0] == '"') {
162     DCHECK_EQ('"', params[program.length() + 1]);
163     DCHECK_EQ(program, params.substr(1, program.length()));
164     params = params.substr(program.length() + 2);
165   } else {
166     DCHECK_EQ(program, params.substr(0, program.length()));
167     params = params.substr(program.length());
168   }
169 
170   base::TrimWhitespace(params, base::TRIM_ALL, &params);
171 
172   HWND uac_foreground_window = CreateUACForegroundWindow();
173 
174   SHELLEXECUTEINFO info = {0};
175   info.cbSize = sizeof(SHELLEXECUTEINFO);
176   info.fMask = SEE_MASK_NOCLOSEPROCESS;
177   info.hwnd = uac_foreground_window;
178   info.lpVerb = L"runas";
179   info.lpFile = program.c_str();
180   info.lpParameters = params.c_str();
181   info.nShow = SW_SHOW;
182 
183   bool success = false;
184   if (::ShellExecuteEx(&info) == TRUE) {
185     ::WaitForSingleObject(info.hProcess, INFINITE);
186     DWORD ret_val = 0;
187     if (::GetExitCodeProcess(info.hProcess, &ret_val)) {
188       success = true;
189       if (exit_code)
190         *exit_code = ret_val;
191     }
192   }
193 
194   if (uac_foreground_window) {
195     DestroyWindow(uac_foreground_window);
196   }
197 
198   return success;
199 }
200 
GetChromeUninstallCmd(bool system_install)201 base::CommandLine InstallUtil::GetChromeUninstallCmd(bool system_install) {
202   ProductState state;
203   if (state.Initialize(system_install))
204     return state.uninstall_command();
205   return base::CommandLine(base::CommandLine::NO_PROGRAM);
206 }
207 
GetChromeVersion(bool system_install)208 base::Version InstallUtil::GetChromeVersion(bool system_install) {
209   base::Version version;
210   RegKey key;
211   base::string16 version_str;
212   if (key.Open(system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
213                install_static::GetClientsKeyPath().c_str(),
214                KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS &&
215       key.ReadValue(google_update::kRegVersionField, &version_str) ==
216           ERROR_SUCCESS &&
217       !version_str.empty()) {
218     version = base::Version(base::UTF16ToASCII(version_str));
219   }
220 
221   if (version.IsValid())
222     VLOG(1) << "Existing Chrome version found: " << version.GetString();
223   else
224     VLOG(1) << "No existing Chrome install found.";
225 
226   return version;
227 }
228 
GetCriticalUpdateVersion()229 base::Version InstallUtil::GetCriticalUpdateVersion() {
230   base::Version version;
231   RegKey key;
232   base::string16 version_str;
233   if (key.Open(install_static::IsSystemInstall() ? HKEY_LOCAL_MACHINE
234                                                  : HKEY_CURRENT_USER,
235                install_static::GetClientsKeyPath().c_str(),
236                KEY_QUERY_VALUE | KEY_WOW64_32KEY) == ERROR_SUCCESS &&
237       key.ReadValue(google_update::kRegCriticalVersionField, &version_str) ==
238           ERROR_SUCCESS &&
239       !version_str.empty()) {
240     version = base::Version(base::UTF16ToASCII(version_str));
241   }
242 
243   if (version.IsValid())
244     VLOG(1) << "Critical Update version found: " << version.GetString();
245   else
246     VLOG(1) << "No existing Chrome install found.";
247 
248   return version;
249 }
250 
IsOSSupported()251 bool InstallUtil::IsOSSupported() {
252   // We do not support anything prior to Windows 7.
253   VLOG(1) << base::SysInfo::OperatingSystemName() << ' '
254           << base::SysInfo::OperatingSystemVersion();
255   return base::win::GetVersion() >= base::win::Version::WIN7;
256 }
257 
AddInstallerResultItems(bool system_install,const base::string16 & state_key,installer::InstallStatus status,int string_resource_id,const base::string16 * const launch_cmd,WorkItemList * install_list)258 void InstallUtil::AddInstallerResultItems(
259     bool system_install,
260     const base::string16& state_key,
261     installer::InstallStatus status,
262     int string_resource_id,
263     const base::string16* const launch_cmd,
264     WorkItemList* install_list) {
265   DCHECK(install_list);
266   DCHECK(install_list->best_effort());
267   DCHECK(!install_list->rollback_enabled());
268 
269   const HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
270   DWORD installer_result = (GetInstallReturnCode(status) == 0) ? 0 : 1;
271   install_list->AddCreateRegKeyWorkItem(root, state_key, KEY_WOW64_32KEY);
272   install_list->AddSetRegValueWorkItem(root, state_key, KEY_WOW64_32KEY,
273                                        installer::kInstallerResult,
274                                        installer_result, true);
275   install_list->AddSetRegValueWorkItem(root, state_key, KEY_WOW64_32KEY,
276                                        installer::kInstallerError,
277                                        static_cast<DWORD>(status), true);
278   if (string_resource_id != 0) {
279     base::string16 msg = installer::GetLocalizedString(string_resource_id);
280     install_list->AddSetRegValueWorkItem(root, state_key, KEY_WOW64_32KEY,
281                                          installer::kInstallerResultUIString,
282                                          msg, true);
283   }
284   if (launch_cmd != nullptr && !launch_cmd->empty()) {
285     install_list->AddSetRegValueWorkItem(
286         root, state_key, KEY_WOW64_32KEY,
287         installer::kInstallerSuccessLaunchCmdLine, *launch_cmd, true);
288   }
289 }
290 
IsPerUserInstall()291 bool InstallUtil::IsPerUserInstall() {
292   return !install_static::InstallDetails::Get().system_level();
293 }
294 
295 // static
IsFirstRunSentinelPresent()296 bool InstallUtil::IsFirstRunSentinelPresent() {
297   // TODO(msw): Consolidate with first_run::internal::IsFirstRunSentinelPresent.
298   base::FilePath user_data_dir;
299   return !base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir) ||
300          base::PathExists(user_data_dir.Append(chrome::kFirstRunSentinel));
301 }
302 
303 // static
IsStartMenuShortcutWithActivatorGuidInstalled()304 bool InstallUtil::IsStartMenuShortcutWithActivatorGuidInstalled() {
305   base::FilePath shortcut_path;
306 
307   if (!ShellUtil::GetShortcutPath(ShellUtil::SHORTCUT_LOCATION_START_MENU_ROOT,
308                                   install_static::IsSystemInstall()
309                                       ? ShellUtil::SYSTEM_LEVEL
310                                       : ShellUtil::CURRENT_USER,
311                                   &shortcut_path)) {
312     LogStartMenuShortcutStatus(StartMenuShortcutStatus::kGetShortcutPathFailed);
313     return false;
314   }
315 
316   shortcut_path = shortcut_path.Append(GetShortcutName() + installer::kLnkExt);
317   if (!base::PathExists(shortcut_path)) {
318     LogStartMenuShortcutStatus(StartMenuShortcutStatus::kShortcutMissing);
319     return false;
320   }
321 
322   base::win::ShortcutProperties properties;
323   if (!base::win::ResolveShortcutProperties(
324           shortcut_path,
325           base::win::ShortcutProperties::PROPERTIES_TOAST_ACTIVATOR_CLSID,
326           &properties)) {
327     LogStartMenuShortcutStatus(
328         StartMenuShortcutStatus::kReadShortcutPropertyFailed);
329     return false;
330   }
331 
332   if (!::IsEqualCLSID(properties.toast_activator_clsid,
333                       install_static::GetToastActivatorClsid())) {
334     LogStartMenuShortcutStatus(
335         StartMenuShortcutStatus::kToastActivatorClsidIncorrect);
336 
337     return false;
338   }
339 
340   LogStartMenuShortcutStatus(StartMenuShortcutStatus::kSuccess);
341   return true;
342 }
343 
344 // static
GetToastActivatorRegistryPath()345 base::string16 InstallUtil::GetToastActivatorRegistryPath() {
346   return STRING16_LITERAL("Software\\Classes\\CLSID\\") +
347          base::win::WStringFromGUID(install_static::GetToastActivatorClsid());
348 }
349 
350 // static
GetEulaSentinelFilePath(base::FilePath * path)351 bool InstallUtil::GetEulaSentinelFilePath(base::FilePath* path) {
352   base::FilePath user_data_dir;
353   if (!base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir))
354     return false;
355   *path = user_data_dir.Append(installer::kEulaSentinelFile);
356   return true;
357 }
358 
359 // This method tries to delete a registry key and logs an error message
360 // in case of failure. It returns true if deletion is successful (or the key did
361 // not exist), otherwise false.
DeleteRegistryKey(HKEY root_key,const base::string16 & key_path,REGSAM wow64_access)362 bool InstallUtil::DeleteRegistryKey(HKEY root_key,
363                                     const base::string16& key_path,
364                                     REGSAM wow64_access) {
365   VLOG(1) << "Deleting registry key " << key_path;
366   RegKey target_key;
367   LONG result =
368       target_key.Open(root_key, key_path.c_str(), DELETE | wow64_access);
369 
370   if (result == ERROR_FILE_NOT_FOUND)
371     return true;
372 
373   if (result == ERROR_SUCCESS)
374     result = target_key.DeleteKey(L"");
375 
376   if (result != ERROR_SUCCESS) {
377     LOG(ERROR) << "Failed to delete registry key: " << key_path
378                << " error: " << result;
379     return false;
380   }
381   return true;
382 }
383 
384 // This method tries to delete a registry value and logs an error message
385 // in case of failure. It returns true if deletion is successful (or the key did
386 // not exist), otherwise false.
DeleteRegistryValue(HKEY reg_root,const base::string16 & key_path,REGSAM wow64_access,const base::string16 & value_name)387 bool InstallUtil::DeleteRegistryValue(HKEY reg_root,
388                                       const base::string16& key_path,
389                                       REGSAM wow64_access,
390                                       const base::string16& value_name) {
391   RegKey key;
392   LONG result =
393       key.Open(reg_root, key_path.c_str(), KEY_SET_VALUE | wow64_access);
394   if (result == ERROR_SUCCESS)
395     result = key.DeleteValue(value_name.c_str());
396   if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
397     LOG(ERROR) << "Failed to delete registry value: " << value_name
398                << " error: " << result;
399     return false;
400   }
401   return true;
402 }
403 
404 // static
DeleteRegistryKeyIf(HKEY root_key,const base::string16 & key_to_delete_path,const base::string16 & key_to_test_path,const REGSAM wow64_access,const wchar_t * value_name,const RegistryValuePredicate & predicate)405 InstallUtil::ConditionalDeleteResult InstallUtil::DeleteRegistryKeyIf(
406     HKEY root_key,
407     const base::string16& key_to_delete_path,
408     const base::string16& key_to_test_path,
409     const REGSAM wow64_access,
410     const wchar_t* value_name,
411     const RegistryValuePredicate& predicate) {
412   DCHECK(root_key);
413   ConditionalDeleteResult delete_result = NOT_FOUND;
414   RegKey key;
415   base::string16 actual_value;
416   if (key.Open(root_key, key_to_test_path.c_str(),
417                KEY_QUERY_VALUE | wow64_access) == ERROR_SUCCESS &&
418       key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS &&
419       predicate.Evaluate(actual_value)) {
420     key.Close();
421     delete_result =
422         DeleteRegistryKey(root_key, key_to_delete_path, wow64_access)
423             ? DELETED
424             : DELETE_FAILED;
425   }
426   return delete_result;
427 }
428 
429 // static
DeleteRegistryValueIf(HKEY root_key,const wchar_t * key_path,REGSAM wow64_access,const wchar_t * value_name,const RegistryValuePredicate & predicate)430 InstallUtil::ConditionalDeleteResult InstallUtil::DeleteRegistryValueIf(
431     HKEY root_key,
432     const wchar_t* key_path,
433     REGSAM wow64_access,
434     const wchar_t* value_name,
435     const RegistryValuePredicate& predicate) {
436   DCHECK(root_key);
437   DCHECK(key_path);
438   ConditionalDeleteResult delete_result = NOT_FOUND;
439   RegKey key;
440   base::string16 actual_value;
441   if (key.Open(root_key, key_path,
442                KEY_QUERY_VALUE | KEY_SET_VALUE | wow64_access) ==
443           ERROR_SUCCESS &&
444       key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS &&
445       predicate.Evaluate(actual_value)) {
446     LONG result = key.DeleteValue(value_name);
447     if (result != ERROR_SUCCESS) {
448       LOG(ERROR) << "Failed to delete registry value: "
449                  << (value_name ? value_name : L"(Default)")
450                  << " error: " << result;
451       delete_result = DELETE_FAILED;
452     } else {
453       delete_result = DELETED;
454     }
455   }
456   return delete_result;
457 }
458 
Evaluate(const base::string16 & value) const459 bool InstallUtil::ValueEquals::Evaluate(const base::string16& value) const {
460   return value == value_to_match_;
461 }
462 
463 // static
GetInstallReturnCode(installer::InstallStatus status)464 int InstallUtil::GetInstallReturnCode(installer::InstallStatus status) {
465   switch (status) {
466     case installer::FIRST_INSTALL_SUCCESS:
467     case installer::INSTALL_REPAIRED:
468     case installer::NEW_VERSION_UPDATED:
469     case installer::IN_USE_UPDATED:
470     case installer::OLD_VERSION_DOWNGRADE:
471     case installer::IN_USE_DOWNGRADE:
472       return 0;
473     default:
474       return status;
475   }
476 }
477 
478 // static
ComposeCommandLine(const base::string16 & program,const base::string16 & arguments,base::CommandLine * command_line)479 void InstallUtil::ComposeCommandLine(const base::string16& program,
480                                      const base::string16& arguments,
481                                      base::CommandLine* command_line) {
482   *command_line =
483       base::CommandLine::FromString(L"\"" + program + L"\" " + arguments);
484 }
485 
AppendModeAndChannelSwitches(base::CommandLine * command_line)486 void InstallUtil::AppendModeAndChannelSwitches(
487     base::CommandLine* command_line) {
488   const install_static::InstallDetails& install_details =
489       install_static::InstallDetails::Get();
490   if (*install_details.install_switch())
491     command_line->AppendSwitch(install_details.install_switch());
492   if (install_details.channel_origin() ==
493       install_static::ChannelOrigin::kPolicy) {
494     command_line->AppendSwitchNative(installer::switches::kChannel,
495                                      install_details.channel());
496   }
497 }
498 
499 // static
GetCurrentDate()500 base::string16 InstallUtil::GetCurrentDate() {
501   static const wchar_t kDateFormat[] = L"yyyyMMdd";
502   wchar_t date_str[base::size(kDateFormat)] = {0};
503   int len = GetDateFormatW(LOCALE_INVARIANT, 0, nullptr, kDateFormat, date_str,
504                            base::size(date_str));
505   if (len) {
506     --len;  // Subtract terminating \0.
507   } else {
508     PLOG(DFATAL) << "GetDateFormat";
509   }
510 
511   return base::string16(date_str, len);
512 }
513 
514 // Open |path| with minimal access to obtain information about it, returning
515 // true and populating |file| on success.
516 // static
OpenForInfo(const base::FilePath & path,base::File * file)517 bool InstallUtil::ProgramCompare::OpenForInfo(const base::FilePath& path,
518                                               base::File* file) {
519   DCHECK(file);
520   file->Initialize(path, base::File::FLAG_OPEN | base::File::FLAG_SHARE_DELETE);
521   return file->IsValid();
522 }
523 
524 // Populate |info| for |file|, returning true on success.
525 // static
GetInfo(const base::File & file,BY_HANDLE_FILE_INFORMATION * info)526 bool InstallUtil::ProgramCompare::GetInfo(const base::File& file,
527                                           BY_HANDLE_FILE_INFORMATION* info) {
528   DCHECK(file.IsValid());
529   return GetFileInformationByHandle(file.GetPlatformFile(), info) != 0;
530 }
531 
532 // static
GetDowngradeVersion()533 base::Optional<base::Version> InstallUtil::GetDowngradeVersion() {
534   RegKey key;
535   base::string16 downgrade_version;
536   if (key.Open(install_static::IsSystemInstall() ? HKEY_LOCAL_MACHINE
537                                                  : HKEY_CURRENT_USER,
538                install_static::GetClientStateKeyPath().c_str(),
539                KEY_QUERY_VALUE | KEY_WOW64_32KEY) != ERROR_SUCCESS ||
540       key.ReadValue(kRegDowngradeVersion, &downgrade_version) !=
541           ERROR_SUCCESS ||
542       downgrade_version.empty()) {
543     return base::nullopt;
544   }
545   base::Version version(base::UTF16ToASCII(downgrade_version));
546   if (!version.IsValid())
547     return base::nullopt;
548   return version;
549 }
550 
551 // static
AddUpdateDowngradeVersionItem(HKEY root,const base::Version & current_version,const base::Version & new_version,WorkItemList * list)552 void InstallUtil::AddUpdateDowngradeVersionItem(
553     HKEY root,
554     const base::Version& current_version,
555     const base::Version& new_version,
556     WorkItemList* list) {
557   DCHECK(list);
558   const auto downgrade_version = GetDowngradeVersion();
559   if (current_version.IsValid() && new_version < current_version) {
560     // This is a downgrade. Write the value if this is the first one (i.e., no
561     // previous value exists). Otherwise, leave any existing value in place.
562     if (!downgrade_version) {
563       list->AddSetRegValueWorkItem(
564           root, install_static::GetClientStateKeyPath(), KEY_WOW64_32KEY,
565           kRegDowngradeVersion, base::ASCIIToUTF16(current_version.GetString()),
566           true);
567     }
568   } else if (!current_version.IsValid() || new_version >= downgrade_version) {
569     // This is a new install or an upgrade to/past a previous DowngradeVersion.
570     list->AddDeleteRegValueWorkItem(root,
571                                     install_static::GetClientStateKeyPath(),
572                                     KEY_WOW64_32KEY, kRegDowngradeVersion);
573   }
574 }
575 
576 // static
577 std::vector<std::pair<std::wstring, std::wstring>>
GetCloudManagementEnrollmentTokenRegistryPaths()578 InstallUtil::GetCloudManagementEnrollmentTokenRegistryPaths() {
579   std::vector<std::pair<std::wstring, std::wstring>> paths;
580   // Prefer the product-agnostic location used by Google Update.
581   paths.emplace_back(GetCloudManagementPoliciesRegistryPath(),
582                      L"EnrollmentToken");
583   // Follow that with the Google Chrome policy.
584   paths.emplace_back(GetChromePoliciesRegistryPath(),
585                      L"CloudManagementEnrollmentToken");
586   return paths;
587 }
588 
589 // static
590 std::pair<base::win::RegKey, std::wstring>
GetCloudManagementDmTokenLocation(ReadOnly read_only,BrowserLocation browser_location)591 InstallUtil::GetCloudManagementDmTokenLocation(
592     ReadOnly read_only,
593     BrowserLocation browser_location) {
594   // The location dictates the path and WoW bit.
595   REGSAM wow_access = 0;
596   std::wstring key_path(L"SOFTWARE\\");
597   if (browser_location) {
598     wow_access |= KEY_WOW64_64KEY;
599     install_static::AppendChromeInstallSubDirectory(
600         install_static::InstallDetails::Get().mode(), /*include_suffix=*/false,
601         &key_path);
602   } else {
603     wow_access |= KEY_WOW64_32KEY;
604     key_path.append(install_static::kCompanyPathName);
605   }
606   key_path.append(L"\\Enrollment");
607 
608   base::win::RegKey key;
609   if (read_only) {
610     key.Open(HKEY_LOCAL_MACHINE, key_path.c_str(),
611              KEY_QUERY_VALUE | wow_access);
612   } else {
613     auto result = key.Create(HKEY_LOCAL_MACHINE, key_path.c_str(),
614                              KEY_SET_VALUE | wow_access);
615     if (result != ERROR_SUCCESS) {
616       ::SetLastError(result);
617       PLOG(ERROR) << "Failed to create/open registry key HKLM\\" << key_path
618                   << " for writing";
619     }
620   }
621 
622   return {std::move(key), L"dmtoken"};
623 }
624 
625 // static
GetCloudManagementEnrollmentToken()626 base::string16 InstallUtil::GetCloudManagementEnrollmentToken() {
627   // Because chrome needs to know if machine level user cloud policies must be
628   // initialized even before the entire policy service is brought up, this
629   // helper function exists to directly read the token from the system policies.
630   //
631   // Putting the enrollment token in the system policy area is a convenient
632   // way for administrators to enroll chrome throughout their fleet by pushing
633   // this token via SCCM.
634   // TODO(rogerta): This may not be the best place for the helpers dealing with
635   // the enrollment and/or DM tokens.  See crbug.com/823852 for details.
636   RegKey key;
637   base::string16 value;
638   for (const auto& key_and_value :
639        GetCloudManagementEnrollmentTokenRegistryPaths()) {
640     if (key.Open(HKEY_LOCAL_MACHINE, key_and_value.first.c_str(),
641                  KEY_QUERY_VALUE) == ERROR_SUCCESS &&
642         key.ReadValue(key_and_value.second.c_str(), &value) == ERROR_SUCCESS) {
643       return value;
644     }
645   }
646 
647   return base::string16();
648 }
649 
650 // static
ShouldCloudManagementBlockOnFailure()651 bool InstallUtil::ShouldCloudManagementBlockOnFailure() {
652   base::string16 key_path;
653   base::string16 value_name;
654   GetCloudManagementBlockOnFailureRegistryPath(&key_path, &value_name);
655 
656   DWORD value = 0;
657   RegKey(HKEY_LOCAL_MACHINE, key_path.c_str(), KEY_QUERY_VALUE)
658       .ReadValueDW(value_name.c_str(), &value);
659 
660   return value != 0;
661 }
662 
663 // static
GetDisplayName()664 base::string16 InstallUtil::GetDisplayName() {
665   return GetShortcutName();
666 }
667 
668 // static
GetAppDescription()669 base::string16 InstallUtil::GetAppDescription() {
670   return installer::GetLocalizedString(IDS_SHORTCUT_TOOLTIP_BASE);
671 }
672 
673 // static
GetPublisherName()674 base::string16 InstallUtil::GetPublisherName() {
675   return installer::GetLocalizedString(IDS_ABOUT_VERSION_COMPANY_NAME_BASE);
676 }
677 
678 // static
GetShortcutName()679 base::string16 InstallUtil::GetShortcutName() {
680   // IDS_PRODUCT_NAME is automatically mapped to the mode-specific shortcut
681   // name; see MODE_SPECIFIC_STRINGS in prebuild/create_string_rc.py.
682   return installer::GetLocalizedString(IDS_PRODUCT_NAME_BASE);
683 }
684 
685 // static
GetChromeShortcutDirNameDeprecated()686 base::string16 InstallUtil::GetChromeShortcutDirNameDeprecated() {
687   return GetShortcutName();
688 }
689 
690 // static
GetChromeAppsShortcutDirName()691 base::string16 InstallUtil::GetChromeAppsShortcutDirName() {
692   // IDS_APP_SHORTCUTS_SUBDIR_NAME is automatically mapped to the mode-specific
693   // dir name; see MODE_SPECIFIC_STRINGS in prebuild/create_string_rc.py.
694   return installer::GetLocalizedString(IDS_APP_SHORTCUTS_SUBDIR_NAME_BASE);
695 }
696 
697 // static
GetLongAppDescription()698 base::string16 InstallUtil::GetLongAppDescription() {
699   return installer::GetLocalizedString(IDS_PRODUCT_DESCRIPTION_BASE);
700 }
701 
ProgramCompare(const base::FilePath & path_to_match)702 InstallUtil::ProgramCompare::ProgramCompare(const base::FilePath& path_to_match)
703     : path_to_match_(path_to_match), file_info_() {
704   DCHECK(!path_to_match_.empty());
705   if (!OpenForInfo(path_to_match_, &file_)) {
706     PLOG(WARNING) << "Failed opening " << path_to_match_.value()
707                   << "; falling back to path string comparisons.";
708   } else if (!GetInfo(file_, &file_info_)) {
709     PLOG(WARNING) << "Failed getting information for " << path_to_match_.value()
710                   << "; falling back to path string comparisons.";
711     file_.Close();
712   }
713 }
714 
~ProgramCompare()715 InstallUtil::ProgramCompare::~ProgramCompare() {}
716 
Evaluate(const base::string16 & value) const717 bool InstallUtil::ProgramCompare::Evaluate(const base::string16& value) const {
718   // Suss out the exe portion of the value, which is expected to be a command
719   // line kinda (or exactly) like:
720   // "c:\foo\bar\chrome.exe" -- "%1"
721   base::FilePath program(base::CommandLine::FromString(value).GetProgram());
722   if (program.empty()) {
723     LOG(WARNING) << "Failed to parse an executable name from command line: \""
724                  << value << "\"";
725     return false;
726   }
727 
728   return EvaluatePath(program);
729 }
730 
EvaluatePath(const base::FilePath & path) const731 bool InstallUtil::ProgramCompare::EvaluatePath(
732     const base::FilePath& path) const {
733   // Try the simple thing first: do the paths happen to match?
734   if (base::FilePath::CompareEqualIgnoreCase(path_to_match_.value(),
735                                              path.value()))
736     return true;
737 
738   // If the paths don't match and we couldn't open the expected file, we've done
739   // our best.
740   if (!file_.IsValid())
741     return false;
742 
743   // Open the program and see if it references the expected file.
744   base::File file;
745   BY_HANDLE_FILE_INFORMATION info = {};
746 
747   return (OpenForInfo(path, &file) && GetInfo(file, &info) &&
748           info.dwVolumeSerialNumber == file_info_.dwVolumeSerialNumber &&
749           info.nFileIndexHigh == file_info_.nFileIndexHigh &&
750           info.nFileIndexLow == file_info_.nFileIndexLow);
751 }
752 
753 // static
GuidToSquid(base::StringPiece16 guid)754 base::string16 InstallUtil::GuidToSquid(base::StringPiece16 guid) {
755   base::string16 squid;
756   squid.reserve(32);
757   auto* input = guid.begin();
758   auto output = std::back_inserter(squid);
759 
760   // Reverse-copy relevant characters, skipping separators.
761   std::reverse_copy(input + 0, input + 8, output);
762   std::reverse_copy(input + 9, input + 13, output);
763   std::reverse_copy(input + 14, input + 18, output);
764   std::reverse_copy(input + 19, input + 21, output);
765   std::reverse_copy(input + 21, input + 23, output);
766   std::reverse_copy(input + 24, input + 26, output);
767   std::reverse_copy(input + 26, input + 28, output);
768   std::reverse_copy(input + 28, input + 30, output);
769   std::reverse_copy(input + 30, input + 32, output);
770   std::reverse_copy(input + 32, input + 34, output);
771   std::reverse_copy(input + 34, input + 36, output);
772   return squid;
773 }
774