// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <unknwn.h>
#include <datetimeapi.h>
#include <lmerr.h>
#include <wrl/client.h>
#include <memory>
#include <string>
#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/environment.h"
#include "base/file_version_info.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/process/launch.h"
#include "base/strings/strcat_win.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/syslog_logging.h"
#include "base/test/scoped_path_override.h"
#include "base/test/test_reg_util_win.h"
#include "base/win/atl.h"
#include "base/win/registry.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/win_util.h"
#include "build/build_config.h"
#include "chrome/common/chrome_version.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/extension/extension_strings.h"
#include "chrome/credential_provider/extension/extension_utils.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider.h"
#include "chrome/credential_provider/gaiacp/gaia_credential_provider_i.h"
#include "chrome/credential_provider/gaiacp/gcp_utils.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"
#include "chrome/credential_provider/setup/gcpw_files.h"
#include "chrome/credential_provider/setup/setup_lib.h"
#include "chrome/credential_provider/setup/setup_utils.h"
#include "chrome/credential_provider/test/gcp_fakes.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace credential_provider {
class GcpSetupTest : public ::testing::Test {
protected:
~GcpSetupTest() override;
const base::FilePath& module_path() const { return module_path_; }
const std::wstring& product_version() const { return product_version_; }
void CreateSentinelFileToSimulateCrash(const std::wstring& product_version);
void ExpectAllFilesToExist(bool exist, const std::wstring& product_version);
void ExpectSentinelFileToNotExist(const std::wstring& product_version);
void ExpectCredentialProviderToBeRegistered(
bool registered,
const std::wstring& product_version);
void ExpectRequiredRegistryEntriesToBePresent();
base::FilePath installed_path_for_version(
const std::wstring& product_version) {
return scoped_temp_prog_dir_.GetPath()
.Append(GetInstallParentDirectoryName())
.Append(FILE_PATH_LITERAL("Credential Provider"))
.Append(product_version);
}
base::FilePath sentinel_path_for_version(
const std::wstring& product_version) {
return scoped_temp_progdata_dir_.GetPath()
.Append(GetInstallParentDirectoryName())
.Append(FILE_PATH_LITERAL("Credential Provider"))
.Append(product_version)
.Append(FILE_PATH_LITERAL("gcpw_startup.sentinel"));
}
base::FilePath installed_path() {
return installed_path_for_version(product_version_);
}
FakeOSUserManager* fake_os_user_manager() { return &fake_os_user_manager_; }
FakeScopedLsaPolicyFactory* fake_scoped_lsa_policy_factory() {
return &fake_scoped_lsa_policy_factory_;
}
FakesForTesting* fakes_for_testing() { return &fakes_; }
std::wstring uninstall_reg_key() {
std::wstring uninstall_reg = kRegUninstall;
uninstall_reg.append(L"\\");
uninstall_reg.append(kRegUninstallProduct);
return uninstall_reg;
}
base::FilePath CreateFilePath(const std::string& file_name) {
return temp_dir_.GetPath().AppendASCII(file_name.c_str());
}
void CreateJsonFile(const base::FilePath& path, const std::string& data) {
ASSERT_TRUE(base::WriteFile(path, data));
}
void assert_addremove_reg_exists() {
base::win::RegKey uninstall_key;
ASSERT_EQ(ERROR_SUCCESS,
uninstall_key.Open(HKEY_LOCAL_MACHINE,
uninstall_reg_key().c_str(), KEY_ALL_ACCESS));
std::wstring uninstall_args;
std::wstring display_name;
std::wstring install_location;
std::wstring display_icon;
std::wstring install_date;
DWORD no_modify;
DWORD no_repair;
std::wstring publisher_name;
std::wstring version_str;
std::wstring display_version;
DWORD version_major;
DWORD version_minor;
ASSERT_EQ(ERROR_SUCCESS,
uninstall_key.ReadValue(kRegUninstallString, &uninstall_args));
ASSERT_EQ(ERROR_SUCCESS,
uninstall_key.ReadValue(kRegUninstallDisplayName, &display_name));
ASSERT_EQ(ERROR_SUCCESS,
uninstall_key.ReadValue(kRegInstallLocation, &install_location));
ASSERT_EQ(ERROR_SUCCESS,
uninstall_key.ReadValue(kRegDisplayIcon, &display_icon));
ASSERT_EQ(ERROR_SUCCESS,
uninstall_key.ReadValue(kRegInstallDate, &install_date));
ASSERT_EQ(ERROR_SUCCESS,
uninstall_key.ReadValueDW(kRegNoModify, &no_modify));
ASSERT_EQ(ERROR_SUCCESS,
uninstall_key.ReadValueDW(kRegNoRepair, &no_repair));
ASSERT_EQ(ERROR_SUCCESS,
uninstall_key.ReadValue(kRegPublisherName, &publisher_name));
ASSERT_EQ(ERROR_SUCCESS,
uninstall_key.ReadValue(kRegVersion, &version_str));
ASSERT_EQ(ERROR_SUCCESS,
uninstall_key.ReadValue(kRegDisplayVersion, &display_version));
ASSERT_EQ(ERROR_SUCCESS,
uninstall_key.ReadValueDW(kRegVersionMajor, &version_major));
ASSERT_EQ(ERROR_SUCCESS,
uninstall_key.ReadValueDW(kRegVersionMinor, &version_minor));
base::CommandLine uninstall_cmdline(
installed_path().Append(kCredentialProviderSetupExe));
uninstall_cmdline.AppendSwitch(switches::kUninstall);
ASSERT_EQ(uninstall_args, uninstall_cmdline.GetCommandLineString().c_str());
ASSERT_EQ(display_name, GetStringResource(IDS_PROJNAME_BASE));
ASSERT_EQ(install_location, installed_path().value());
ASSERT_EQ(publisher_name, kRegPublisher);
base::FilePath setup_exe =
installed_path().Append(kCredentialProviderSetupExe);
ASSERT_EQ(display_icon, (setup_exe.value() + L",0").c_str());
ASSERT_EQ(install_date, GetCurrentDateForTesting());
ASSERT_EQ(no_modify, (DWORD)1);
ASSERT_EQ(no_repair, (DWORD)1);
base::Version version(CHROME_VERSION_STRING);
ASSERT_EQ(version_str, base::ASCIIToWide(version.GetString()));
ASSERT_EQ(display_version, base::ASCIIToWide(version.GetString()));
const std::vector<uint32_t>& version_components = version.components();
ASSERT_EQ(version_major, static_cast<DWORD>(version_components[2]));
ASSERT_EQ(version_minor, static_cast<DWORD>(version_components[3]));
}
void SetUp() override;
private:
std::wstring GetCurrentDateForTesting() {
static const wchar_t kDateFormat[] = L"yyyyMMdd";
wchar_t date_str[std::size(kDateFormat)] = {0};
int len = GetDateFormatW(LOCALE_INVARIANT, 0, nullptr, kDateFormat,
date_str, std::size(date_str));
if (len) {
--len; // Subtract terminating \0.
} else {
return L"";
}
return std::wstring(date_str, len);
}
void GetModulePathAndProductVersion(base::FilePath* module_path,
std::wstring* product_version);
base::win::ScopedCOMInitializer com_initializer_{
base::win::ScopedCOMInitializer::kMTA};
registry_util::RegistryOverrideManager registry_override_;
base::ScopedTempDir scoped_temp_prog_dir_;
base::ScopedTempDir scoped_temp_start_menu_dir_;
base::ScopedTempDir scoped_temp_progdata_dir_;
base::ScopedTempDir temp_dir_;
std::unique_ptr<base::ScopedPathOverride> program_files_override_;
std::unique_ptr<base::ScopedPathOverride> start_menu_override_;
std::unique_ptr<base::ScopedPathOverride> programdata_override_;
std::unique_ptr<base::ScopedPathOverride> dll_path_override_;
base::FilePath module_path_;
std::wstring product_version_;
FakeGCPWFiles fake_gcpw_files_;
FakeOSUserManager fake_os_user_manager_;
FakeOSProcessManager fake_os_process_manager_;
FakeScopedLsaPolicyFactory fake_scoped_lsa_policy_factory_;
FakeOSServiceManager fake_os_service_manager_;
FakesForTesting fakes_;
};
GcpSetupTest::~GcpSetupTest() {
logging::ResetEventSourceForTesting();
}
void GcpSetupTest::GetModulePathAndProductVersion(
base::FilePath* module_path,
std::wstring* product_version) {
// Pass null module handle to get path for the executable file of the
// current process.
wchar_t module[MAX_PATH];
ASSERT_TRUE(::GetModuleFileName(nullptr, module, MAX_PATH));
// These tests assume all the binaries exist in the same directory.
*module_path = base::FilePath(module);
ASSERT_FALSE(module_path->empty());
*product_version = base::AsWString(
FileVersionInfo::CreateFileVersionInfo(*module_path)->product_version());
ASSERT_FALSE(product_version->empty());
}
void GcpSetupTest::CreateSentinelFileToSimulateCrash(
const std::wstring& product_version) {
base::FilePath sentinel_file = sentinel_path_for_version(product_version);
// Create the destination folder
ASSERT_TRUE(base::CreateDirectory(sentinel_file.DirName()));
base::win::ScopedHandle file(
CreateFile(sentinel_file.value().c_str(), GENERIC_WRITE, 0, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
ASSERT_TRUE(file.IsValid());
}
void GcpSetupTest::ExpectSentinelFileToNotExist(
const std::wstring& product_version) {
base::FilePath sentinel_file = sentinel_path_for_version(product_version);
EXPECT_EQ(false, base::PathExists(sentinel_file));
}
void GcpSetupTest::ExpectAllFilesToExist(bool exist,
const std::wstring& product_version) {
base::FilePath root = installed_path_for_version(product_version);
EXPECT_EQ(exist, base::PathExists(root));
bool extension_found = false;
auto install_files = GCPWFiles::Get()->GetEffectiveInstallFiles();
for (auto& install_file : install_files) {
if (kCredentialProviderExtensionExe.find(install_file) !=
base::FilePath::StringType::npos)
extension_found = true;
EXPECT_EQ(exist, base::PathExists(root.Append(install_file)));
}
EXPECT_EQ(extension::IsGCPWExtensionEnabled(), extension_found);
}
void GcpSetupTest::ExpectCredentialProviderToBeRegistered(
bool registered,
const std::wstring& product_version) {
auto guid_string = base::win::WStringFromGUID(CLSID_GaiaCredentialProvider);
// Make sure COM object is registered.
std::wstring register_key_path =
base::StrCat({L"CLSID\\", guid_string, L"\\InprocServer32"});
base::win::RegKey clsid_key(HKEY_CLASSES_ROOT, register_key_path.c_str(),
KEY_READ);
EXPECT_EQ(registered, clsid_key.Valid());
if (registered) {
base::FilePath path = installed_path_for_version(product_version)
.Append(FILE_PATH_LITERAL("Gaia1_0.dll"));
std::wstring value;
EXPECT_EQ(ERROR_SUCCESS, clsid_key.ReadValue(L"", &value));
EXPECT_EQ(path.value(), value);
}
std::wstring cp_key_path =
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Authentication\\"
L"Credential Providers\\" +
guid_string;
// Make sure credential provider is registered.
base::win::RegKey cp_key(HKEY_LOCAL_MACHINE, cp_key_path.c_str(), KEY_READ);
EXPECT_EQ(registered, cp_key.Valid());
// Make sure eventlog source is registered.
base::win::RegKey el_key(
HKEY_LOCAL_MACHINE,
L"SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\GCPW",
KEY_READ);
EXPECT_EQ(registered, el_key.Valid());
if (registered) {
base::FilePath path =
installed_path_for_version(product_version)
.Append(FILE_PATH_LITERAL("gcp_eventlog_provider.dll"));
std::wstring value;
EXPECT_EQ(ERROR_SUCCESS, el_key.ReadValue(L"EventMessageFile", &value));
EXPECT_EQ(path.value(), value);
}
}
void GcpSetupTest::ExpectRequiredRegistryEntriesToBePresent() {
base::win::RegKey key(HKEY_LOCAL_MACHINE, kGcpRootKeyName, KEY_READ);
EXPECT_TRUE(key.Valid());
}
void GcpSetupTest::SetUp() {
// Get the path to the setup exe (this exe during unit tests) and the
// chrome version.
GetModulePathAndProductVersion(&module_path_, &product_version_);
// Override registry so that tests don't mess up the machine's state.
ASSERT_NO_FATAL_FAILURE(
registry_override_.OverrideRegistry(HKEY_LOCAL_MACHINE));
ASSERT_NO_FATAL_FAILURE(
registry_override_.OverrideRegistry(HKEY_CLASSES_ROOT));
ASSERT_NO_FATAL_FAILURE(registry_override_.OverrideRegistry(HKEY_USERS));
// Override the environment used by setup so that tests don't mess
// up the machine's state.
std::unique_ptr<base::Environment> env(base::Environment::Create());
ASSERT_NE(nullptr, env.get());
ASSERT_TRUE(scoped_temp_prog_dir_.CreateUniqueTempDir());
program_files_override_ = std::make_unique<base::ScopedPathOverride>(
base::DIR_PROGRAM_FILES, scoped_temp_prog_dir_.GetPath());
ASSERT_TRUE(scoped_temp_start_menu_dir_.CreateUniqueTempDir());
start_menu_override_ = std::make_unique<base::ScopedPathOverride>(
base::DIR_COMMON_START_MENU, scoped_temp_start_menu_dir_.GetPath());
ASSERT_TRUE(scoped_temp_progdata_dir_.CreateUniqueTempDir());
programdata_override_ = std::make_unique<base::ScopedPathOverride>(
base::DIR_COMMON_APP_DATA, scoped_temp_progdata_dir_.GetPath());
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
// In non-component builds, base::FILE_MODULE will always return the path
// to base.dll because of the way CURRENT_MODULE works. Therefore overriding
// to point to gaia1_0.dll's destination path (i.e. after it is installed).
// The actual file name is not important, just the directory.
dll_path_override_ = std::make_unique<base::ScopedPathOverride>(
base::FILE_MODULE, installed_path().Append(FILE_PATH_LITERAL("foo.dll")),
true /*=is_absolute*/, false /*=create*/);
base::FilePath startup_path =
scoped_temp_start_menu_dir_.GetPath().Append(L"StartUp");
ASSERT_TRUE(base::CreateDirectoryAndGetError(startup_path, nullptr));
// Fake factories for testing the DLL registration.
fakes_.os_user_manager_for_testing = &fake_os_user_manager_;
fakes_.os_process_manager_for_testing = &fake_os_process_manager_;
fakes_.scoped_lsa_policy_creator =
fake_scoped_lsa_policy_factory_.GetCreatorCallback();
}
TEST_F(GcpSetupTest, DoInstall) {
logging::ResetEventSourceForTesting();
ASSERT_EQ(S_OK,
DoInstall(module_path(), product_version(), fakes_for_testing()));
ExpectAllFilesToExist(true, product_version());
ExpectCredentialProviderToBeRegistered(true, product_version());
ExpectRequiredRegistryEntriesToBePresent();
EXPECT_FALSE(
fake_os_user_manager()->GetUserInfo(kDefaultGaiaAccountName).sid.empty());
EXPECT_FALSE(fake_scoped_lsa_policy_factory()
->private_data()[kLsaKeyGaiaPassword]
.empty());
EXPECT_EQ(
kDefaultGaiaAccountName,
fake_scoped_lsa_policy_factory()->private_data()[kLsaKeyGaiaUsername]);
}
TEST_F(GcpSetupTest, DoInstallWithExtension) {
logging::ResetEventSourceForTesting();
base::win::RegKey key;
ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_LOCAL_MACHINE, kGcpRootKeyName,
KEY_SET_VALUE | KEY_WOW64_32KEY));
ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(extension::kEnableGCPWExtension, 1));
ASSERT_EQ(S_OK,
DoInstall(module_path(), product_version(), fakes_for_testing()));
ExpectAllFilesToExist(true, product_version());
ExpectCredentialProviderToBeRegistered(true, product_version());
ExpectRequiredRegistryEntriesToBePresent();
EXPECT_FALSE(
fake_os_user_manager()->GetUserInfo(kDefaultGaiaAccountName).sid.empty());
EXPECT_FALSE(fake_scoped_lsa_policy_factory()
->private_data()[kLsaKeyGaiaPassword]
.empty());
EXPECT_EQ(
kDefaultGaiaAccountName,
fake_scoped_lsa_policy_factory()->private_data()[kLsaKeyGaiaUsername]);
}
// Tests install over old install for different types of installations.
// 0 - Indicates that initial installation is standalone.
// 1 - Indicates that the initial installation is through MSI.
class GcpInstallOverOldInstallTest : public GcpSetupTest,
public ::testing::WithParamInterface<int> {
public:
void SetInstallerConfig(bool add_installer_data) {
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
// Indicates fresh installation.
command_line.AppendSwitch(switches::kStandaloneInstall);
if (add_installer_data) {
// Only set if installation source is MSI.
std::string installer_json = "{\"distribution\": {\"msi\": true}}";
base::FilePath installer_data_path = CreateFilePath("myfile.txt");
CreateJsonFile(installer_data_path, installer_json);
command_line.AppendSwitchPath(switches::kInstallerData,
installer_data_path);
}
StandaloneInstallerConfigurator::Get()->ConfigureInstallationType(
command_line);
}
};
// TODO: crbug.com/347201817 - Fix ODR violation.
#if BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER)
#define MAYBE_DoInstallOverOldInstall DISABLED_DoInstallOverOldInstall
#else
#define MAYBE_DoInstallOverOldInstall DoInstallOverOldInstall
#endif
TEST_P(GcpInstallOverOldInstallTest, MAYBE_DoInstallOverOldInstall) {
logging::ResetEventSourceForTesting();
// Set installer data argument to indicate the installation source.
SetInstallerConfig(GetParam());
// Install using some old version.
const std::wstring old_version(L"1.0.0.0");
ASSERT_EQ(S_OK, DoInstall(module_path(), old_version, fakes_for_testing()));
ExpectAllFilesToExist(true, old_version);
CreateSentinelFileToSimulateCrash(old_version);
FakeOSUserManager::UserInfo old_user_info =
fake_os_user_manager()->GetUserInfo(kDefaultGaiaAccountName);
std::wstring old_password =
fake_scoped_lsa_policy_factory()->private_data()[kLsaKeyGaiaPassword];
EXPECT_FALSE(old_password.empty());
std::wstring old_username =
fake_scoped_lsa_policy_factory()->private_data()[kLsaKeyGaiaUsername];
EXPECT_EQ(old_username, kDefaultGaiaAccountName);
logging::ResetEventSourceForTesting();
// Don't include any flag to indicate the installation source as
// the registry was set the first time this is called.
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
StandaloneInstallerConfigurator::Get()->ConfigureInstallationType(
command_line);
EXPECT_TRUE(
StandaloneInstallerConfigurator::Get()->IsStandaloneInstallation() ==
!GetParam());
// Now install a newer version.
ASSERT_EQ(S_OK,
DoInstall(module_path(), product_version(), fakes_for_testing()));
// Make sure newer version exists and old version is gone.
ExpectAllFilesToExist(true, product_version());
ExpectAllFilesToExist(false, old_version);
ExpectSentinelFileToNotExist(old_version);
// Make sure kGaiaAccountName info and private data are unchanged.
EXPECT_EQ(old_user_info,
fake_os_user_manager()->GetUserInfo(kDefaultGaiaAccountName));
EXPECT_EQ(
old_password,
fake_scoped_lsa_policy_factory()->private_data()[kLsaKeyGaiaPassword]);
EXPECT_EQ(
old_username,
fake_scoped_lsa_policy_factory()->private_data()[kLsaKeyGaiaUsername]);
}
INSTANTIATE_TEST_SUITE_P(All,
GcpInstallOverOldInstallTest,
::testing::Values(0, 1));
// TODO: crbug.com/347201817 - Fix ODR violation.
#if BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER)
#define MAYBE_DoInstallOverOldLockedInstall \
DISABLED_DoInstallOverOldLockedInstall
#else
#define MAYBE_DoInstallOverOldLockedInstall DoInstallOverOldLockedInstall
#endif
TEST_F(GcpSetupTest, MAYBE_DoInstallOverOldLockedInstall) {
logging::ResetEventSourceForTesting();
// Install using some old version.
const std::wstring old_version(L"1.0.0.0");
ASSERT_EQ(S_OK, DoInstall(module_path(), old_version, fakes_for_testing()));
ExpectAllFilesToExist(true, old_version);
// Lock the CP DLL.
base::FilePath dll_path = installed_path_for_version(old_version)
.Append(FILE_PATH_LITERAL("Gaia1_0.dll"));
base::File lock(dll_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_WIN_EXCLUSIVE_READ |
base::File::FLAG_WIN_EXCLUSIVE_WRITE);
logging::ResetEventSourceForTesting();
// Now install a newer version.
ASSERT_EQ(S_OK,
DoInstall(module_path(), product_version(), fakes_for_testing()));
// Make sure newer version exists.
ExpectAllFilesToExist(true, product_version());
// The locked file will still exist, the others are gone.
auto install_files = GCPWFiles::Get()->GetEffectiveInstallFiles();
for (auto& install_file : install_files) {
const base::FilePath path =
installed_path_for_version(old_version).Append(install_file);
EXPECT_EQ(path == dll_path, base::PathExists(path));
}
}
// TODO: crbug.com/347201817 - Fix ODR violation.
#if BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER)
#define MAYBE_LaunchGcpAfterInstall DISABLED_LaunchGcpAfterInstall
#else
#define MAYBE_LaunchGcpAfterInstall LaunchGcpAfterInstall
#endif
TEST_F(GcpSetupTest, MAYBE_LaunchGcpAfterInstall) {
logging::ResetEventSourceForTesting();
// Install using some old version.
const std::wstring old_version(L"1.0.0.0");
ASSERT_EQ(S_OK, DoInstall(module_path(), old_version, fakes_for_testing()));
ExpectAllFilesToExist(true, old_version);
// Lock the CP DLL.
base::FilePath dll_path = installed_path_for_version(old_version)
.Append(FILE_PATH_LITERAL("Gaia1_0.dll"));
base::File locked_file(dll_path, base::File::FLAG_OPEN |
base::File::FLAG_READ |
base::File::FLAG_WIN_EXCLUSIVE_READ |
base::File::FLAG_WIN_EXCLUSIVE_WRITE);
logging::ResetEventSourceForTesting();
// Now install a newer version.
ASSERT_EQ(S_OK,
DoInstall(module_path(), product_version(), fakes_for_testing()));
// Make sure newer version exists.
ExpectAllFilesToExist(true, product_version());
// The locked file will still exist, the others are gone.
auto install_files = GCPWFiles::Get()->GetEffectiveInstallFiles();
for (auto& install_file : install_files) {
const base::FilePath path =
installed_path_for_version(old_version).Append(install_file);
EXPECT_EQ(path == dll_path, base::PathExists(path));
}
locked_file.Close();
Microsoft::WRL::ComPtr<IGaiaCredentialProvider> provider;
ASSERT_EQ(S_OK,
CComCreator<CComObject<CGaiaCredentialProvider>>::CreateInstance(
nullptr, IID_PPV_ARGS(&provider)));
// Make sure newer version exists and old version is gone.
ExpectAllFilesToExist(true, product_version());
ExpectAllFilesToExist(false, old_version);
}
// Tests installations from exe and MSI.
// 0 - installation is not standalone.
// 1 - installation is standalone.
class GcpInstallerTest : public GcpSetupTest,
public ::testing::WithParamInterface<int> {};
// TODO: crbug.com/347201817 - Fix ODR violation.
#if BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER)
#define MAYBE_DoUninstall DISABLED_DoUninstall
#else
#define MAYBE_DoUninstall DoUninstall
#endif
TEST_P(GcpInstallerTest, MAYBE_DoUninstall) {
int standalone_installer = GetParam();
logging::ResetEventSourceForTesting();
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
command_line.AppendSwitch(switches::kStandaloneInstall);
if (!standalone_installer) {
std::string installer_json = "{\"distribution\": {\"msi\": true}}";
base::FilePath installer_data_path = CreateFilePath("myfile.txt");
CreateJsonFile(installer_data_path, installer_json);
command_line.AppendSwitchPath(switches::kInstallerData,
installer_data_path);
}
StandaloneInstallerConfigurator::Get()->ConfigureInstallationType(
command_line);
ASSERT_EQ(S_OK,
DoInstall(module_path(), product_version(), fakes_for_testing()));
if (standalone_installer)
assert_addremove_reg_exists();
CreateSentinelFileToSimulateCrash(product_version());
logging::ResetEventSourceForTesting();
ASSERT_EQ(S_OK,
DoUninstall(module_path(), installed_path(), fakes_for_testing()));
ExpectAllFilesToExist(false, product_version());
ExpectSentinelFileToNotExist(product_version());
ExpectCredentialProviderToBeRegistered(false, product_version());
EXPECT_TRUE(
fake_os_user_manager()->GetUserInfo(kDefaultGaiaAccountName).sid.empty());
EXPECT_TRUE(fake_scoped_lsa_policy_factory()
->private_data()[kLsaKeyGaiaPassword]
.empty());
EXPECT_TRUE(fake_scoped_lsa_policy_factory()
->private_data()[kLsaKeyGaiaUsername]
.empty());
base::win::RegKey uninstall_key;
EXPECT_NE(ERROR_SUCCESS,
uninstall_key.Open(HKEY_LOCAL_MACHINE, uninstall_reg_key().c_str(),
KEY_ALL_ACCESS));
}
INSTANTIATE_TEST_SUITE_P(All, GcpInstallerTest, ::testing::Values(0, 1));
// TODO: crbug.com/347201817 - Fix ODR violation.
#if BUILDFLAG(IS_WIN) && defined(ADDRESS_SANITIZER)
#define MAYBE_DoUninstallWithExtension DISABLED_DoUninstallWithExtension
#else
#define MAYBE_DoUninstallWithExtension DoUninstallWithExtension
#endif
TEST_F(GcpSetupTest, MAYBE_DoUninstallWithExtension) {
logging::ResetEventSourceForTesting();
base::win::RegKey key;
ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_LOCAL_MACHINE, kGcpRootKeyName,
KEY_SET_VALUE | KEY_WOW64_32KEY));
ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(extension::kEnableGCPWExtension, 1));
ASSERT_EQ(S_OK,
DoInstall(module_path(), product_version(), fakes_for_testing()));
CreateSentinelFileToSimulateCrash(product_version());
logging::ResetEventSourceForTesting();
ASSERT_EQ(S_OK,
DoUninstall(module_path(), installed_path(), fakes_for_testing()));
ExpectAllFilesToExist(false, product_version());
ExpectSentinelFileToNotExist(product_version());
ExpectCredentialProviderToBeRegistered(false, product_version());
EXPECT_TRUE(
fake_os_user_manager()->GetUserInfo(kDefaultGaiaAccountName).sid.empty());
EXPECT_TRUE(fake_scoped_lsa_policy_factory()
->private_data()[kLsaKeyGaiaPassword]
.empty());
EXPECT_TRUE(fake_scoped_lsa_policy_factory()
->private_data()[kLsaKeyGaiaUsername]
.empty());
}
TEST_F(GcpSetupTest, ValidLsaWithNoExistingUser) {
logging::ResetEventSourceForTesting();
// Create the default user so that name is not taken when the user is created.
CComBSTR sid;
DWORD error;
EXPECT_EQ(S_OK, fake_os_user_manager()->AddUser(
kDefaultGaiaAccountName, L"password", L"fullname",
L"comment", true, &sid, &error));
// Even if the LSA information is correct, if no actual user exists, a new
// user needs to be created.
fake_scoped_lsa_policy_factory()->private_data()[kLsaKeyGaiaUsername] =
L"gaia1";
fake_scoped_lsa_policy_factory()->private_data()[kLsaKeyGaiaPassword] =
L"password";
ASSERT_EQ(S_OK,
DoInstall(module_path(), product_version(), fakes_for_testing()));
logging::ResetEventSourceForTesting();
EXPECT_FALSE(fake_scoped_lsa_policy_factory()
->private_data()[kLsaKeyGaiaPassword]
.empty());
std::wstring expected_gaia_username =
L"gaia" + base::NumberToWString(kInitialDuplicateUsernameIndex);
EXPECT_FALSE(fake_os_user_manager()
->GetUserInfo(expected_gaia_username.c_str())
.sid.empty());
EXPECT_EQ(
expected_gaia_username,
fake_scoped_lsa_policy_factory()->private_data()[kLsaKeyGaiaUsername]);
}
TEST_F(GcpSetupTest, EnableStats) {
// Make sure usagestats does not exist.
base::win::RegKey key;
EXPECT_EQ(ERROR_SUCCESS,
key.Create(HKEY_LOCAL_MACHINE, kRegUpdaterClientStateAppPath,
KEY_ALL_ACCESS | KEY_WOW64_32KEY));
DWORD value;
EXPECT_NE(ERROR_SUCCESS, key.ReadValueDW(kRegUsageStatsName, &value));
// Enable stats.
base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
cmdline.AppendSwitch(switches::kEnableStats);
EXPECT_EQ(0, EnableStatsCollection(cmdline));
// Stats should be enabled.
EXPECT_EQ(ERROR_SUCCESS, key.ReadValueDW(kRegUsageStatsName, &value));
EXPECT_EQ(1u, value);
}
TEST_F(GcpSetupTest, DisableStats) {
// Make sure usagestats does not exist.
base::win::RegKey key;
EXPECT_EQ(ERROR_SUCCESS,
key.Create(HKEY_LOCAL_MACHINE, kRegUpdaterClientStateAppPath,
KEY_ALL_ACCESS | KEY_WOW64_32KEY));
DWORD value;
EXPECT_NE(ERROR_SUCCESS, key.ReadValueDW(kRegUsageStatsName, &value));
// Disable stats.
base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
cmdline.AppendSwitch(switches::kDisableStats);
EXPECT_EQ(0, EnableStatsCollection(cmdline));
// Stats should be disabled.
EXPECT_EQ(ERROR_SUCCESS, key.ReadValueDW(kRegUsageStatsName, &value));
EXPECT_EQ(0u, value);
}
TEST_F(GcpSetupTest, EnableDisableStats) {
// Make sure usagestats does not exist.
base::win::RegKey key;
EXPECT_EQ(ERROR_SUCCESS,
key.Create(HKEY_LOCAL_MACHINE, kRegUpdaterClientStateAppPath,
KEY_ALL_ACCESS | KEY_WOW64_32KEY));
DWORD value;
EXPECT_NE(ERROR_SUCCESS, key.ReadValueDW(kRegUsageStatsName, &value));
// Enable and disable stats.
base::CommandLine cmdline(base::CommandLine::NO_PROGRAM);
cmdline.AppendSwitch(switches::kEnableStats);
cmdline.AppendSwitch(switches::kDisableStats);
EXPECT_EQ(0, EnableStatsCollection(cmdline));
// Stats should be disabled.
EXPECT_EQ(ERROR_SUCCESS, key.ReadValueDW(kRegUsageStatsName, &value));
EXPECT_EQ(0u, value);
}
TEST_F(GcpSetupTest, WriteUninstallStringsForMSI) {
base::win::RegKey key;
ASSERT_EQ(ERROR_SUCCESS,
key.Create(HKEY_LOCAL_MACHINE, kRegUpdaterClientStateAppPath,
KEY_ALL_ACCESS | KEY_WOW64_32KEY));
// Write uninstall strings.
base::FilePath file_path =
installed_path().Append(FILE_PATH_LITERAL("foo.exe"));
ASSERT_EQ(S_OK, WriteUninstallRegistryValues(file_path));
// Verify uninstall strings.
std::wstring uninstall_string;
ASSERT_EQ(ERROR_SUCCESS,
key.ReadValue(kRegUninstallStringField, &uninstall_string));
EXPECT_EQ(uninstall_string, file_path.value());
std::wstring uninstall_arguments;
ASSERT_EQ(ERROR_SUCCESS,
key.ReadValue(kRegUninstallArgumentsField, &uninstall_arguments));
base::CommandLine expected_uninstall_arguments(base::CommandLine::NO_PROGRAM);
expected_uninstall_arguments.AppendSwitch(switches::kUninstall);
EXPECT_EQ(uninstall_arguments,
expected_uninstall_arguments.GetCommandLineString());
}
TEST_F(GcpSetupTest, WriteCredentialProviderRegistryValues) {
// Verify keys don't exist.
base::win::RegKey key;
ASSERT_NE(ERROR_SUCCESS,
key.Open(HKEY_LOCAL_MACHINE, kGcpRootKeyName, KEY_ALL_ACCESS));
base::win::RegKey uninstall_key;
ASSERT_NE(ERROR_SUCCESS,
uninstall_key.Open(HKEY_LOCAL_MACHINE, uninstall_reg_key().c_str(),
KEY_ALL_ACCESS));
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
// Set the standalone registry, but not installer data so that EXE
// installation is indicated.
command_line.AppendSwitch(switches::kStandaloneInstall);
StandaloneInstallerConfigurator::Get()->ConfigureInstallationType(
command_line);
// Write GCPW registry keys.
ASSERT_EQ(S_OK, WriteCredentialProviderRegistryValues(installed_path()));
// Verify keys were created.
ASSERT_EQ(ERROR_SUCCESS,
key.Open(HKEY_LOCAL_MACHINE, kGcpRootKeyName, KEY_ALL_ACCESS));
assert_addremove_reg_exists();
}
TEST_F(GcpSetupTest, DoInstallWritesUninstallStrings) {
logging::ResetEventSourceForTesting();
ASSERT_EQ(S_OK,
DoInstall(module_path(), product_version(), fakes_for_testing()));
ExpectAllFilesToExist(true, product_version());
base::win::RegKey key;
ASSERT_EQ(ERROR_SUCCESS,
key.Create(HKEY_LOCAL_MACHINE, kRegUpdaterClientStateAppPath,
KEY_ALL_ACCESS | KEY_WOW64_32KEY));
// Verify uninstall strings.
std::wstring uninstall_string;
ASSERT_EQ(ERROR_SUCCESS,
key.ReadValue(kRegUninstallStringField, &uninstall_string));
EXPECT_EQ(uninstall_string,
installed_path().Append(kCredentialProviderSetupExe).value());
std::wstring uninstall_arguments;
ASSERT_EQ(ERROR_SUCCESS,
key.ReadValue(kRegUninstallArgumentsField, &uninstall_arguments));
base::CommandLine expected_uninstall_arguments(base::CommandLine::NO_PROGRAM);
expected_uninstall_arguments.AppendSwitch(switches::kUninstall);
EXPECT_EQ(uninstall_arguments,
expected_uninstall_arguments.GetCommandLineString());
}
// This test checks the expect success / failure of DLL registration when
// a gaia user already exists
// Parameters:
// int: Number of gaia users to create before trying to register the DLL.
// bool: Whether the final user creation is expected to succeed. For
// kMaxAttempts = 10, 0 to 8 gaia users can be created and still have the
// test succeed. If a 9th user is create the test will fail.
class GcpGaiaUserCreationTest
: public GcpSetupTest,
public testing::WithParamInterface<std::tuple<int, bool>> {};
TEST_P(GcpGaiaUserCreationTest, ExistingGaiaUserTest) {
CComBSTR sid;
DWORD error;
EXPECT_EQ(S_OK, fake_os_user_manager()->AddUser(
kDefaultGaiaAccountName, L"password", L"fullname",
L"comment", true, &sid, &error));
int last_user_index = std::get<0>(GetParam());
for (int i = 0; i < last_user_index; ++i) {
std::wstring existing_gaia_username = kDefaultGaiaAccountName;
existing_gaia_username +=
base::NumberToWString(i + kInitialDuplicateUsernameIndex);
EXPECT_EQ(S_OK, fake_os_user_manager()->AddUser(
existing_gaia_username.c_str(), L"password",
L"fullname", L"comment", true, &sid, &error));
}
logging::ResetEventSourceForTesting();
bool should_succeed = std::get<1>(GetParam());
ASSERT_EQ(should_succeed ? S_OK : E_UNEXPECTED,
DoInstall(module_path(), product_version(), fakes_for_testing()));
logging::ResetEventSourceForTesting();
if (should_succeed) {
EXPECT_FALSE(fake_scoped_lsa_policy_factory()
->private_data()[kLsaKeyGaiaPassword]
.empty());
std::wstring expected_gaia_username = kDefaultGaiaAccountName;
expected_gaia_username +=
base::NumberToWString(last_user_index + kInitialDuplicateUsernameIndex);
EXPECT_FALSE(fake_os_user_manager()
->GetUserInfo(expected_gaia_username.c_str())
.sid.empty());
EXPECT_EQ(
expected_gaia_username,
fake_scoped_lsa_policy_factory()->private_data()[kLsaKeyGaiaUsername]);
} else {
EXPECT_TRUE(fake_scoped_lsa_policy_factory()
->private_data()[kLsaKeyGaiaPassword]
.empty());
EXPECT_TRUE(fake_scoped_lsa_policy_factory()
->private_data()[kLsaKeyGaiaUsername]
.empty());
}
}
// For a max retry of 10, it is possible to create gaia users 'gaia',
// 'gaia0' ... 'gaia8' before failing. At 'gaia9' the test should fail.
INSTANTIATE_TEST_SUITE_P(
AvailableGaiaUserName,
GcpGaiaUserCreationTest,
::testing::Combine(::testing::Range(0, kMaxUsernameAttempts - 2),
::testing::Values(true)));
INSTANTIATE_TEST_SUITE_P(
UnavailableGaiaUserName,
GcpGaiaUserCreationTest,
::testing::Values(std::make_tuple<int, bool>(kMaxUsernameAttempts - 1,
false)));
} // namespace credential_provider