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, ¶ms);
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