blob: f26f199a9418c1cbc8aa8636e81fb45190773a6e [file] [log] [blame]
// Copyright 2018 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/test/gcp_fakes.h"
#include <windows.h>
#include <lm.h>
#include <sddl.h>
#include <atlcomcli.h>
#include <atlconv.h>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/win/scoped_handle.h"
#include "base/win/scoped_process_information.h"
#include "chrome/credential_provider/common/gcp_strings.h"
#include "chrome/credential_provider/gaiacp/logging.h"
#include "chrome/credential_provider/gaiacp/mdm_utils.h"
#include "chrome/credential_provider/gaiacp/reg_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace credential_provider {
namespace {
HRESULT CreateArbitrarySid(DWORD subauth0, PSID* sid) {
SID_IDENTIFIER_AUTHORITY Authority = {SECURITY_NON_UNIQUE_AUTHORITY};
if (!::AllocateAndInitializeSid(&Authority, 1, subauth0, 0, 0, 0, 0, 0, 0, 0,
sid)) {
return (HRESULT_FROM_WIN32(::GetLastError()));
}
return S_OK;
}
} // namespace
///////////////////////////////////////////////////////////////////////////////
void InitializeRegistryOverrideForTesting(
registry_util::RegistryOverrideManager* registry_override) {
ASSERT_NO_FATAL_FAILURE(
registry_override->OverrideRegistry(HKEY_LOCAL_MACHINE));
base::win::RegKey key;
ASSERT_EQ(ERROR_SUCCESS,
key.Create(HKEY_LOCAL_MACHINE, kGcpRootKeyName, KEY_WRITE));
ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(kRegMdmUrl, L""));
}
///////////////////////////////////////////////////////////////////////////////
FakeOSProcessManager::FakeOSProcessManager()
: original_manager_(*GetInstanceStorage()) {
*GetInstanceStorage() = this;
}
FakeOSProcessManager::~FakeOSProcessManager() {
*GetInstanceStorage() = original_manager_;
}
HRESULT FakeOSProcessManager::GetTokenLogonSID(
const base::win::ScopedHandle& token,
PSID* sid) {
// Make sure the token is valid, but otherwise ignore it.
if (!token.IsValid())
return E_INVALIDARG;
return CreateArbitrarySid(++next_rid_, sid);
}
HRESULT FakeOSProcessManager::SetupPermissionsForLogonSid(PSID sid) {
// Ignore.
return S_OK;
}
HRESULT FakeOSProcessManager::CreateProcessWithToken(
const base::win::ScopedHandle& logon_token,
const base::CommandLine& command_line,
_STARTUPINFOW* startupinfo,
base::win::ScopedProcessInformation* procinfo) {
// Ignore the logon token and create a process as the current user.
// If the startupinfo includes a desktop name, make sure to ignore. In tests
// the desktop has not been configured to allow a newly created process to
// to access it.
_STARTUPINFOW local_startupinfo = *startupinfo;
local_startupinfo.lpDesktop = nullptr;
PROCESS_INFORMATION new_procinfo = {};
// Pass a copy of the command line string to CreateProcessW() because this
// function could change the string.
std::unique_ptr<wchar_t, void (*)(void*)> cmdline(
_wcsdup(command_line.GetCommandLineString().c_str()), std::free);
if (!::CreateProcessW(command_line.GetProgram().value().c_str(),
cmdline.get(), nullptr, nullptr, TRUE, CREATE_SUSPENDED,
nullptr, nullptr, &local_startupinfo, &new_procinfo)) {
HRESULT hr = HRESULT_FROM_WIN32(::GetLastError());
return hr;
}
procinfo->Set(new_procinfo);
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
FakeOSUserManager::FakeOSUserManager()
: original_manager_(*GetInstanceStorage()) {
*GetInstanceStorage() = this;
}
FakeOSUserManager::~FakeOSUserManager() {
*GetInstanceStorage() = original_manager_;
}
HRESULT FakeOSUserManager::GenerateRandomPassword(wchar_t* password,
int length) {
if (length < kMinPasswordLength)
return E_INVALIDARG;
// Make sure to generate a different password each time. Actually randomness
// is not important for tests.
static int nonce = 0;
EXPECT_NE(-1, swprintf_s(password, length, L"bad-password-%d", ++nonce));
return S_OK;
}
HRESULT FakeOSUserManager::AddUser(const wchar_t* username,
const wchar_t* password,
const wchar_t* fullname,
const wchar_t* comment,
bool add_to_users_group,
BSTR* sid,
DWORD* error) {
USES_CONVERSION;
DCHECK(sid);
if (error)
*error = 0;
bool user_found = username_to_info_.count(username) > 0;
if (user_found) {
*sid = ::SysAllocString(W2COLE(username_to_info_[username].sid.c_str()));
return HRESULT_FROM_WIN32(NERR_UserExists);
}
PSID psid = nullptr;
HRESULT hr = CreateNewSID(&psid);
if (FAILED(hr))
return hr;
wchar_t* sidstr = nullptr;
bool ok = ::ConvertSidToStringSid(psid, &sidstr);
::FreeSid(psid);
if (!ok) {
*sid = nullptr;
return HRESULT_FROM_WIN32(NERR_ProgNeedsExtraMem);
}
*sid = ::SysAllocString(W2COLE(sidstr));
username_to_info_.emplace(
username, UserInfo(OSUserManager::GetLocalDomain().c_str(), password,
fullname, comment, sidstr));
::LocalFree(sidstr);
return S_OK;
}
HRESULT FakeOSUserManager::ChangeUserPassword(const wchar_t* domain,
const wchar_t* username,
const wchar_t* old_password,
const wchar_t* new_password) {
DCHECK(domain);
DCHECK(username);
DCHECK(old_password);
DCHECK(new_password);
if (username_to_info_.count(username) > 0) {
if (username_to_info_[username].password != old_password)
return HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD);
username_to_info_[username].password = new_password;
return S_OK;
}
return HRESULT_FROM_WIN32(NERR_UserNotFound);
}
HRESULT FakeOSUserManager::SetUserPassword(const wchar_t* domain,
const wchar_t* username,
const wchar_t* new_password) {
DCHECK(domain);
DCHECK(username);
DCHECK(new_password);
if (username_to_info_.count(username) > 0) {
username_to_info_[username].password = new_password;
return S_OK;
}
return HRESULT_FROM_WIN32(NERR_UserNotFound);
}
HRESULT FakeOSUserManager::IsWindowsPasswordValid(const wchar_t* domain,
const wchar_t* username,
const wchar_t* password) {
DCHECK(domain);
DCHECK(username);
DCHECK(password);
if (username_to_info_.count(username) > 0) {
const UserInfo& info = username_to_info_[username];
if (info.domain != domain)
return HRESULT_FROM_WIN32(NERR_UserNotFound);
return info.password == password ? S_OK : S_FALSE;
}
return HRESULT_FROM_WIN32(NERR_UserNotFound);
}
HRESULT FakeOSUserManager::CreateLogonToken(const wchar_t* domain,
const wchar_t* username,
const wchar_t* password,
bool /*interactive*/,
base::win::ScopedHandle* token) {
DCHECK(domain);
DCHECK(username);
DCHECK(password);
if (username_to_info_.count(username) == 0) {
return HRESULT_FROM_WIN32(NERR_BadUsername);
} else if (username_to_info_[username].password != password) {
return HRESULT_FROM_WIN32(NERR_UserExists);
}
const UserInfo& info = username_to_info_[username];
if (info.domain != domain)
return HRESULT_FROM_WIN32(NERR_BadUsername);
// Create a token with a dummy handle value.
base::FilePath path;
if (!base::CreateTemporaryFile(&path))
return HRESULT_FROM_WIN32(::GetLastError());
token->Set(CreateFile(path.value().c_str(), GENERIC_READ | GENERIC_WRITE, 0,
nullptr, CREATE_ALWAYS,
FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE,
nullptr));
return token->IsValid() ? S_OK : HRESULT_FROM_WIN32(::GetLastError());
}
HRESULT FakeOSUserManager::GetUserSID(const wchar_t* domain,
const wchar_t* username,
PSID* sid) {
DCHECK(domain);
DCHECK(username);
DCHECK(sid);
if (username_to_info_.count(username) > 0) {
const UserInfo& info = username_to_info_[username];
if (info.domain == domain) {
if (!::ConvertStringSidToSid(info.sid.c_str(), sid))
return HRESULT_FROM_WIN32(NERR_ProgNeedsExtraMem);
return S_OK;
}
}
return HRESULT_FROM_WIN32(NERR_UserNotFound);
}
HRESULT FakeOSUserManager::FindUserBySID(const wchar_t* sid,
wchar_t* username,
DWORD username_size,
wchar_t* domain,
DWORD domain_size) {
for (auto& kv : username_to_info_) {
if (kv.second.sid == sid) {
if (username)
wcscpy_s(username, username_size, kv.first.c_str());
if (domain)
wcscpy_s(domain, domain_size, kv.second.domain.c_str());
return S_OK;
}
}
return HRESULT_FROM_WIN32(ERROR_NONE_MAPPED);
}
HRESULT FakeOSUserManager::RemoveUser(const wchar_t* username,
const wchar_t* password) {
username_to_info_.erase(username);
return S_OK;
}
HRESULT FakeOSUserManager::GetUserFullname(const wchar_t* domain,
const wchar_t* username,
base::string16* fullname) {
DCHECK(domain);
DCHECK(username);
DCHECK(fullname);
if (username_to_info_.count(username) > 0) {
const UserInfo& info = username_to_info_[username];
if (info.domain == domain) {
*fullname = info.fullname;
return S_OK;
}
}
return HRESULT_FROM_WIN32(NERR_UserNotFound);
}
HRESULT FakeOSUserManager::ModifyUserAccessWithLogonHours(
const wchar_t* domain,
const wchar_t* username,
bool allow) {
return S_OK;
}
FakeOSUserManager::UserInfo::UserInfo(const wchar_t* domain,
const wchar_t* password,
const wchar_t* fullname,
const wchar_t* comment,
const wchar_t* sid)
: domain(domain),
password(password),
fullname(fullname),
comment(comment),
sid(sid) {}
FakeOSUserManager::UserInfo::UserInfo() {}
FakeOSUserManager::UserInfo::UserInfo(const UserInfo& other) = default;
FakeOSUserManager::UserInfo::~UserInfo() {}
bool FakeOSUserManager::UserInfo::operator==(const UserInfo& other) const {
return domain == other.domain && password == other.password &&
fullname == other.fullname && comment == other.comment &&
sid == other.sid;
}
const FakeOSUserManager::UserInfo FakeOSUserManager::GetUserInfo(
const wchar_t* username) {
return (username_to_info_.count(username) > 0) ? username_to_info_[username]
: UserInfo();
}
HRESULT FakeOSUserManager::CreateNewSID(PSID* sid) {
return CreateArbitrarySid(++next_rid_, sid);
}
HRESULT FakeOSUserManager::CreateTestOSUser(const base::string16& username,
const base::string16& password,
const base::string16& fullname,
const base::string16& comment,
const base::string16& gaia_id,
const base::string16& email,
BSTR* sid) {
DWORD error;
HRESULT hr = AddUser(username.c_str(), password.c_str(), fullname.c_str(),
comment.c_str(), true, sid, &error);
if (FAILED(hr))
return hr;
if (!gaia_id.empty()) {
hr = SetUserProperty(OLE2CW(*sid), kUserId, gaia_id);
if (FAILED(hr))
return hr;
hr = SetUserProperty(OLE2CW(*sid), kUserTokenHandle, L"token_handle");
if (FAILED(hr))
return hr;
}
if (!email.empty()) {
hr = SetUserProperty(OLE2CW(*sid), kUserEmail, email);
if (FAILED(hr))
return hr;
}
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
FakeScopedLsaPolicyFactory::FakeScopedLsaPolicyFactory()
: original_creator_(*ScopedLsaPolicy::GetCreatorCallbackStorage()) {
*ScopedLsaPolicy::GetCreatorCallbackStorage() = GetCreatorCallback();
}
FakeScopedLsaPolicyFactory::~FakeScopedLsaPolicyFactory() {
*ScopedLsaPolicy::GetCreatorCallbackStorage() = original_creator_;
}
ScopedLsaPolicy::CreatorCallback
FakeScopedLsaPolicyFactory::GetCreatorCallback() {
return base::BindRepeating(&FakeScopedLsaPolicyFactory::Create,
base::Unretained(this));
}
std::unique_ptr<ScopedLsaPolicy> FakeScopedLsaPolicyFactory::Create(
ACCESS_MASK mask) {
return std::unique_ptr<ScopedLsaPolicy>(new FakeScopedLsaPolicy(this));
}
FakeScopedLsaPolicy::FakeScopedLsaPolicy(FakeScopedLsaPolicyFactory* factory)
: ScopedLsaPolicy(STANDARD_RIGHTS_READ), factory_(factory) {
// The base class ctor will fail to initialize because these tests are not
// running elevated. That's OK, everything is faked out anyway.
}
FakeScopedLsaPolicy::~FakeScopedLsaPolicy() {}
HRESULT FakeScopedLsaPolicy::StorePrivateData(const wchar_t* key,
const wchar_t* value) {
private_data()[key] = value;
return S_OK;
}
HRESULT FakeScopedLsaPolicy::RemovePrivateData(const wchar_t* key) {
private_data().erase(key);
return S_OK;
}
HRESULT FakeScopedLsaPolicy::RetrievePrivateData(const wchar_t* key,
wchar_t* value,
size_t length) {
if (private_data().count(key) == 0)
return E_INVALIDARG;
errno_t err = wcscpy_s(value, length, private_data()[key].c_str());
if (err != 0)
return E_FAIL;
return S_OK;
}
HRESULT FakeScopedLsaPolicy::AddAccountRights(PSID sid, const wchar_t* right) {
return S_OK;
}
HRESULT FakeScopedLsaPolicy::RemoveAccount(PSID sid) {
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
FakeScopedUserProfileFactory::FakeScopedUserProfileFactory()
: original_creator_(*ScopedUserProfile::GetCreatorFunctionStorage()) {
*ScopedUserProfile::GetCreatorFunctionStorage() = base::BindRepeating(
&FakeScopedUserProfileFactory::Create, base::Unretained(this));
}
FakeScopedUserProfileFactory::~FakeScopedUserProfileFactory() {
*ScopedUserProfile::GetCreatorFunctionStorage() = original_creator_;
}
std::unique_ptr<ScopedUserProfile> FakeScopedUserProfileFactory::Create(
const base::string16& sid,
const base::string16& domain,
const base::string16& username,
const base::string16& password) {
return std::unique_ptr<ScopedUserProfile>(
new FakeScopedUserProfile(sid, domain, username, password));
}
FakeScopedUserProfile::FakeScopedUserProfile(const base::string16& sid,
const base::string16& domain,
const base::string16& username,
const base::string16& password) {
is_valid_ = OSUserManager::Get()->IsWindowsPasswordValid(
domain.c_str(), username.c_str(), password.c_str()) == S_OK;
}
FakeScopedUserProfile::~FakeScopedUserProfile() {}
HRESULT FakeScopedUserProfile::SaveAccountInfo(const base::Value& properties) {
if (!is_valid_)
return E_INVALIDARG;
base::string16 sid;
base::string16 id;
base::string16 email;
base::string16 token_handle;
HRESULT hr = ExtractAssociationInformation(properties, &sid, &id, &email,
&token_handle);
if (FAILED(hr))
return hr;
hr = RegisterAssociation(sid, id, email, token_handle);
if (FAILED(hr))
return hr;
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
FakeWinHttpUrlFetcherFactory::Response::Response() {}
FakeWinHttpUrlFetcherFactory::Response::Response(const Response& rhs)
: headers(rhs.headers),
response(rhs.response),
send_response_event_handle(rhs.send_response_event_handle) {}
FakeWinHttpUrlFetcherFactory::Response::Response(
const WinHttpUrlFetcher::Headers& new_headers,
const std::string& new_response,
HANDLE new_send_response_event_handle)
: headers(new_headers),
response(new_response),
send_response_event_handle(new_send_response_event_handle) {}
FakeWinHttpUrlFetcherFactory::Response::~Response() = default;
FakeWinHttpUrlFetcherFactory::FakeWinHttpUrlFetcherFactory()
: original_creator_(*WinHttpUrlFetcher::GetCreatorFunctionStorage()) {
*WinHttpUrlFetcher::GetCreatorFunctionStorage() = base::BindRepeating(
&FakeWinHttpUrlFetcherFactory::Create, base::Unretained(this));
}
FakeWinHttpUrlFetcherFactory::~FakeWinHttpUrlFetcherFactory() {
*WinHttpUrlFetcher::GetCreatorFunctionStorage() = original_creator_;
}
void FakeWinHttpUrlFetcherFactory::SetFakeResponse(
const GURL& url,
const WinHttpUrlFetcher::Headers& headers,
const std::string& response,
HANDLE send_response_event_handle /*=INVALID_HANDLE_VALUE*/) {
fake_responses_[url] =
Response(headers, response, send_response_event_handle);
}
std::unique_ptr<WinHttpUrlFetcher> FakeWinHttpUrlFetcherFactory::Create(
const GURL& url) {
if (fake_responses_.count(url) == 0)
return nullptr;
const Response& response = fake_responses_[url];
FakeWinHttpUrlFetcher* fetcher = new FakeWinHttpUrlFetcher(std::move(url));
fetcher->response_headers_ = response.headers;
fetcher->response_ = response.response;
fetcher->send_response_event_handle_ = response.send_response_event_handle;
++requests_created_;
return std::unique_ptr<WinHttpUrlFetcher>(fetcher);
}
FakeWinHttpUrlFetcher::FakeWinHttpUrlFetcher(const GURL& url)
: WinHttpUrlFetcher() {}
FakeWinHttpUrlFetcher::~FakeWinHttpUrlFetcher() {}
bool FakeWinHttpUrlFetcher::IsValid() const {
return true;
}
HRESULT FakeWinHttpUrlFetcher::Fetch(std::vector<char>* response) {
if (send_response_event_handle_ != INVALID_HANDLE_VALUE)
::WaitForSingleObject(send_response_event_handle_, INFINITE);
response->resize(response_.size());
memcpy(response->data(), response_.c_str(), response->size());
return S_OK;
}
HRESULT FakeWinHttpUrlFetcher::Close() {
return S_OK;
}
///////////////////////////////////////////////////////////////////////////////
FakeAssociatedUserValidator::FakeAssociatedUserValidator()
: AssociatedUserValidator(
AssociatedUserValidator::kDefaultTokenHandleValidationTimeout),
original_validator_(*GetInstanceStorage()) {
*GetInstanceStorage() = this;
}
FakeAssociatedUserValidator::FakeAssociatedUserValidator(
base::TimeDelta validation_timeout)
: AssociatedUserValidator(validation_timeout),
original_validator_(*GetInstanceStorage()) {
*GetInstanceStorage() = this;
}
FakeAssociatedUserValidator::~FakeAssociatedUserValidator() {
*GetInstanceStorage() = original_validator_;
}
///////////////////////////////////////////////////////////////////////////////
FakeInternetAvailabilityChecker::FakeInternetAvailabilityChecker(
HasInternetConnectionCheckType has_internet_connection /*=kHicForceYes*/)
: original_checker_(*GetInstanceStorage()),
has_internet_connection_(has_internet_connection) {
*GetInstanceStorage() = this;
}
FakeInternetAvailabilityChecker::~FakeInternetAvailabilityChecker() {
*GetInstanceStorage() = original_checker_;
}
bool FakeInternetAvailabilityChecker::HasInternetConnection() {
return has_internet_connection_ == kHicForceYes;
}
void FakeInternetAvailabilityChecker::SetHasInternetConnection(
HasInternetConnectionCheckType has_internet_connection) {
has_internet_connection_ = has_internet_connection;
}
} // namespace credential_provider