// Copyright 2020 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/credential_provider/setup/setup_utils.h" #include #include #include "base/files/file_util.h" #include "base/json/json_string_value_serializer.h" #include "base/strings/utf_string_conversions.h" #include "base/win/registry.h" #include "chrome/common/chrome_version.h" #include "chrome/credential_provider/common/gcp_strings.h" #include "chrome/credential_provider/gaiacp/gaia_resources.h" #include "chrome/credential_provider/gaiacp/gcp_utils.h" #include "chrome/credential_provider/gaiacp/logging.h" #include "chrome/credential_provider/gaiacp/reg_utils.h" #include "chrome/credential_provider/setup/gcpw_files.h" namespace credential_provider { namespace switches { const char kParentHandle[] = "parent-handle"; const char kInstallPath[] = "install-path"; const char kUninstall[] = "uninstall"; const char kEnableStats[] = "enable-stats"; const char kDisableStats[] = "disable-stats"; const char kInstallerData[] = "installerdata"; const char kStandaloneInstall[] = "standalone"; } // namespace switches namespace { // Path to the msi json value inside the dictionary which is parsed from // installer data argument in the command line. const char kMsiJsonPath[] = "distribution.msi"; // The registry name which is saved to indicate installation source. const wchar_t kMsiInstall[] = L"msi"; // Parses the json data and returns it as a dictionary. If the json data isn't // valid, returns nullptr. base::DictionaryValue* ParseDistributionPreferences( const std::string& json_data) { JSONStringValueDeserializer json(json_data); std::string error; std::unique_ptr root(json.Deserialize(nullptr, &error)); if (!root.get()) { LOGFN(WARNING) << "Failed to parse initial prefs file: " << error; return nullptr; } if (!root->is_dict()) { LOGFN(WARNING) << "Failed to parse installer data file"; return nullptr; } return static_cast(root.release()); } } // namespace StandaloneInstallerConfigurator::StandaloneInstallerConfigurator() : is_msi_installation_(false) {} StandaloneInstallerConfigurator::~StandaloneInstallerConfigurator() {} // static StandaloneInstallerConfigurator** StandaloneInstallerConfigurator::GetInstanceStorage() { static StandaloneInstallerConfigurator* instance = new StandaloneInstallerConfigurator(); return &instance; } // static StandaloneInstallerConfigurator* StandaloneInstallerConfigurator::Get() { return *GetInstanceStorage(); } // Sets the installer source for GCPW. When installed through MSI, // contains installer data file name as argument. void StandaloneInstallerConfigurator::ConfigureInstallationType( const base::CommandLine& cmdline) { // There are following scenarios for installations: // First time install from MSI // First time install from EXE // MSIs before this kMsiInstall registry gets auto-updated // MSIs with kMsiInstall registry gets auto-updated // EXEs with kMsiInstall registry gets auto-updated // |kStandaloneInstall| indicates fresh installation. if (cmdline.HasSwitch(switches::kStandaloneInstall)) { base::Value* is_msi = nullptr; if (cmdline.HasSwitch(switches::kInstallerData)) { base::FilePath prefs_path( cmdline.GetSwitchValuePath(switches::kInstallerData)); if (InitializeFromInstallerData(prefs_path)) is_msi = installer_data_dictionary_->FindPath(kMsiJsonPath); } is_msi_installation_ = false; if (is_msi && is_msi->is_bool() && is_msi->GetBool()) { is_msi_installation_ = true; } } else { // Honor the registry if it is found, otherwise fall back to MSI // installation. is_msi_installation_ = GetUpdaterClientsAppPathFlagOrDefault(kMsiInstall, 1); } HRESULT hr = SetUpdaterClientsAppPathFlag(kMsiInstall, is_msi_installation_ ? 1 : 0); if (FAILED(hr)) LOGFN(ERROR) << "SetGlobalFlag failed" << putHR(hr); } base::string16 StandaloneInstallerConfigurator::GetCurrentDate() { static const wchar_t kDateFormat[] = L"yyyyMMdd"; wchar_t date_str[base::size(kDateFormat)] = {0}; int len = GetDateFormatW(LOCALE_INVARIANT, 0, nullptr, kDateFormat, date_str, base::size(date_str)); if (len) { --len; // Subtract terminating \0. } else { LOGFN(ERROR) << "GetDateFormat failed"; return L""; } return base::string16(date_str, len); } bool StandaloneInstallerConfigurator::IsStandaloneInstallation() const { return !is_msi_installation_; } HRESULT StandaloneInstallerConfigurator::AddUninstallKey( const base::FilePath& install_path) { LOGFN(VERBOSE); if (is_msi_installation_) return S_OK; std::wstring uninstall_reg = kRegUninstall; uninstall_reg.append(L"\\"); uninstall_reg.append(kRegUninstallProduct); base::win::RegKey key; LONG status = key.Create(HKEY_LOCAL_MACHINE, uninstall_reg.c_str(), KEY_SET_VALUE); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to create " << uninstall_reg << " hr=" << putHR(hr); return hr; } base::CommandLine uninstall_string( install_path.Append(kCredentialProviderSetupExe)); uninstall_string.AppendSwitch(switches::kUninstall); status = key.WriteValue(kRegUninstallString, uninstall_string.GetCommandLineString().c_str()); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to write " << kRegUninstallString << " hr=" << putHR(hr); return hr; } status = key.WriteValue(kRegUninstallDisplayName, GetStringResource(IDS_PROJNAME_BASE).c_str()); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to write " << kRegUninstallDisplayName << " hr=" << putHR(hr); return hr; } status = key.WriteValue(kRegInstallLocation, install_path.value().c_str()); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to write " << kRegInstallLocation << " hr=" << putHR(hr); return hr; } status = key.WriteValue( kRegDisplayIcon, (install_path.Append(kCredentialProviderSetupExe).value() + L",0") .c_str()); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to write " << kRegDisplayIcon << " hr=" << putHR(hr); return hr; } status = key.WriteValue(kRegNoModify, 1); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to write " << kRegNoModify << " hr=" << putHR(hr); return hr; } status = key.WriteValue(kRegNoRepair, 1); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to write " << kRegNoRepair << " hr=" << putHR(hr); return hr; } status = key.WriteValue(kRegPublisherName, kRegPublisher); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to write " << kRegPublisherName << " hr=" << putHR(hr); return hr; } status = key.WriteValue(kRegInstallDate, GetCurrentDate().c_str()); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to write " << kRegInstallDate << " hr=" << putHR(hr); return hr; } base::Version version(CHROME_VERSION_STRING); status = key.WriteValue(kRegVersion, base::ASCIIToUTF16(version.GetString()).c_str()); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to write " << kRegVersion << " hr=" << putHR(hr); return hr; } status = key.WriteValue(kRegDisplayVersion, base::ASCIIToUTF16(version.GetString()).c_str()); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to write " << kRegDisplayVersion << " hr=" << putHR(hr); return hr; } const std::vector& version_components = version.components(); if (version_components.size() == 4) { status = key.WriteValue(kRegVersionMajor, static_cast(version_components[2])); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to write " << kRegVersionMajor << " hr=" << putHR(hr); return hr; } status = key.WriteValue(kRegVersionMinor, static_cast(version_components[3])); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to write " << kRegVersionMinor << " hr=" << putHR(hr); return hr; } } return S_OK; } HRESULT StandaloneInstallerConfigurator::RemoveUninstallKey() { LOGFN(VERBOSE); base::win::RegKey key; LONG status = key.Create(HKEY_LOCAL_MACHINE, kRegUninstall, KEY_SET_VALUE); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to create " << kRegUninstall << " hr=" << putHR(hr); return hr; } status = key.DeleteKey(kRegUninstallProduct); if (status != ERROR_SUCCESS) { HRESULT hr = HRESULT_FROM_WIN32(status); LOGFN(ERROR) << "Unable to delete " << kRegUninstallProduct << " hr=" << putHR(hr); return hr; } return S_OK; } bool StandaloneInstallerConfigurator::InitializeFromInstallerData( base::FilePath prefs_path) { std::string json_data; if (base::PathExists(prefs_path) && !base::ReadFileToString(prefs_path, &json_data)) { LOGFN(ERROR) << "Failed to read preferences from " << prefs_path.value(); return false; } if (json_data.empty()) { LOGFN(WARNING) << "Installer data is empty!"; return false; } installer_data_dictionary_.reset(ParseDistributionPreferences(json_data)); if (!installer_data_dictionary_) { LOGFN(WARNING) << "Installer data is empty!"; return false; } return true; } } // namespace credential_provider