blob: 7f7144f448d927b6ad537a941e022864cb4b806d [file] [log] [blame]
// Copyright 2019 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cryptohome/userdataauth.h"
#include <algorithm>
#include <deque>
#include <limits>
#include <memory>
#include <optional>
#include <set>
#include <vector>
#include <base/check.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/location.h>
#include <base/stl_util.h>
#include <base/test/bind.h>
#include <base/test/test_future.h>
#include <base/test/test_mock_time_task_runner.h>
#include <base/time/time.h>
#include <brillo/cryptohome.h>
#include <brillo/errors/error_codes.h>
#include <brillo/secure_blob.h>
#include <chaps/token_manager_client_mock.h>
#include <cryptohome/proto_bindings/auth_factor.pb.h>
#include <cryptohome/proto_bindings/UserDataAuth.pb.h>
#include <dbus/mock_bus.h>
#include <featured/fake_platform_features.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libhwsec/backend/mock_backend.h>
#include <libhwsec/error/tpm_error.h>
#include <libhwsec/factory/mock_factory.h>
#include <libhwsec/factory/tpm2_simulator_factory_for_test.h>
#include <libhwsec/frontend/cryptohome/mock_frontend.h>
#include <libhwsec/frontend/pinweaver/mock_frontend.h>
#include <libhwsec/frontend/recovery_crypto/mock_frontend.h>
#include <libhwsec/status.h>
#include <libhwsec-foundation/crypto/libscrypt_compat.h>
#include <libhwsec-foundation/crypto/secure_blob_util.h>
#include <libhwsec-foundation/crypto/aes.h>
#include <libhwsec-foundation/crypto/sha.h>
#include <libhwsec-foundation/error/testing_helper.h>
#include <libhwsec-foundation/tpm/tpm_version.h>
#include <metrics/metrics_library_mock.h>
#include "cryptohome/auth_blocks/biometrics_auth_block_service.h"
#include "cryptohome/auth_blocks/mock_auth_block_utility.h"
#include "cryptohome/auth_blocks/mock_biometrics_command_processor.h"
#include "cryptohome/auth_factor/auth_factor_metadata.h"
#include "cryptohome/challenge_credentials/mock_challenge_credentials_helper.h"
#include "cryptohome/cleanup/mock_disk_cleanup.h"
#include "cryptohome/cleanup/mock_low_disk_space_handler.h"
#include "cryptohome/cleanup/mock_user_oldest_activity_timestamp_manager.h"
#include "cryptohome/common/print_UserDataAuth_proto.h"
#include "cryptohome/error/cryptohome_mount_error.h"
#include "cryptohome/fake_features.h"
#include "cryptohome/features.h"
#include "cryptohome/filesystem_layout.h"
#include "cryptohome/flatbuffer_schemas/auth_factor.h"
#include "cryptohome/mock_credential_verifier.h"
#include "cryptohome/mock_cryptohome_keys_manager.h"
#include "cryptohome/mock_fingerprint_manager.h"
#include "cryptohome/mock_firmware_management_parameters.h"
#include "cryptohome/mock_install_attributes.h"
#include "cryptohome/mock_key_challenge_service.h"
#include "cryptohome/mock_key_challenge_service_factory.h"
#include "cryptohome/mock_keyset_management.h"
#include "cryptohome/mock_pkcs11_init.h"
#include "cryptohome/mock_platform.h"
#include "cryptohome/mock_vault_keyset.h"
#include "cryptohome/pinweaver_manager/mock_le_credential_manager.h"
#include "cryptohome/pkcs11/fake_pkcs11_token.h"
#include "cryptohome/pkcs11/mock_pkcs11_token_factory.h"
#include "cryptohome/storage/file_system_keyset.h"
#include "cryptohome/storage/homedirs.h"
#include "cryptohome/storage/mock_homedirs.h"
#include "cryptohome/storage/mock_mount.h"
#include "cryptohome/storage/mock_mount_factory.h"
#include "cryptohome/user_secret_stash/storage.h"
#include "cryptohome/user_secret_stash/user_secret_stash.h"
#include "cryptohome/user_session/mock_user_session.h"
#include "cryptohome/user_session/mock_user_session_factory.h"
#include "cryptohome/username.h"
using base::FilePath;
using base::test::TestFuture;
using brillo::SecureBlob;
using brillo::cryptohome::home::GetGuestUsername;
using brillo::cryptohome::home::SanitizeUserName;
using cryptohome::error::CryptohomeCryptoError;
using cryptohome::error::CryptohomeError;
using cryptohome::error::CryptohomeMountError;
using cryptohome::error::CryptohomeTPMError;
using cryptohome::error::ErrorActionSet;
using cryptohome::error::PossibleAction;
using cryptohome::error::PrimaryAction;
using ::hwsec::TPMError;
using ::hwsec::TPMErrorBase;
using ::hwsec::TPMRetryAction;
using ::hwsec_foundation::CreateSecureRandomBlob;
using ::hwsec_foundation::kAesGcm256KeySize;
using ::hwsec_foundation::Sha1;
using ::hwsec_foundation::error::testing::IsOk;
using ::hwsec_foundation::error::testing::ReturnError;
using ::hwsec_foundation::error::testing::ReturnOk;
using ::hwsec_foundation::error::testing::ReturnValue;
using ::hwsec_foundation::status::MakeStatus;
using ::hwsec_foundation::status::OkStatus;
using ::hwsec_foundation::status::StatusChain;
using ::testing::_;
using ::testing::AtLeast;
using ::testing::ByMove;
using ::testing::DoAll;
using ::testing::ElementsAre;
using ::testing::ElementsAreArray;
using ::testing::EndsWith;
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::IsEmpty;
using ::testing::NiceMock;
using ::testing::Pointee;
using ::testing::Property;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SaveArgPointee;
using ::testing::SetArgPointee;
using ::testing::UnorderedElementsAre;
namespace cryptohome {
namespace {
// Set to match the 5 minute timer and a 1 minute extension in AuthSession.
constexpr int kAuthSessionExtensionDuration = 60;
constexpr auto kAuthSessionTimeout = base::Minutes(5);
// Fake labels to be in used in this test suite.
constexpr char kFakeLabel[] = "test_label";
ACTION_P(SetEphemeralSettings, ephemeral_settings) {
*arg0 = ephemeral_settings;
return true;
}
} // namespace
// UserDataAuthTestBase is a test fixture that does not call
// UserDataAuth::Initialize() during setup. Therefore, it's suited to tests that
// can be conducted without calling UserDataAuth::Initialize(), or for tests
// that wants some flexibility before calling UserDataAuth::Initialize(), note
// that in this case the test have to call UserDataAuth::Initialize().
// Note: We shouldn't use this test fixture directly.
class UserDataAuthTestBase : public ::testing::Test {
public:
UserDataAuthTestBase() = default;
UserDataAuthTestBase(const UserDataAuthTestBase&) = delete;
UserDataAuthTestBase& operator=(const UserDataAuthTestBase&) = delete;
~UserDataAuthTestBase() override = default;
void SetUp() override {
// Note: If anything is modified/added here, we might need to adjust
// UserDataAuthApiTest::SetUp() as well.
SetupDefaultUserDataAuth();
SetupHwsec();
}
void SetupHwsec() {
userdataauth_->set_auth_block_utility(&auth_block_utility_);
userdataauth_->set_keyset_management(&keyset_management_);
userdataauth_->set_crypto(&crypto_);
userdataauth_->set_hwsec_factory(&hwsec_factory_);
userdataauth_->set_hwsec(&hwsec_);
userdataauth_->set_cryptohome_keys_manager(&cryptohome_keys_manager_);
userdataauth_->set_challenge_credentials_helper(
&challenge_credentials_helper_);
userdataauth_->set_user_session_factory(&user_session_factory_);
}
void SetupDefaultUserDataAuth() {
SET_DEFAULT_TPM_FOR_TESTING;
attrs_ = std::make_unique<NiceMock<MockInstallAttributes>>();
dbus::Bus::Options options;
options.bus_type = dbus::Bus::SYSTEM;
mount_bus_ = base::MakeRefCounted<NiceMock<dbus::MockBus>>(options);
ON_CALL(hwsec_, IsEnabled()).WillByDefault(ReturnValue(true));
ON_CALL(hwsec_, IsReady()).WillByDefault(ReturnValue(true));
ON_CALL(hwsec_, IsPinWeaverEnabled()).WillByDefault(ReturnValue(false));
ON_CALL(hwsec_, IsSealingSupported()).WillByDefault(ReturnValue(true));
ON_CALL(pinweaver_, IsEnabled()).WillByDefault(ReturnValue(true));
ON_CALL(pinweaver_, GetVersion()).WillByDefault(ReturnValue(2));
ON_CALL(pinweaver_, BlockGeneratePk()).WillByDefault(ReturnOk<TPMError>());
if (!userdataauth_) {
// Note that this branch is usually taken as |userdataauth_| is usually
// NULL. The reason for this branch is because some derived-class of this
// class (such as UserDataAuthTestThreaded) need to have the constructor
// of UserDataAuth run on a specific thread, and therefore will construct
// |userdataauth_| before calling UserDataAuthTestBase::SetUp().
userdataauth_ = std::make_unique<UserDataAuth>();
}
userdataauth_->set_user_activity_timestamp_manager(
&user_activity_timestamp_manager_);
userdataauth_->set_install_attrs(attrs_.get());
userdataauth_->set_homedirs(&homedirs_);
userdataauth_->set_pinweaver(&pinweaver_);
userdataauth_->set_recovery_crypto(&recovery_crypto_);
userdataauth_->set_platform(&platform_);
userdataauth_->set_chaps_client(&chaps_client_);
userdataauth_->set_firmware_management_parameters(&fwmp_);
userdataauth_->set_fingerprint_manager(&fingerprint_manager_);
userdataauth_->set_pkcs11_init(&pkcs11_init_);
userdataauth_->set_pkcs11_token_factory(&pkcs11_token_factory_);
userdataauth_->set_key_challenge_service_factory(
&key_challenge_service_factory_);
userdataauth_->set_low_disk_space_handler(&low_disk_space_handler_);
{
auto mock_processor =
std::make_unique<NiceMock<MockBiometricsCommandProcessor>>();
bio_processor_ = mock_processor.get();
bio_service_ = std::make_unique<BiometricsAuthBlockService>(
std::move(mock_processor),
/*enroll_signal_sender=*/base::DoNothing(),
/*auth_signal_sender=*/base::DoNothing());
}
userdataauth_->set_biometrics_service(bio_service_.get());
userdataauth_->set_features(&features_.object);
// Empty token list by default. The effect is that there are no
// attempts to unload tokens unless a test explicitly sets up the token
// list.
ON_CALL(chaps_client_, GetTokenList(_, _)).WillByDefault(Return(true));
// Skip CleanUpStaleMounts by default.
ON_CALL(platform_, GetMountsBySourcePrefix(_, _))
.WillByDefault(Return(false));
// Low Disk space handler initialization will do nothing.
ON_CALL(low_disk_space_handler_, Init(_)).WillByDefault(Return(true));
ON_CALL(low_disk_space_handler_, disk_cleanup())
.WillByDefault(Return(&disk_cleanup_));
// Make sure FreeDiskSpaceDuringLogin is not called unexpectedly.
EXPECT_CALL(disk_cleanup_, FreeDiskSpaceDuringLogin(_)).Times(0);
}
// Create a new session and store an unowned pointer to it in |session_|.
std::unique_ptr<NiceMock<MockUserSession>> CreateSessionAndRememberPtr() {
auto owned_session = std::make_unique<NiceMock<MockUserSession>>();
session_ = owned_session.get();
return owned_session;
}
// This is a utility function for tests to setup a mount for a particular
// user. After calling this function, |session_| is available for use.
void SetupMount(const std::string& username) {
EXPECT_TRUE(userdataauth_->AddUserSessionForTest(
Username(username), CreateSessionAndRememberPtr()));
}
// This is a helper function that compute the obfuscated username with the
// fake salt.
ObfuscatedUsername GetObfuscatedUsername(const Username& username) {
return SanitizeUserName(username);
}
// Helper function for creating a brillo::Error
static brillo::ErrorPtr CreateDefaultError(const base::Location& from_here) {
brillo::ErrorPtr error;
brillo::Error::AddTo(&error, from_here, brillo::errors::dbus::kDomain,
DBUS_ERROR_FAILED, "Here's a fake error");
return error;
}
protected:
// Mock KeysetManagent object, will be passed to UserDataAuth for its internal
// use.
NiceMock<MockKeysetManagement> keyset_management_;
// Mock AuthBlockUtility object, will be passed to UserDataAuth for its
// internal use.
NiceMock<MockAuthBlockUtility> auth_block_utility_;
// Mock UserOldestActivityTimestampManager, will be passed to UserDataAuth for
// its internal use.
NiceMock<MockUserOldestActivityTimestampManager>
user_activity_timestamp_manager_;
// Mock HomeDirs object, will be passed to UserDataAuth for its internal use.
NiceMock<MockHomeDirs> homedirs_;
// Mock DiskCleanup object, will be passed to UserDataAuth for its internal
// use. Only FreeDiskSpaceDuringLogin should be called and it should not
// be called more than necessary.
NiceMock<MockDiskCleanup> disk_cleanup_;
// Mock InstallAttributes object, will be passed to UserDataAuth for its
// internal use.
std::unique_ptr<NiceMock<MockInstallAttributes>> attrs_;
// Mock Platform object, will be passed to UserDataAuth for its internal use.
NiceMock<MockPlatform> platform_;
// Mock HWSec factory object, will be passed to UserDataAuth for its internal
// use.
NiceMock<hwsec::MockFactory> hwsec_factory_;
// Mock HWSec object, will be passed to UserDataAuth for its internal use.
NiceMock<hwsec::MockCryptohomeFrontend> hwsec_;
// Mock pinweaver object, will be passed to UserDataAuth for its internal use.
NiceMock<hwsec::MockPinWeaverFrontend> pinweaver_;
// Mock recovery crypto object, will be passed to UserDataAuth for its
// internal use.
NiceMock<hwsec::MockRecoveryCryptoFrontend> recovery_crypto_;
// Mock Cryptohome Key Loader object, will be passed to UserDataAuth for its
// internal use.
NiceMock<MockCryptohomeKeysManager> cryptohome_keys_manager_;
// Fake Crypto object, will be passed to UserDataAuth for its internal use.
Crypto crypto_{&hwsec_, &pinweaver_, &cryptohome_keys_manager_,
&recovery_crypto_};
// Mock chaps token manager client, will be passed to UserDataAuth for its
// internal use.
NiceMock<chaps::TokenManagerClientMock> chaps_client_;
// Mock PKCS#11 init object, will be passed to UserDataAuth for its internal
// use.
NiceMock<MockPkcs11Init> pkcs11_init_;
// Mock Pcks11TokenFactory, will be passed to UserDataAuth for its internal
// use.
NiceMock<MockPkcs11TokenFactory> pkcs11_token_factory_;
// Mock Firmware Management Parameters object, will be passed to UserDataAuth
// for its internal use.
NiceMock<MockFirmwareManagementParameters> fwmp_;
// Mock Fingerprint Manager object, will be passed to UserDataAuth for its
// internal use.
NiceMock<MockFingerprintManager> fingerprint_manager_;
// Biometrics service object and the mock biometrics command processor object
// that it is wrapping, the service object will be passed into UserDataAuth.
NiceMock<MockBiometricsCommandProcessor>* bio_processor_;
std::unique_ptr<BiometricsAuthBlockService> bio_service_;
// Mock challenge credential helper utility object, will be passed to
// UserDataAuth for its internal use.
NiceMock<MockChallengeCredentialsHelper> challenge_credentials_helper_;
// Mock factory of key challenge services, will be passed to UserDataAuth for
// its internal use.
NiceMock<MockKeyChallengeServiceFactory> key_challenge_service_factory_;
// Mock User Session Factory object.
NiceMock<MockUserSessionFactory> user_session_factory_;
// Mock Low Disk Space handler object, will be passed to UserDataAuth for its
// internal use.
NiceMock<MockLowDiskSpaceHandler> low_disk_space_handler_;
// Mock DBus object on mount thread, will be passed to UserDataAuth for its
// internal use.
scoped_refptr<NiceMock<dbus::MockBus>> mount_bus_;
// Unowned pointer to the session object.
NiceMock<MockUserSession>* session_ = nullptr;
// Fake PlatformFeatures object, will be passed to Features for its internal
// use.
FakeFeaturesForTesting features_;
// Declare |userdataauth_| last so it gets destroyed before all the mocks.
// This is important because otherwise the background thread may call into
// mocks that have already been destroyed.
std::unique_ptr<UserDataAuth> userdataauth_;
const error::CryptohomeError::ErrorLocationPair kErrorLocationPlaceholder =
error::CryptohomeError::ErrorLocationPair(
static_cast<::cryptohome::error::CryptohomeError::ErrorLocation>(1),
"Testing1");
};
// Test fixture that implements two task runners, which is similar to the task
// environment in UserDataAuth. Developers could fast forward the time in
// UserDataAuth, and prevent the flakiness caused by the real time clock. Note
// that this does not initialize |userdataauth_|. And using WaitableEvent in it
// may hang the test runner.
class UserDataAuthTestTasked : public UserDataAuthTestBase {
public:
UserDataAuthTestTasked() = default;
UserDataAuthTestTasked(const UserDataAuthTestTasked&) = delete;
UserDataAuthTestTasked& operator=(const UserDataAuthTestTasked&) = delete;
~UserDataAuthTestTasked() override = default;
void SetUp() override {
// Note: If anything is modified/added here, we might need to adjust
// UserDataAuthApiTest::SetUp() as well.
// Setup the usual stuff
UserDataAuthTestBase::SetUp();
SetupTasks();
}
void SetupTasks() {
// We do the task runner stuff for this test fixture.
userdataauth_->set_origin_task_runner(origin_task_runner_);
userdataauth_->set_mount_task_runner(mount_task_runner_);
ON_CALL(platform_, GetCurrentTime()).WillByDefault(Invoke([this]() {
// The time between origin and mount task runner may have a skew when fast
// forwarding the time. But current running task runner time must be the
// biggest one.
return std::max(origin_task_runner_->Now(), mount_task_runner_->Now());
}));
}
void CreatePkcs11TokenInSession(NiceMock<MockUserSession>* session) {
auto token = std::make_unique<FakePkcs11Token>();
ON_CALL(*session, GetPkcs11Token()).WillByDefault(Return(token.get()));
tokens_.insert(std::move(token));
}
void InitializePkcs11TokenInSession(NiceMock<MockUserSession>* session) {
// PKCS#11 will initialization works only when it's mounted.
ON_CALL(*session, IsActive()).WillByDefault(Return(true));
userdataauth_->InitializePkcs11(session);
}
void TearDown() override {
RunUntilIdle();
// Destruct the |userdataauth_| object.
userdataauth_.reset();
}
// Initialize |userdataauth_| in |origin_task_runner_|
void InitializeUserDataAuth() {
ASSERT_TRUE(userdataauth_->Initialize(mount_bus_));
// Let all initialization tasks complete.
RunUntilIdle();
}
// Fast-forwards virtual time by |delta|
void FastForwardBy(base::TimeDelta delta) {
// Keep running the loop until there is no virtual time remain.
while (!delta.is_zero()) {
const base::TimeDelta& origin_delay =
origin_task_runner_->NextPendingTaskDelay();
const base::TimeDelta& mount_delay =
mount_task_runner_->NextPendingTaskDelay();
// Find the earliest task/deadline to forward.
const base::TimeDelta& delay =
std::min(delta, std::min(origin_delay, mount_delay));
// Forward and run the origin task runner
origin_task_runner_->FastForwardBy(delay);
// Forward and run the mount task runner
mount_task_runner_->FastForwardBy(delay);
// Decrease the virtual time.
delta -= delay;
}
// Make sure there is no zero delay tasks remain.
RunUntilIdle();
}
// Run the all of the task runners until they don't find any zero delay tasks
// in their queues.
void RunUntilIdle() {
while (origin_task_runner_->NextPendingTaskDelay().is_zero() ||
mount_task_runner_->NextPendingTaskDelay().is_zero()) {
origin_task_runner_->RunUntilIdle();
mount_task_runner_->RunUntilIdle();
}
}
protected:
// Holder for tokens to preserve life time.
std::set<std::unique_ptr<FakePkcs11Token>> tokens_;
// MockTimeTaskRunner for origin and mount thread.
scoped_refptr<base::TestMockTimeTaskRunner> origin_task_runner_{
new base::TestMockTimeTaskRunner(
base::TestMockTimeTaskRunner::Type::kBoundToThread)};
scoped_refptr<base::TestMockTimeTaskRunner> mount_task_runner_{
new base::TestMockTimeTaskRunner()};
};
// Using UserDataAuthTestTasked for not initialized tests.
using UserDataAuthTestNotInitialized = UserDataAuthTestTasked;
// Variant of UserDataAuthTestNotInitialized for DeathTest. We should be careful
// in not creating threads in this class.
using UserDataAuthTestNotInitializedDeathTest = UserDataAuthTestNotInitialized;
// Standard, fully initialized UserDataAuth test fixture
class UserDataAuthTest : public UserDataAuthTestNotInitialized {
public:
UserDataAuthTest() = default;
UserDataAuthTest(const UserDataAuthTest&) = delete;
UserDataAuthTest& operator=(const UserDataAuthTest&) = delete;
~UserDataAuthTest() override = default;
void SetUp() override {
// Note: If anything is modified/added here, we might need to adjust
// UserDataAuthApiTest::SetUp() as well.
UserDataAuthTestNotInitialized::SetUp();
InitializeUserDataAuth();
}
};
namespace CryptohomeErrorCodeEquivalenceTest {
// This test is completely static, so it is not wrapped in the TEST_F() block.
static_assert(static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_NOT_SET) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_NOT_SET),
"Enum member CRYPTOHOME_ERROR_NOT_SET differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND),
"Enum member CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_NOT_FOUND) ==
static_cast<int>(
cryptohome::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_NOT_FOUND),
"Enum member CRYPTOHOME_ERROR_AUTHORIZATION_KEY_NOT_FOUND differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED),
"Enum member CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_NOT_IMPLEMENTED) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_NOT_IMPLEMENTED),
"Enum member CRYPTOHOME_ERROR_NOT_IMPLEMENTED differs between "
"user_data_auth:: and cryptohome::");
static_assert(static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_MOUNT_FATAL) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_MOUNT_FATAL),
"Enum member CRYPTOHOME_ERROR_MOUNT_FATAL differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY),
"Enum member CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_TPM_COMM_ERROR) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_TPM_COMM_ERROR),
"Enum member CRYPTOHOME_ERROR_TPM_COMM_ERROR differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_TPM_DEFEND_LOCK),
"Enum member CRYPTOHOME_ERROR_TPM_DEFEND_LOCK differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_TPM_NEEDS_REBOOT) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_TPM_NEEDS_REBOOT),
"Enum member CRYPTOHOME_ERROR_TPM_NEEDS_REBOOT differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_DENIED) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_DENIED),
"Enum member CRYPTOHOME_ERROR_AUTHORIZATION_KEY_DENIED differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_KEY_QUOTA_EXCEEDED) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_KEY_QUOTA_EXCEEDED),
"Enum member CRYPTOHOME_ERROR_KEY_QUOTA_EXCEEDED differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_KEY_LABEL_EXISTS) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_KEY_LABEL_EXISTS),
"Enum member CRYPTOHOME_ERROR_KEY_LABEL_EXISTS differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE),
"Enum member CRYPTOHOME_ERROR_BACKING_STORE_FAILURE differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::CRYPTOHOME_ERROR_UPDATE_SIGNATURE_INVALID) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_UPDATE_SIGNATURE_INVALID),
"Enum member CRYPTOHOME_ERROR_UPDATE_SIGNATURE_INVALID differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_KEY_NOT_FOUND),
"Enum member CRYPTOHOME_ERROR_KEY_NOT_FOUND differs between "
"user_data_auth:: and cryptohome::");
static_assert(static_cast<int>(
user_data_auth::CRYPTOHOME_ERROR_LOCKBOX_SIGNATURE_INVALID) ==
static_cast<int>(
cryptohome::CRYPTOHOME_ERROR_LOCKBOX_SIGNATURE_INVALID),
"Enum member CRYPTOHOME_ERROR_LOCKBOX_SIGNATURE_INVALID differs "
"between user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_LOCKBOX_CANNOT_SIGN) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_LOCKBOX_CANNOT_SIGN),
"Enum member CRYPTOHOME_ERROR_LOCKBOX_CANNOT_SIGN differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::CRYPTOHOME_ERROR_BOOT_ATTRIBUTE_NOT_FOUND) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_BOOT_ATTRIBUTE_NOT_FOUND),
"Enum member CRYPTOHOME_ERROR_BOOT_ATTRIBUTE_NOT_FOUND differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::CRYPTOHOME_ERROR_BOOT_ATTRIBUTES_CANNOT_SIGN) ==
static_cast<int>(
cryptohome::CRYPTOHOME_ERROR_BOOT_ATTRIBUTES_CANNOT_SIGN),
"Enum member CRYPTOHOME_ERROR_BOOT_ATTRIBUTES_CANNOT_SIGN differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_TPM_EK_NOT_AVAILABLE) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_TPM_EK_NOT_AVAILABLE),
"Enum member CRYPTOHOME_ERROR_TPM_EK_NOT_AVAILABLE differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_ATTESTATION_NOT_READY) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_ATTESTATION_NOT_READY),
"Enum member CRYPTOHOME_ERROR_ATTESTATION_NOT_READY differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_CANNOT_CONNECT_TO_CA) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_CANNOT_CONNECT_TO_CA),
"Enum member CRYPTOHOME_ERROR_CANNOT_CONNECT_TO_CA differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_CA_REFUSED_ENROLLMENT) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_CA_REFUSED_ENROLLMENT),
"Enum member CRYPTOHOME_ERROR_CA_REFUSED_ENROLLMENT differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_CA_REFUSED_CERTIFICATE) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_CA_REFUSED_CERTIFICATE),
"Enum member CRYPTOHOME_ERROR_CA_REFUSED_CERTIFICATE differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR) ==
static_cast<int>(
cryptohome::CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR),
"Enum member CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::
CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_INVALID) ==
static_cast<int>(
cryptohome::
CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_INVALID),
"Enum member CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_INVALID "
"differs between user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::
CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_CANNOT_STORE) ==
static_cast<int>(
cryptohome::
CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_CANNOT_STORE),
"Enum member CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_CANNOT_STORE "
"differs between user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::
CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_CANNOT_REMOVE) ==
static_cast<int>(
cryptohome::
CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_CANNOT_REMOVE),
"Enum member CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_CANNOT_REMOVE "
"differs between user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_MOUNT_OLD_ENCRYPTION) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_MOUNT_OLD_ENCRYPTION),
"Enum member CRYPTOHOME_ERROR_MOUNT_OLD_ENCRYPTION differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::CRYPTOHOME_ERROR_MOUNT_PREVIOUS_MIGRATION_INCOMPLETE) ==
static_cast<int>(
cryptohome::CRYPTOHOME_ERROR_MOUNT_PREVIOUS_MIGRATION_INCOMPLETE),
"Enum member CRYPTOHOME_ERROR_MOUNT_PREVIOUS_MIGRATION_INCOMPLETE differs "
"between user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_MIGRATE_KEY_FAILED) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_MIGRATE_KEY_FAILED),
"Enum member CRYPTOHOME_ERROR_MIGRATE_KEY_FAILED differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_REMOVE_FAILED) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_REMOVE_FAILED),
"Enum member CRYPTOHOME_ERROR_REMOVE_FAILED differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_INVALID_ARGUMENT),
"Enum member CRYPTOHOME_ERROR_INVALID_ARGUMENT differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::CRYPTOHOME_ERROR_INSTALL_ATTRIBUTES_GET_FAILED) ==
static_cast<int>(
cryptohome::CRYPTOHOME_ERROR_INSTALL_ATTRIBUTES_GET_FAILED),
"Enum member CRYPTOHOME_ERROR_INSTALL_ATTRIBUTES_GET_FAILED differs "
"between user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::CRYPTOHOME_ERROR_INSTALL_ATTRIBUTES_SET_FAILED) ==
static_cast<int>(
cryptohome::CRYPTOHOME_ERROR_INSTALL_ATTRIBUTES_SET_FAILED),
"Enum member CRYPTOHOME_ERROR_INSTALL_ATTRIBUTES_SET_FAILED differs "
"between user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::CRYPTOHOME_ERROR_INSTALL_ATTRIBUTES_FINALIZE_FAILED) ==
static_cast<int>(
cryptohome::CRYPTOHOME_ERROR_INSTALL_ATTRIBUTES_FINALIZE_FAILED),
"Enum member CRYPTOHOME_ERROR_INSTALL_ATTRIBUTES_FINALIZE_FAILED differs "
"between user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::
CRYPTOHOME_ERROR_UPDATE_USER_ACTIVITY_TIMESTAMP_FAILED) ==
static_cast<int>(
cryptohome::CRYPTOHOME_ERROR_UPDATE_USER_ACTIVITY_TIMESTAMP_FAILED),
"Enum member CRYPTOHOME_ERROR_UPDATE_USER_ACTIVITY_TIMESTAMP_FAILED "
"differs between user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_FAILED_TO_READ_PCR) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_FAILED_TO_READ_PCR),
"Enum member CRYPTOHOME_ERROR_FAILED_TO_READ_PCR differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_PCR_ALREADY_EXTENDED) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_PCR_ALREADY_EXTENDED),
"Enum member CRYPTOHOME_ERROR_PCR_ALREADY_EXTENDED differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_FAILED_TO_EXTEND_PCR) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_FAILED_TO_EXTEND_PCR),
"Enum member CRYPTOHOME_ERROR_FAILED_TO_EXTEND_PCR differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_TPM_UPDATE_REQUIRED) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_TPM_UPDATE_REQUIRED),
"Enum member CRYPTOHOME_ERROR_TPM_UPDATE_REQUIRED differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_VAULT_UNRECOVERABLE) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_VAULT_UNRECOVERABLE),
"Enum member CRYPTOHOME_ERROR_VAULT_UNRECOVERABLE differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_TOKEN_SERIALIZATION_FAILED) ==
static_cast<int>(cryptohome::CRYPTOHOME_TOKEN_SERIALIZATION_FAILED),
"Enum member CRYPTOHOME_TOKEN_SERIALIZATION_FAILED differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN) ==
static_cast<int>(cryptohome::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN),
"Enum member CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ADD_CREDENTIALS_FAILED) ==
static_cast<int>(cryptohome::CRYPTOHOME_ADD_CREDENTIALS_FAILED),
"Enum member CRYPTOHOME_ADD_CREDENTIALS_FAILED differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(
user_data_auth::CRYPTOHOME_ERROR_UNAUTHENTICATED_AUTH_SESSION) ==
static_cast<int>(
cryptohome::CRYPTOHOME_ERROR_UNAUTHENTICATED_AUTH_SESSION),
"Enum member CRYPTOHOME_ERROR_UNAUTHENTICATED_AUTH_SESSION differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_UNKNOWN_LEGACY) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_UNKNOWN_LEGACY),
"Enum member CRYPTOHOME_ERROR_UNKNOWN_LEGACY differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_UNUSABLE_VAULT) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_UNUSABLE_VAULT),
"Enum member CRYPTOHOME_ERROR_UNUSABLE_VAULT differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_REMOVE_CREDENTIALS_FAILED) ==
static_cast<int>(cryptohome::CRYPTOHOME_REMOVE_CREDENTIALS_FAILED),
"Enum member CRYPTOHOME_REMOVE_CREDENTIALS_FAILED differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_UPDATE_CREDENTIALS_FAILED) ==
static_cast<int>(cryptohome::CRYPTOHOME_UPDATE_CREDENTIALS_FAILED),
"Enum member CRYPTOHOME_UPDATE_CREDENTIALS_FAILED differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_BIOMETRICS_BUSY) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_BIOMETRICS_BUSY),
"Enum member CRYPTOHOME_ERROR_BIOMETRICS_BUSY differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_CREDENTIAL_LOCKED) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_CREDENTIAL_LOCKED),
"Enum member CRYPTOHOME_ERROR_CREDENTIAL_LOCKED differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CRYPTOHOME_ERROR_CREDENTIAL_EXPIRED) ==
static_cast<int>(cryptohome::CRYPTOHOME_ERROR_CREDENTIAL_EXPIRED),
"Enum member CRYPTOHOME_ERROR_CREDENTIAL_EXPIRED differs between "
"user_data_auth:: and cryptohome::");
static_assert(
static_cast<int>(user_data_auth::CryptohomeErrorCode_MAX) ==
static_cast<int>(cryptohome::CryptohomeErrorCode_MAX),
"user_data_auth::CryptohomeErrorCode and cryptohome::CryptohomeErrorCode "
"have different element counts");
} // namespace CryptohomeErrorCodeEquivalenceTest
namespace SignatureAlgorithmEquivalenceTest {
// This test is completely static, so it is not wrapped in the TEST_F() block.
static_assert(static_cast<int>(user_data_auth::SmartCardSignatureAlgorithm::
CHALLENGE_RSASSA_PKCS1_V1_5_SHA1) ==
static_cast<int>(cryptohome::ChallengeSignatureAlgorithm::
CHALLENGE_RSASSA_PKCS1_V1_5_SHA1),
"Enum member CHALLENGE_RSASSA_PKCS1_V1_5_SHA1 differs between "
"user_data_auth:: and cryptohome::");
static_assert(static_cast<int>(user_data_auth::SmartCardSignatureAlgorithm::
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256) ==
static_cast<int>(cryptohome::ChallengeSignatureAlgorithm::
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256),
"Enum member CHALLENGE_RSASSA_PKCS1_V1_5_SHA256 differs between "
"user_data_auth:: and cryptohome::");
static_assert(static_cast<int>(user_data_auth::SmartCardSignatureAlgorithm::
CHALLENGE_RSASSA_PKCS1_V1_5_SHA384) ==
static_cast<int>(cryptohome::ChallengeSignatureAlgorithm::
CHALLENGE_RSASSA_PKCS1_V1_5_SHA384),
"Enum member CHALLENGE_RSASSA_PKCS1_V1_5_SHA384 differs between "
"user_data_auth:: and cryptohome::");
static_assert(static_cast<int>(user_data_auth::SmartCardSignatureAlgorithm::
CHALLENGE_RSASSA_PKCS1_V1_5_SHA512) ==
static_cast<int>(cryptohome::ChallengeSignatureAlgorithm::
CHALLENGE_RSASSA_PKCS1_V1_5_SHA512),
"Enum member CHALLENGE_RSASSA_PKCS1_V1_5_SHA512 differs between "
"user_data_auth:: and cryptohome::");
static_assert(
user_data_auth::SmartCardSignatureAlgorithm_MAX == 4,
"user_data_auth::CrytpohomeErrorCode's element count is incorrect");
static_assert(cryptohome::ChallengeSignatureAlgorithm_MAX == 4,
"cryptohome::CrytpohomeErrorCode's element count is incorrect");
} // namespace SignatureAlgorithmEquivalenceTest
TEST_F(UserDataAuthTest, IsMounted) {
// By default there are no mount right after initialization
EXPECT_FALSE(userdataauth_->IsMounted());
EXPECT_FALSE(userdataauth_->IsMounted(Username("foo@gmail.com")));
// Add a mount associated with foo@gmail.com
SetupMount("foo@gmail.com");
// Test the code path that doesn't specify a user, and when there's a mount
// that's unmounted.
EXPECT_CALL(*session_, IsActive()).WillOnce(Return(false));
EXPECT_FALSE(userdataauth_->IsMounted());
// Test to see if is_ephemeral works and test the code path that doesn't
// specify a user.
bool is_ephemeral = true;
EXPECT_CALL(*session_, IsActive()).WillOnce(Return(true));
EXPECT_CALL(*session_, IsEphemeral()).WillOnce(Return(false));
EXPECT_TRUE(userdataauth_->IsMounted(Username(""), &is_ephemeral));
EXPECT_FALSE(is_ephemeral);
// Test to see if is_ephemeral works, and test the code path that specify the
// user.
EXPECT_CALL(*session_, IsActive()).WillOnce(Return(true));
EXPECT_CALL(*session_, IsEphemeral()).WillOnce(Return(true));
EXPECT_TRUE(
userdataauth_->IsMounted(Username("foo@gmail.com"), &is_ephemeral));
EXPECT_TRUE(is_ephemeral);
// Note: IsMounted will not be called in this case.
EXPECT_FALSE(
userdataauth_->IsMounted(Username("bar@gmail.com"), &is_ephemeral));
EXPECT_FALSE(is_ephemeral);
}
TEST_F(UserDataAuthTest, Unmount_AllDespiteFailures) {
const Username kUsername1("foo@gmail.com");
const Username kUsername2("bar@gmail.com");
auto owned_session1 = std::make_unique<NiceMock<MockUserSession>>();
auto* const session1 = owned_session1.get();
EXPECT_TRUE(userdataauth_->AddUserSessionForTest(kUsername1,
std::move(owned_session1)));
auto owned_session2 = std::make_unique<NiceMock<MockUserSession>>();
auto* const session2 = owned_session2.get();
EXPECT_TRUE(userdataauth_->AddUserSessionForTest(kUsername2,
std::move(owned_session2)));
InSequence sequence;
EXPECT_CALL(*session2, IsActive()).WillOnce(Return(true));
EXPECT_CALL(*session2, Unmount()).WillOnce(Return(false));
EXPECT_CALL(*session1, IsActive()).WillOnce(Return(true));
EXPECT_CALL(*session1, Unmount()).WillOnce(Return(true));
EXPECT_FALSE(userdataauth_->RemoveAllMounts());
}
TEST_F(UserDataAuthTest, Unmount_EphemeralNotEnabled) {
// Unmount validity test.
// The tests on whether stale mount are cleaned up is in another set of tests
// called CleanUpStale_*
// Add a mount associated with foo@gmail.com
SetupMount("foo@gmail.com");
// Unmount will be successful.
EXPECT_CALL(*session_, Unmount()).WillOnce(Return(true));
// If anyone asks, this mount is still mounted.
ON_CALL(*session_, IsActive()).WillByDefault(Return(true));
// Test that non-owner's vaults are not touched.
policy::DevicePolicy::EphemeralSettings ephemeral_settings;
ephemeral_settings.global_ephemeral_users_enabled = false;
EXPECT_CALL(homedirs_, GetEphemeralSettings(_))
.WillRepeatedly(SetEphemeralSettings(ephemeral_settings));
EXPECT_CALL(homedirs_, RemoveCryptohomesBasedOnPolicy())
.WillOnce(Return(HomeDirs::CryptohomesRemovedStatus::kNone));
// Unmount should be successful.
EXPECT_EQ(userdataauth_->Unmount().error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
// It should be unmounted in the end.
EXPECT_FALSE(userdataauth_->IsMounted());
// Add another mount associated with bar@gmail.com
SetupMount("bar@gmail.com");
// Unmount will be unsuccessful.
EXPECT_CALL(*session_, Unmount()).WillOnce(Return(false));
// If anyone asks, this mount is still mounted.
ON_CALL(*session_, IsActive()).WillByDefault(Return(true));
// Test that non-owner's vaults are not touched.
ephemeral_settings.global_ephemeral_users_enabled = false;
EXPECT_CALL(homedirs_, GetEphemeralSettings(_))
.WillRepeatedly(SetEphemeralSettings(ephemeral_settings));
EXPECT_CALL(homedirs_, RemoveCryptohomesBasedOnPolicy())
.WillOnce(Return(HomeDirs::CryptohomesRemovedStatus::kNone));
// Unmount should be honest about failures.
EXPECT_NE(userdataauth_->Unmount().error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
// Unmount will remove all mounts even if it failed.
EXPECT_FALSE(userdataauth_->IsMounted());
}
TEST_F(UserDataAuthTest, Unmount_EphemeralEnabled) {
// Unmount validity test.
// The tests on whether stale mount are cleaned up is in another set of tests
// called CleanUpStale_*
// Add a mount associated with foo@gmail.com
SetupMount("foo@gmail.com");
// Unmount will be successful.
EXPECT_CALL(*session_, Unmount()).WillOnce(Return(true));
// If anyone asks, this mount is still mounted.
ON_CALL(*session_, IsActive()).WillByDefault(Return(true));
// Test that non-owner's vaults are cleaned up.
policy::DevicePolicy::EphemeralSettings ephemeral_settings;
ephemeral_settings.global_ephemeral_users_enabled = true;
EXPECT_CALL(homedirs_, GetEphemeralSettings(_))
.WillRepeatedly(SetEphemeralSettings(ephemeral_settings));
EXPECT_CALL(homedirs_, RemoveCryptohomesBasedOnPolicy())
.WillOnce(Return(HomeDirs::CryptohomesRemovedStatus::kSome));
// Unmount should be successful.
EXPECT_EQ(userdataauth_->Unmount().error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
// It should be unmounted in the end.
EXPECT_FALSE(userdataauth_->IsMounted());
// Add another mount associated with bar@gmail.com
SetupMount("bar@gmail.com");
// Unmount will be unsuccessful.
EXPECT_CALL(*session_, Unmount()).WillOnce(Return(false));
// If anyone asks, this mount is still mounted.
ON_CALL(*session_, IsActive()).WillByDefault(Return(true));
// Test that non-owner's vaults are cleaned up anyway.
ephemeral_settings.global_ephemeral_users_enabled = true;
EXPECT_CALL(homedirs_, GetEphemeralSettings(_))
.WillRepeatedly(SetEphemeralSettings(ephemeral_settings));
EXPECT_CALL(homedirs_, RemoveCryptohomesBasedOnPolicy())
.WillOnce(Return(HomeDirs::CryptohomesRemovedStatus::kSome));
// Unmount should be honest about failures.
EXPECT_NE(userdataauth_->Unmount().error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
// Unmount will remove all mounts even if it failed.
EXPECT_FALSE(userdataauth_->IsMounted());
}
TEST_F(UserDataAuthTest, InitializePkcs11Success) {
// This test the most common success case for PKCS#11 initialization.
EXPECT_FALSE(userdataauth_->IsMounted());
// Add a mount associated with foo@gmail.com
SetupMount("foo@gmail.com");
CreatePkcs11TokenInSession(session_);
// At first the token is not ready
EXPECT_FALSE(session_->GetPkcs11Token()->IsReady());
InitializePkcs11TokenInSession(session_);
EXPECT_TRUE(session_->GetPkcs11Token()->IsReady());
}
TEST_F(UserDataAuthTest, InitializePkcs11Unmounted) {
// Add a mount associated with foo@gmail.com
SetupMount("foo@gmail.com");
CreatePkcs11TokenInSession(session_);
// At first the token is not ready
EXPECT_FALSE(session_->GetPkcs11Token()->IsReady());
ON_CALL(*session_, IsActive()).WillByDefault(Return(false));
// The initialization code should at least check, right?
EXPECT_CALL(*session_, IsActive())
.Times(AtLeast(1))
.WillRepeatedly(Return(false));
userdataauth_->InitializePkcs11(session_);
// Still not ready because already unmounted
EXPECT_FALSE(session_->GetPkcs11Token()->IsReady());
}
TEST_F(UserDataAuthTest, Pkcs11IsTpmTokenReady) {
// When there's no mount at all, it should be true.
EXPECT_TRUE(userdataauth_->Pkcs11IsTpmTokenReady());
const Username kUsername1("foo@gmail.com");
const Username kUsername2("bar@gmail.com");
auto owned_session1 = std::make_unique<NiceMock<MockUserSession>>();
auto* const session1 = owned_session1.get();
EXPECT_TRUE(userdataauth_->AddUserSessionForTest(kUsername1,
std::move(owned_session1)));
CreatePkcs11TokenInSession(session1);
auto owned_session2 = std::make_unique<NiceMock<MockUserSession>>();
auto* const session2 = owned_session2.get();
EXPECT_TRUE(userdataauth_->AddUserSessionForTest(kUsername2,
std::move(owned_session2)));
CreatePkcs11TokenInSession(session2);
// Both are uninitialized.
EXPECT_FALSE(userdataauth_->Pkcs11IsTpmTokenReady());
// Only one is initialized.
InitializePkcs11TokenInSession(session2);
EXPECT_FALSE(userdataauth_->Pkcs11IsTpmTokenReady());
// Both is initialized.
InitializePkcs11TokenInSession(session1);
EXPECT_TRUE(userdataauth_->Pkcs11IsTpmTokenReady());
}
TEST_F(UserDataAuthTest, Pkcs11GetTpmTokenInfo) {
user_data_auth::TpmTokenInfo info;
constexpr CK_SLOT_ID kSlot = 42;
const Username kUsername1("foo@gmail.com");
// Check the system token case.
EXPECT_CALL(pkcs11_init_, GetTpmTokenSlotForPath(_, _))
.WillOnce(DoAll(SetArgPointee<1>(kSlot), Return(true)));
info = userdataauth_->Pkcs11GetTpmTokenInfo(Username());
EXPECT_EQ(info.label(), Pkcs11Init::kDefaultSystemLabel);
EXPECT_EQ(info.user_pin(), Pkcs11Init::kDefaultPin);
EXPECT_EQ(info.slot(), kSlot);
// Check the user token case.
EXPECT_CALL(pkcs11_init_, GetTpmTokenSlotForPath(_, _))
.WillOnce(DoAll(SetArgPointee<1>(kSlot), Return(true)));
info = userdataauth_->Pkcs11GetTpmTokenInfo(kUsername1);
// Note that the label will usually be appended with a part of the sanitized
// username. However, the sanitized username cannot be generated during
// testing as we can't mock global functions in libbrillo. Therefore, we'll
// only test that it is prefixed by prefix.
EXPECT_EQ(info.label().substr(0, strlen(Pkcs11Init::kDefaultUserLabelPrefix)),
Pkcs11Init::kDefaultUserLabelPrefix);
EXPECT_EQ(info.user_pin(), Pkcs11Init::kDefaultPin);
EXPECT_EQ(info.slot(), kSlot);
// Verify that if GetTpmTokenSlotForPath fails, we'll get -1 for slot.
EXPECT_CALL(pkcs11_init_, GetTpmTokenSlotForPath(_, _))
.WillOnce(DoAll(SetArgPointee<1>(kSlot), Return(false)));
info = userdataauth_->Pkcs11GetTpmTokenInfo(Username());
EXPECT_EQ(info.slot(), -1);
EXPECT_CALL(pkcs11_init_, GetTpmTokenSlotForPath(_, _))
.WillOnce(DoAll(SetArgPointee<1>(kSlot), Return(false)));
info = userdataauth_->Pkcs11GetTpmTokenInfo(kUsername1);
EXPECT_EQ(info.slot(), -1);
}
TEST_F(UserDataAuthTest, Pkcs11Terminate) {
// Check that it'll not crash when there's no mount
userdataauth_->Pkcs11Terminate();
// Check that we'll indeed get the Mount object to remove the PKCS#11 token.
constexpr char kUsername1[] = "foo@gmail.com";
SetupMount(kUsername1);
CreatePkcs11TokenInSession(session_);
InitializePkcs11TokenInSession(session_);
EXPECT_TRUE(session_->GetPkcs11Token()->IsReady());
userdataauth_->Pkcs11Terminate();
EXPECT_FALSE(session_->GetPkcs11Token()->IsReady());
}
TEST_F(UserDataAuthTest, Pkcs11RestoreTpmTokens) {
// This test the most common success case for PKCS#11 retrieving TPM tokens.
// Add a mount associated with foo@gmail.com
SetupMount("foo@gmail.com");
CreatePkcs11TokenInSession(session_);
// PKCS#11 will initialization works only when it's mounted.
ON_CALL(*session_, IsActive()).WillByDefault(Return(true));
// The initialization code should at least check, right?
EXPECT_CALL(*session_, IsActive())
.Times(AtLeast(1))
.WillRepeatedly(Return(true));
EXPECT_FALSE(session_->GetPkcs11Token()->IsReady());
userdataauth_->Pkcs11RestoreTpmTokens();
EXPECT_TRUE(session_->GetPkcs11Token()->IsReady());
}
TEST_F(UserDataAuthTest, Pkcs11RestoreTpmTokensWaitingOnTPM) {
// This test the most common success case for PKCS#11 retrieving TPM tokens
// when it's waiting TPM ready.
// Add a mount associated with foo@gmail.com
SetupMount("foo@gmail.com");
CreatePkcs11TokenInSession(session_);
// PKCS#11 will initialization works only when it's mounted.
ON_CALL(*session_, IsActive()).WillByDefault(Return(true));
// The initialization code should at least check, right?
EXPECT_CALL(*session_, IsActive())
.Times(AtLeast(1))
.WillRepeatedly(Return(true));
EXPECT_FALSE(session_->GetPkcs11Token()->IsReady());
userdataauth_->Pkcs11RestoreTpmTokens();
EXPECT_TRUE(session_->GetPkcs11Token()->IsReady());
}
TEST_F(UserDataAuthTestNotInitialized, InstallAttributesEnterpriseOwned) {
EXPECT_CALL(*attrs_, Init()).WillOnce(Return(true));
std::string str_true = "true";
std::vector<uint8_t> blob_true(str_true.begin(), str_true.end());
blob_true.push_back(0);
EXPECT_CALL(*attrs_, Get("enterprise.owned", _))
.WillOnce(DoAll(SetArgPointee<1>(blob_true), Return(true)));
InitializeUserDataAuth();
EXPECT_TRUE(userdataauth_->IsEnterpriseOwned());
}
TEST_F(UserDataAuthTestNotInitialized, InstallAttributesNotEnterpriseOwned) {
EXPECT_CALL(*attrs_, Init()).WillOnce(Return(true));
std::string str_true = "false";
std::vector<uint8_t> blob_true(str_true.begin(), str_true.end());
blob_true.push_back(0);
EXPECT_CALL(*attrs_, Get("enterprise.owned", _))
.WillOnce(DoAll(SetArgPointee<1>(blob_true), Return(true)));
InitializeUserDataAuth();
EXPECT_FALSE(userdataauth_->IsEnterpriseOwned());
}
TEST_F(UserDataAuthTestNotInitialized, LowDiskSpaceHandlerInit) {
// Both callbacks need to be set before Init.
EXPECT_CALL(low_disk_space_handler_,
SetUpdateUserActivityTimestampCallback(_));
EXPECT_CALL(low_disk_space_handler_, SetLowDiskSpaceCallback(_));
InitializeUserDataAuth();
}
constexpr char kInstallAttributeName[] = "SomeAttribute";
constexpr uint8_t kInstallAttributeData[] = {0x01, 0x02, 0x00,
0x03, 0xFF, 0xAB};
TEST_F(UserDataAuthTest, InstallAttributesGet) {
// Test for successful case.
EXPECT_CALL(*attrs_, Get(kInstallAttributeName, _))
.WillOnce(
Invoke([](const std::string& name, std::vector<uint8_t>* data_out) {
*data_out = std::vector<uint8_t>(
kInstallAttributeData,
kInstallAttributeData + sizeof(kInstallAttributeData));
return true;
}));
std::vector<uint8_t> data;
EXPECT_TRUE(
userdataauth_->InstallAttributesGet(kInstallAttributeName, &data));
EXPECT_THAT(data, ElementsAreArray(kInstallAttributeData));
// Test for unsuccessful case.
EXPECT_CALL(*attrs_, Get(kInstallAttributeName, _)).WillOnce(Return(false));
EXPECT_FALSE(
userdataauth_->InstallAttributesGet(kInstallAttributeName, &data));
}
TEST_F(UserDataAuthTest, InstallAttributesSet) {
// Test for successful case.
EXPECT_CALL(*attrs_, Set(kInstallAttributeName,
ElementsAreArray(kInstallAttributeData)))
.WillOnce(Return(true));
std::vector<uint8_t> data(
kInstallAttributeData,
kInstallAttributeData + sizeof(kInstallAttributeData));
EXPECT_TRUE(userdataauth_->InstallAttributesSet(kInstallAttributeName, data));
// Test for unsuccessful case.
EXPECT_CALL(*attrs_, Set(kInstallAttributeName,
ElementsAreArray(kInstallAttributeData)))
.WillOnce(Return(false));
EXPECT_FALSE(
userdataauth_->InstallAttributesSet(kInstallAttributeName, data));
}
TEST_F(UserDataAuthTest, InstallAttributesFinalize) {
// Test for successful case.
EXPECT_CALL(*attrs_, Finalize()).WillOnce(Return(true));
EXPECT_TRUE(userdataauth_->InstallAttributesFinalize());
// Test for unsuccessful case.
EXPECT_CALL(*attrs_, Finalize()).WillOnce(Return(false));
EXPECT_FALSE(userdataauth_->InstallAttributesFinalize());
}
TEST_F(UserDataAuthTest, InstallAttributesCount) {
constexpr int kCount = 42; // The Answer!!
EXPECT_CALL(*attrs_, Count()).WillOnce(Return(kCount));
EXPECT_EQ(kCount, userdataauth_->InstallAttributesCount());
}
TEST_F(UserDataAuthTest, InstallAttributesIsSecure) {
// Test for successful case.
EXPECT_CALL(*attrs_, IsSecure()).WillOnce(Return(true));
EXPECT_TRUE(userdataauth_->InstallAttributesIsSecure());
// Test for unsuccessful case.
EXPECT_CALL(*attrs_, IsSecure()).WillOnce(Return(false));
EXPECT_FALSE(userdataauth_->InstallAttributesIsSecure());
}
TEST_F(UserDataAuthTest, InstallAttributesGetStatus) {
std::vector<InstallAttributesInterface::Status> status_list = {
InstallAttributesInterface::Status::kUnknown,
InstallAttributesInterface::Status::kTpmNotOwned,
InstallAttributesInterface::Status::kFirstInstall,
InstallAttributesInterface::Status::kValid,
InstallAttributesInterface::Status::kInvalid};
for (auto& s : status_list) {
EXPECT_CALL(*attrs_, status()).WillOnce(Return(s));
EXPECT_EQ(s, userdataauth_->InstallAttributesGetStatus());
}
}
TEST_F(UserDataAuthTestNotInitialized, InstallAttributesStatusToProtoEnum) {
EXPECT_EQ(user_data_auth::InstallAttributesState::UNKNOWN,
UserDataAuth::InstallAttributesStatusToProtoEnum(
InstallAttributesInterface::Status::kUnknown));
EXPECT_EQ(user_data_auth::InstallAttributesState::TPM_NOT_OWNED,
UserDataAuth::InstallAttributesStatusToProtoEnum(
InstallAttributesInterface::Status::kTpmNotOwned));
EXPECT_EQ(user_data_auth::InstallAttributesState::FIRST_INSTALL,
UserDataAuth::InstallAttributesStatusToProtoEnum(
InstallAttributesInterface::Status::kFirstInstall));
EXPECT_EQ(user_data_auth::InstallAttributesState::VALID,
UserDataAuth::InstallAttributesStatusToProtoEnum(
InstallAttributesInterface::Status::kValid));
EXPECT_EQ(user_data_auth::InstallAttributesState::INVALID,
UserDataAuth::InstallAttributesStatusToProtoEnum(
InstallAttributesInterface::Status::kInvalid));
static_assert(
user_data_auth::InstallAttributesState_MAX == 4,
"Incorrect element count in user_data_auth::InstallAttributesState");
static_assert(
static_cast<int>(InstallAttributesInterface::Status::COUNT) == 5,
"Incorrect element count in InstallAttributesInterface::Status");
}
TEST_F(UserDataAuthTest, LockToSingleUserMountUntilRebootValidity) {
const Username kUsername1("foo@gmail.com");
AccountIdentifier account_id;
account_id.set_account_id(*kUsername1);
const ObfuscatedUsername kUsername1Obfuscated =
GetObfuscatedUsername(kUsername1);
EXPECT_CALL(homedirs_, SetLockedToSingleUser()).WillOnce(Return(true));
EXPECT_CALL(hwsec_, IsCurrentUserSet()).WillOnce(ReturnValue(false));
EXPECT_CALL(hwsec_, SetCurrentUser(*kUsername1Obfuscated))
.WillOnce(ReturnOk<TPMError>());
EXPECT_EQ(userdataauth_->LockToSingleUserMountUntilReboot(account_id),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
}
TEST_F(UserDataAuthTest, LockToSingleUserMountUntilRebootReadPCRFail) {
constexpr char kUsername1[] = "foo@gmail.com";
AccountIdentifier account_id;
account_id.set_account_id(kUsername1);
ON_CALL(homedirs_, SetLockedToSingleUser()).WillByDefault(Return(true));
EXPECT_CALL(hwsec_, IsCurrentUserSet())
.WillOnce(ReturnError<TPMError>("fake", TPMRetryAction::kNoRetry));
EXPECT_EQ(userdataauth_->LockToSingleUserMountUntilReboot(account_id),
user_data_auth::CRYPTOHOME_ERROR_FAILED_TO_READ_PCR);
}
TEST_F(UserDataAuthTest, LockToSingleUserMountUntilRebootAlreadyExtended) {
constexpr char kUsername1[] = "foo@gmail.com";
AccountIdentifier account_id;
account_id.set_account_id(kUsername1);
ON_CALL(homedirs_, SetLockedToSingleUser()).WillByDefault(Return(true));
EXPECT_CALL(hwsec_, IsCurrentUserSet()).WillOnce(ReturnValue(true));
EXPECT_EQ(userdataauth_->LockToSingleUserMountUntilReboot(account_id),
user_data_auth::CRYPTOHOME_ERROR_PCR_ALREADY_EXTENDED);
}
TEST_F(UserDataAuthTest, LockToSingleUserMountUntilRebootExtendFail) {
const Username kUsername1("foo@gmail.com");
AccountIdentifier account_id;
account_id.set_account_id(*kUsername1);
const ObfuscatedUsername kUsername1Obfuscated =
GetObfuscatedUsername(kUsername1);
EXPECT_CALL(homedirs_, SetLockedToSingleUser()).WillOnce(Return(true));
EXPECT_CALL(hwsec_, IsCurrentUserSet()).WillOnce(ReturnValue(false));
EXPECT_CALL(hwsec_, SetCurrentUser(*kUsername1Obfuscated))
.WillOnce(ReturnError<TPMError>("fake", TPMRetryAction::kNoRetry));
EXPECT_EQ(userdataauth_->LockToSingleUserMountUntilReboot(account_id),
user_data_auth::CRYPTOHOME_ERROR_FAILED_TO_EXTEND_PCR);
}
TEST_F(UserDataAuthTest, GetEncryptionInfoEnabledTest) {
EXPECT_CALL(homedirs_, KeylockerForStorageEncryptionEnabled())
.WillRepeatedly(Return(true));
// Verify that a request produces encryption info.
user_data_auth::GetEncryptionInfoRequest request;
user_data_auth::GetEncryptionInfoReply reply =
userdataauth_->GetEncryptionInfo(request);
ASSERT_EQ(reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
ASSERT_TRUE(reply.keylocker_supported());
}
// ================== Firmware Management Parameters tests ==================
TEST_F(UserDataAuthTest, GetFirmwareManagementParametersSuccess) {
user_data_auth::FirmwareManagementParameters fwmp;
EXPECT_CALL(fwmp_, GetFWMP(&fwmp)).WillOnce(Return(true));
EXPECT_EQ(user_data_auth::CRYPTOHOME_ERROR_NOT_SET,
userdataauth_->GetFirmwareManagementParameters(&fwmp));
}
TEST_F(UserDataAuthTest, GetFirmwareManagementParametersError) {
user_data_auth::FirmwareManagementParameters fwmp;
EXPECT_CALL(fwmp_, GetFWMP(&fwmp)).WillOnce(Return(false));
EXPECT_EQ(
user_data_auth::CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_INVALID,
userdataauth_->GetFirmwareManagementParameters(&fwmp));
}
TEST_F(UserDataAuthTest, SetFirmwareManagementParametersSuccess) {
user_data_auth::FirmwareManagementParameters fwmp;
EXPECT_CALL(fwmp_, SetFWMP(_)).WillOnce(Return(true));
EXPECT_EQ(user_data_auth::CRYPTOHOME_ERROR_NOT_SET,
userdataauth_->SetFirmwareManagementParameters(fwmp));
}
TEST_F(UserDataAuthTest, SetFirmwareManagementParametersError) {
user_data_auth::FirmwareManagementParameters fwmp;
EXPECT_CALL(fwmp_, SetFWMP(_)).WillOnce(Return(false));
EXPECT_EQ(user_data_auth::
CRYPTOHOME_ERROR_FIRMWARE_MANAGEMENT_PARAMETERS_CANNOT_STORE,
userdataauth_->SetFirmwareManagementParameters(fwmp));
}
TEST_F(UserDataAuthTest, RemoveFirmwareManagementParametersSuccess) {
EXPECT_CALL(fwmp_, Destroy()).WillOnce(Return(true));
EXPECT_TRUE(userdataauth_->RemoveFirmwareManagementParameters());
}
TEST_F(UserDataAuthTest, RemoveFirmwareManagementParametersError) {
EXPECT_CALL(fwmp_, Destroy()).WillOnce(Return(false));
EXPECT_FALSE(userdataauth_->RemoveFirmwareManagementParameters());
}
TEST_F(UserDataAuthTest, GetSystemSaltSucess) {
EXPECT_EQ(brillo::SecureBlob(*brillo::cryptohome::home::GetSystemSalt()),
userdataauth_->GetSystemSalt());
}
TEST_F(UserDataAuthTestNotInitializedDeathTest, GetSystemSaltUninitialized) {
EXPECT_DEATH(userdataauth_->GetSystemSalt(),
"Cannot call GetSystemSalt before initialization");
}
TEST_F(UserDataAuthTest, HwsecReadyCallbackSuccess) {
base::OnceCallback<void(hwsec::Status)> callback;
// Called by Initialize().
EXPECT_CALL(hwsec_, RegisterOnReadyCallback)
.WillOnce([&](base::OnceCallback<void(hwsec::Status)> cb) {
callback = std::move(cb);
});
InitializeUserDataAuth();
EXPECT_FALSE(callback.is_null());
SetupMount("foo@gmail.com");
// Called by EnsureCryptohomeKeys().
EXPECT_CALL(cryptohome_keys_manager_, HasAnyCryptohomeKey())
.WillOnce(Return(true));
// Called by InitializeInstallAttributes()
EXPECT_CALL(*attrs_, Init()).WillOnce(Return(true));
std::move(callback).Run(hwsec::OkStatus());
}
TEST_F(UserDataAuthTest, HwsecReadyCallbackFail) {
base::OnceCallback<void(hwsec::Status)> callback;
// Called by Initialize().
EXPECT_CALL(hwsec_, RegisterOnReadyCallback)
.WillOnce([&](base::OnceCallback<void(hwsec::Status)> cb) {
callback = std::move(cb);
});
InitializeUserDataAuth();
EXPECT_FALSE(callback.is_null());
SetupMount("foo@gmail.com");
// This function will not be called.
EXPECT_CALL(cryptohome_keys_manager_, HasAnyCryptohomeKey()).Times(0);
EXPECT_CALL(*attrs_, Init()).Times(0);
std::move(callback).Run(
MakeStatus<hwsec::TPMError>("fake", TPMRetryAction::kNoRetry));
}
TEST_F(UserDataAuthTest, UpdateCurrentUserActivityTimestampSuccess) {
constexpr int kTimeshift = 5;
// Test case for single mount
SetupMount("foo@gmail.com");
EXPECT_CALL(*session_, IsActive()).WillOnce(Return(true));
EXPECT_CALL(*session_, IsEphemeral()).WillOnce(Return(false));
EXPECT_CALL(user_activity_timestamp_manager_,
UpdateTimestamp(_, base::Seconds(kTimeshift)))
.WillOnce(Return(true));
EXPECT_TRUE(userdataauth_->UpdateCurrentUserActivityTimestamp(kTimeshift));
// Test case for multiple mounts
NiceMock<MockUserSession>* const prev_session = session_;
SetupMount("bar@gmail.com");
EXPECT_CALL(*session_, IsActive()).WillOnce(Return(true));
EXPECT_CALL(*session_, IsEphemeral()).WillOnce(Return(false));
EXPECT_CALL(*prev_session, IsActive()).WillOnce(Return(true));
EXPECT_CALL(*prev_session, IsEphemeral()).WillOnce(Return(false));
EXPECT_CALL(user_activity_timestamp_manager_,
UpdateTimestamp(_, base::Seconds(kTimeshift)))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_TRUE(userdataauth_->UpdateCurrentUserActivityTimestamp(kTimeshift));
}
TEST_F(UserDataAuthTest, UpdateCurrentUserActivityTimestampFailure) {
constexpr int kTimeshift = 5;
// Test case for single mount
SetupMount("foo@gmail.com");
EXPECT_CALL(*session_, IsActive()).WillOnce(Return(true));
EXPECT_CALL(*session_, IsEphemeral()).WillOnce(Return(false));
EXPECT_CALL(user_activity_timestamp_manager_,
UpdateTimestamp(_, base::Seconds(kTimeshift)))
.WillOnce(Return(false));
EXPECT_FALSE(userdataauth_->UpdateCurrentUserActivityTimestamp(kTimeshift));
}
// ======================= CleanUpStaleMounts tests ==========================
namespace {
struct Mounts {
const FilePath src;
const FilePath dst;
};
const std::vector<Mounts> kShadowMounts = {
{FilePath("/home/.shadow/a"), FilePath("/home/root/0")},
{FilePath("/home/.shadow/a"), FilePath("/home/user/0")},
{FilePath("/home/.shadow/a"), FilePath("/home/chronos/user")},
{FilePath("/home/.shadow/a/Downloads"),
FilePath("/home/chronos/user/MyFiles/Downloads")},
{FilePath("/home/.shadow/a/server/run"),
FilePath("/daemon-store/server/a")},
{FilePath("/home/.shadow/b"), FilePath("/home/root/1")},
{FilePath("/home/.shadow/b"), FilePath("/home/user/1")},
{FilePath("/home/.shadow/b/Downloads"),
FilePath("/home/chronos/u-b/MyFiles/Downloads")},
{FilePath("/home/.shadow/b/Downloads"),
FilePath("/home/user/b/MyFiles/Downloads")},
{FilePath("/home/.shadow/b/server/run"),
FilePath("/daemon-store/server/b")},
};
const std::vector<Mounts> kDmcryptMounts = {
{FilePath("/dev/mapper/dmcrypt-4567-data"), FilePath("/home/root/1")},
{FilePath("/dev/mapper/dmcrypt-4567-data"), FilePath("/home/user/1")},
{FilePath("/dev/mapper/dmcrypt-1234-data"), FilePath("/home/root/0")},
{FilePath("/dev/mapper/dmcrypt-1234-data"), FilePath("/home/user/0")},
{FilePath("/dev/mapper/dmcrypt-1234-data"), FilePath("/home/chronos/user")},
{FilePath("/dev/mapper/dmcrypt-1234-data"),
FilePath("/home/chronos/user/MyFiles/Downloads")},
{FilePath("/dev/mapper/dmcrypt-1234-data"),
FilePath("/daemon-store/server/a")},
{FilePath("/dev/mapper/dmcrypt-1234-data"),
FilePath("/home/chronos/u-b/MyFiles/Downloads")},
{FilePath("/dev/mapper/dmcrypt-1234-data"),
FilePath("/home/user/b/MyFiles/Downloads")},
{FilePath("/dev/mapper/dmcrypt-1234-data"),
FilePath("/daemon-store/server/b")},
{FilePath("/dev/mapper/dmcrypt-1234-data"),
FilePath("/home/chronos/user/Cache")},
{FilePath("/dev/mapper/dmcrypt-1234-data"),
FilePath("/home/chronos/user/GCache")},
{FilePath("/dev/mapper/dmcrypt-1234-data"),
FilePath("/home/chronos/u-1234/Cache")},
{FilePath("/dev/mapper/dmcrypt-1234-data"),
FilePath("/home/chronos/u-1234/GCache")},
{FilePath("/dev/mapper/dmcrypt-1234-data"),
FilePath("/home/user/1234/Cache")},
{FilePath("/dev/mapper/dmcrypt-1234-data"),
FilePath("/home/user/1234/GCache")},
};
// Ephemeral mounts must be at the beginning.
const std::vector<Mounts> kLoopDevMounts = {
{FilePath("/dev/loop7"), FilePath("/run/cryptohome/ephemeral_mount/1")},
{FilePath("/dev/loop7"), FilePath("/home/user/0")},
{FilePath("/dev/loop7"), FilePath("/home/root/0")},
{FilePath("/dev/loop7"), FilePath("/home/chronos/u-1")},
{FilePath("/dev/loop7"), FilePath("/home/chronos/user")},
{FilePath("/dev/loop1"), FilePath("/opt/google/containers")},
{FilePath("/dev/loop2"), FilePath("/home/root/1")},
{FilePath("/dev/loop2"), FilePath("/home/user/1")},
};
// 5 Mounts in the above are from /dev/loop7, which is ephemeral as seen
// in kLoopDevices.
const int kEphemeralMountsCount = 5;
// Constants used by CleanUpStaleMounts tests.
const std::vector<Platform::LoopDevice> kLoopDevices = {
{FilePath("/mnt/stateful_partition/encrypted.block"),
FilePath("/dev/loop0")},
{FilePath("/run/cryptohome/ephemeral_data/1"), FilePath("/dev/loop7")},
};
const std::vector<FilePath> kSparseFiles = {
FilePath("/run/cryptohome/ephemeral_data/2"),
FilePath("/run/cryptohome/ephemeral_data/1"),
};
// Utility functions used by CleanUpStaleMounts tests.
bool StaleShadowMounts(const FilePath& from_prefix,
std::multimap<const FilePath, const FilePath>* mounts) {
int i = 0;
for (const auto& m : kShadowMounts) {
if (m.src.value().find(from_prefix.value()) == 0) {
i++;
if (mounts)
mounts->insert(std::make_pair(m.src, m.dst));
}
}
return i > 0;
}
bool DmcryptDeviceMounts(
const std::string& from_prefix,
std::multimap<const FilePath, const FilePath>* mounts) {
if (!mounts)
return false;
for (const auto& m : kDmcryptMounts)
mounts->insert(std::make_pair(m.src, m.dst));
return true;
}
bool LoopDeviceMounts(std::multimap<const FilePath, const FilePath>* mounts) {
if (!mounts)
return false;
for (const auto& m : kLoopDevMounts)
mounts->insert(std::make_pair(m.src, m.dst));
return true;
}
bool EnumerateSparseFiles(const base::FilePath& path,
bool is_recursive,
std::vector<base::FilePath>* ent_list) {
if (path != FilePath(kEphemeralCryptohomeDir).Append(kSparseFileDir))
return false;
ent_list->insert(ent_list->begin(), kSparseFiles.begin(), kSparseFiles.end());
return true;
}
} // namespace
TEST_F(UserDataAuthTest, CleanUpStale_NoOpenFiles_Dmcrypt) {
// Check that when we have dm-crypt mounts, no active mounts,
// and no open filehandles, all stale mounts are unmounted.
EXPECT_CALL(platform_, GetMountsByDevicePrefix("/dev/mapper/dmcrypt", _))
.WillOnce(Invoke(DmcryptDeviceMounts));
EXPECT_CALL(platform_, ExpireMount(_))
.Times(kDmcryptMounts.size())
.WillRepeatedly(Return(ExpireMountResult::kMarked));
for (int i = 0; i < kDmcryptMounts.size(); ++i) {
EXPECT_CALL(platform_, Unmount(kDmcryptMounts[i].dst, true, _))
.WillRepeatedly(Return(true));
}
EXPECT_FALSE(userdataauth_->CleanUpStaleMounts(false));
}
TEST_F(UserDataAuthTest, CleanUpStale_OpenFiles_Dmcrypt) {
// Check that when we have dm-crypt mounts, files open on dm-crypt cryptohome
// for one user and no open filehandles, all stale mounts for the second user
// are unmounted.
EXPECT_CALL(platform_, GetMountsByDevicePrefix("/dev/mapper/dmcrypt", _))
.WillOnce(Invoke(DmcryptDeviceMounts));
// The number of expired mounts depends on when the first busy mount is
// traversed through. In this case, /home/chronos/user is the 3rd mount in
// the list, so ExpireMount() is called for the first two non-busy mounts for
// user 1234 and then for the non-busy stale mounts for user 4567.
const int kBusyMountIndex = 4;
EXPECT_CALL(platform_, ExpireMount(_))
.Times(kBusyMountIndex)
.WillRepeatedly(Return(ExpireMountResult::kMarked));
EXPECT_CALL(platform_, ExpireMount(kDmcryptMounts[kBusyMountIndex].dst))
.Times(1)
.WillRepeatedly(Return(ExpireMountResult::kBusy));
// Only user 4567's mounts will be unmounted.
for (int i = 0; i < 2; ++i) {
EXPECT_CALL(platform_, Unmount(kDmcryptMounts[i].dst, true, _))
.WillRepeatedly(Return(true));
}
EXPECT_TRUE(userdataauth_->CleanUpStaleMounts(false));
}
TEST_F(UserDataAuthTest, CleanUpStale_OpenFiles_Dmcrypt_Forced) {
// Check that when we have dm-crypt mounts, files open on dm-crypt
// and no open filehandles, all stale mounts are unmounted.
EXPECT_CALL(platform_, GetMountsByDevicePrefix("/dev/mapper/dmcrypt", _))
.WillOnce(Invoke(DmcryptDeviceMounts));
EXPECT_CALL(platform_, ExpireMount(_)).Times(0);
for (int i = 0; i < kDmcryptMounts.size(); ++i) {
EXPECT_CALL(platform_, Unmount(kDmcryptMounts[i].dst, true, _))
.WillRepeatedly(Return(true));
}
EXPECT_FALSE(userdataauth_->CleanUpStaleMounts(true));
}
TEST_F(UserDataAuthTest, CleanUpStale_NoOpenFiles_Ephemeral) {
// Check that when we have ephemeral mounts, no active mounts,
// and no open filehandles, all stale mounts are unmounted, loop device is
// detached and sparse file is deleted.
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _)).WillOnce(Return(false));
EXPECT_CALL(platform_, GetAttachedLoopDevices())
.WillRepeatedly(Return(kLoopDevices));
EXPECT_CALL(platform_, GetLoopDeviceMounts(_))
.WillOnce(Invoke(LoopDeviceMounts));
EXPECT_CALL(
platform_,
EnumerateDirectoryEntries(
FilePath(kEphemeralCryptohomeDir).Append(kSparseFileDir), _, _))
.WillOnce(Invoke(EnumerateSparseFiles));
EXPECT_CALL(platform_, ExpireMount(_))
.Times(kEphemeralMountsCount)
.WillRepeatedly(Return(ExpireMountResult::kMarked));
for (int i = 0; i < kEphemeralMountsCount; ++i) {
EXPECT_CALL(platform_, Unmount(kLoopDevMounts[i].dst, true, _))
.WillRepeatedly(Return(true));
}
EXPECT_CALL(platform_, DetachLoop(FilePath("/dev/loop7")))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(kSparseFiles[0])).WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(kSparseFiles[1])).WillOnce(Return(true));
EXPECT_CALL(platform_, DeletePathRecursively(kLoopDevMounts[0].dst))
.WillOnce(Return(true));
EXPECT_FALSE(userdataauth_->CleanUpStaleMounts(false));
}
TEST_F(UserDataAuthTest, CleanUpStale_OpenLegacy_Ephemeral) {
// Check that when we have ephemeral mounts, no active mounts,
// and some open filehandles to the legacy homedir, everything is kept.
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _)).WillOnce(Return(false));
EXPECT_CALL(platform_, GetAttachedLoopDevices())
.WillRepeatedly(Return(kLoopDevices));
EXPECT_CALL(platform_, GetLoopDeviceMounts(_))
.WillOnce(Invoke(LoopDeviceMounts));
EXPECT_CALL(
platform_,
EnumerateDirectoryEntries(
FilePath(kEphemeralCryptohomeDir).Append(kSparseFileDir), _, _))
.WillOnce(Invoke(EnumerateSparseFiles));
EXPECT_CALL(platform_, ExpireMount(_))
.Times(kEphemeralMountsCount - 1)
.WillRepeatedly(Return(ExpireMountResult::kMarked));
EXPECT_CALL(platform_, ExpireMount(FilePath("/home/chronos/user")))
.Times(1)
.WillRepeatedly(Return(ExpireMountResult::kBusy));
EXPECT_CALL(platform_, GetMountsBySourcePrefix(FilePath("/dev/loop7"), _))
.WillOnce(Return(false));
EXPECT_CALL(platform_, Unmount(_, _, _)).Times(0);
EXPECT_TRUE(userdataauth_->CleanUpStaleMounts(false));
}
TEST_F(UserDataAuthTest, CleanUpStale_OpenLegacy_Ephemeral_Forced) {
// Check that when we have ephemeral mounts, no active mounts,
// and some open filehandles to the legacy homedir, but cleanup is forced,
// all mounts are unmounted, loop device is detached and file is deleted.
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _)).WillOnce(Return(false));
EXPECT_CALL(platform_, GetAttachedLoopDevices())
.WillRepeatedly(Return(kLoopDevices));
EXPECT_CALL(platform_, GetLoopDeviceMounts(_))
.WillOnce(Invoke(LoopDeviceMounts));
EXPECT_CALL(
platform_,
EnumerateDirectoryEntries(
FilePath(kEphemeralCryptohomeDir).Append(kSparseFileDir), _, _))
.WillOnce(Invoke(EnumerateSparseFiles));
EXPECT_CALL(platform_, ExpireMount(_)).Times(0);
for (int i = 0; i < kEphemeralMountsCount; ++i) {
EXPECT_CALL(platform_, Unmount(kLoopDevMounts[i].dst, true, _))
.WillRepeatedly(Return(true));
}
EXPECT_CALL(platform_, DetachLoop(FilePath("/dev/loop7")))
.WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(kSparseFiles[0])).WillOnce(Return(true));
EXPECT_CALL(platform_, DeleteFile(kSparseFiles[1])).WillOnce(Return(true));
EXPECT_CALL(platform_, DeletePathRecursively(kLoopDevMounts[0].dst))
.WillOnce(Return(true));
EXPECT_FALSE(userdataauth_->CleanUpStaleMounts(true));
}
TEST_F(UserDataAuthTest, CleanUpStale_EmptyMap_NoOpenFiles_ShadowOnly) {
// Check that when we have a bunch of stale shadow mounts, no active mounts,
// and no open filehandles, all stale mounts are unmounted.
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _))
.WillOnce(Invoke(StaleShadowMounts));
EXPECT_CALL(platform_, GetAttachedLoopDevices())
.WillRepeatedly(Return(std::vector<Platform::LoopDevice>()));
EXPECT_CALL(platform_, GetLoopDeviceMounts(_)).WillOnce(Return(false));
EXPECT_CALL(
platform_,
EnumerateDirectoryEntries(
FilePath(kEphemeralCryptohomeDir).Append(kSparseFileDir), _, _))
.WillOnce(Return(false));
EXPECT_CALL(platform_, ExpireMount(_))
.Times(kShadowMounts.size())
.WillRepeatedly(Return(ExpireMountResult::kMarked));
EXPECT_CALL(platform_, Unmount(_, true, _))
.Times(kShadowMounts.size())
.WillRepeatedly(Return(true));
EXPECT_FALSE(userdataauth_->CleanUpStaleMounts(false));
}
TEST_F(UserDataAuthTest, CleanUpStale_EmptyMap_NoOpenFiles_ShadowOnly_Forced) {
// Check that when we have a bunch of stale shadow mounts, no active mounts,
// and no open filehandles, all stale mounts are unmounted and we attempt
// to clear the encryption key for fscrypt/ecryptfs mounts.
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _))
.WillOnce(Invoke(StaleShadowMounts));
EXPECT_CALL(platform_, GetAttachedLoopDevices())
.WillRepeatedly(Return(std::vector<Platform::LoopDevice>()));
EXPECT_CALL(platform_, GetLoopDeviceMounts(_)).WillOnce(Return(false));
EXPECT_CALL(
platform_,
EnumerateDirectoryEntries(
FilePath(kEphemeralCryptohomeDir).Append(kSparseFileDir), _, _))
.WillOnce(Return(false));
EXPECT_CALL(platform_, Unmount(_, true, _))
.Times(kShadowMounts.size())
.WillRepeatedly(Return(true));
// Expect the cleanup to clear user keys.
EXPECT_CALL(platform_, ClearUserKeyring()).WillOnce(Return(true));
EXPECT_CALL(platform_, InvalidateDirCryptoKey(_, _))
.Times(kShadowMounts.size())
.WillRepeatedly(Return(true));
EXPECT_FALSE(userdataauth_->CleanUpStaleMounts(true));
}
TEST_F(UserDataAuthTest, CleanUpStale_EmptyMap_OpenLegacy_ShadowOnly) {
// Check that when we have a bunch of stale shadow mounts, no active mounts,
// and some open filehandles to the legacy homedir, all mounts without
// filehandles are unmounted.
// Called by CleanUpStaleMounts and each time a directory is excluded.
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _))
.Times(4)
.WillRepeatedly(Invoke(StaleShadowMounts));
EXPECT_CALL(platform_, GetAttachedLoopDevices())
.WillRepeatedly(Return(std::vector<Platform::LoopDevice>()));
EXPECT_CALL(platform_, GetLoopDeviceMounts(_)).WillOnce(Return(false));
EXPECT_CALL(
platform_,
EnumerateDirectoryEntries(
FilePath(kEphemeralCryptohomeDir).Append(kSparseFileDir), _, _))
.WillOnce(Return(false));
EXPECT_CALL(platform_,
ExpireMount(Property(&FilePath::value, EndsWith("/0"))))
.WillRepeatedly(Return(ExpireMountResult::kBusy));
EXPECT_CALL(platform_, ExpireMount(FilePath("/home/chronos/user")))
.WillRepeatedly(Return(ExpireMountResult::kBusy));
EXPECT_CALL(platform_,
ExpireMount(Property(
&FilePath::value,
AnyOf(EndsWith("/1"), EndsWith("b/MyFiles/Downloads")))))
.Times(4)
.WillRepeatedly(Return(ExpireMountResult::kMarked));
EXPECT_CALL(platform_, ExpireMount(FilePath("/daemon-store/server/b")))
.WillOnce(Return(ExpireMountResult::kMarked));
// Given /home/chronos/user and a is marked as active, only b mounts should be
// removed.
EXPECT_CALL(
platform_,
Unmount(Property(&FilePath::value,
AnyOf(EndsWith("/1"), EndsWith("b/MyFiles/Downloads"))),
true, _))
.Times(4)
.WillRepeatedly(Return(true));
EXPECT_CALL(platform_, Unmount(FilePath("/daemon-store/server/b"), true, _))
.WillOnce(Return(true));
EXPECT_CALL(platform_,
Unmount(Property(&FilePath::value, EndsWith("/0")), true, _))
.Times(0);
EXPECT_CALL(platform_, Unmount(FilePath("/home/chronos/user"), true, _))
.Times(0);
EXPECT_TRUE(userdataauth_->CleanUpStaleMounts(false));
}
TEST_F(UserDataAuthTest, CleanUpStale_FilledMap_NoOpenFiles_ShadowOnly) {
constexpr char kUser[] = "foo@bar.net";
// Checks that when we have a bunch of stale shadow mounts, some active
// mounts, and no open filehandles, all inactive mounts are unmounted.
// Override the optional USS experiment flag with any value to avoid extra
// FileExists checks on USS flag files.
SetUserSecretStashExperimentForTesting(/*enabled*/ true);
EXPECT_CALL(platform_, FileExists(base::FilePath("/home/.shadow/salt")))
.WillOnce(Return(true));
EXPECT_CALL(platform_,
FileExists(base::FilePath("/run/cryptohome/not_first_boot")))
.WillOnce(Return(true));
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _)).WillOnce(Return(false));
EXPECT_CALL(platform_, GetAttachedLoopDevices())
.WillRepeatedly(Return(std::vector<Platform::LoopDevice>()));
EXPECT_CALL(platform_, GetLoopDeviceMounts(_)).WillOnce(Return(false));
InitializeUserDataAuth();
EXPECT_CALL(user_session_factory_, New(Username(kUser), _, _))
.WillOnce(Return(ByMove(CreateSessionAndRememberPtr())));
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(Return(true));
EXPECT_CALL(disk_cleanup_, FreeDiskSpaceDuringLogin(_));
EXPECT_CALL(*session_, MountVault(_, _, _))
.WillOnce(ReturnError<CryptohomeMountError>());
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _)).WillOnce(Return(false));
EXPECT_CALL(platform_, GetAttachedLoopDevices())
.WillRepeatedly(Return(std::vector<Platform::LoopDevice>()));
EXPECT_CALL(platform_, GetLoopDeviceMounts(_)).WillOnce(Return(false));
// StartAuthSession for new user.
user_data_auth::StartAuthSessionRequest start_session_req;
start_session_req.mutable_account_id()->set_account_id(kUser);
start_session_req.set_flags(
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE);
start_session_req.set_intent(user_data_auth::AuthIntent::AUTH_INTENT_DECRYPT);
TestFuture<user_data_auth::StartAuthSessionReply> reply_future;
userdataauth_->StartAuthSession(
start_session_req,
reply_future.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::optional<base::UnguessableToken> auth_session_id =
AuthSession::GetTokenFromSerializedString(
reply_future.Get().auth_session_id());
ASSERT_TRUE(auth_session_id.has_value());
// Get the session into an authenticated state by treating it as if we just
// freshly created the user.
{
InUseAuthSession auth_session =
userdataauth_->auth_session_manager_->FindAuthSession(
auth_session_id.value());
ASSERT_THAT(auth_session.AuthSessionStatus(), IsOk());
EXPECT_THAT(auth_session->OnUserCreated(), IsOk());
}
// Mount user vault.
user_data_auth::PreparePersistentVaultRequest prepare_request;
prepare_request.set_auth_session_id(reply_future.Get().auth_session_id());
TestFuture<user_data_auth::PreparePersistentVaultReply> prepare_future;
userdataauth_->PreparePersistentVault(
prepare_request,
prepare_future
.GetCallback<const user_data_auth::PreparePersistentVaultReply&>());
ASSERT_EQ(prepare_future.Get().error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
// Test CleanUpStaleMounts.
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _))
.Times(4)
.WillRepeatedly(Invoke(StaleShadowMounts));
EXPECT_CALL(platform_, GetAttachedLoopDevices())
.WillRepeatedly(Return(std::vector<Platform::LoopDevice>()));
EXPECT_CALL(platform_, GetLoopDeviceMounts(_)).WillOnce(Return(false));
EXPECT_CALL(
platform_,
EnumerateDirectoryEntries(
FilePath(kEphemeralCryptohomeDir).Append(kSparseFileDir), _, _))
.WillOnce(Return(false));
// Only 5 look ups: user/1 and root/1 are owned, children of these
// directories are excluded. ExpireMount is expected to run on exactly the
// same mount points that are expected to be unmounted below. But it is
// important to check the number of calls here to make sure ExpireMount
// doesn't run on any other mount points.
EXPECT_CALL(platform_, ExpireMount(_))
.Times(5)
.WillRepeatedly(Return(ExpireMountResult::kMarked));
EXPECT_CALL(*session_, OwnsMountPoint(_)).WillRepeatedly(Return(false));
EXPECT_CALL(*session_, OwnsMountPoint(FilePath("/home/user/1")))
.WillOnce(Return(true));
EXPECT_CALL(*session_, OwnsMountPoint(FilePath("/home/root/1")))
.WillOnce(Return(true));
EXPECT_CALL(platform_,
Unmount(Property(&FilePath::value, EndsWith("/0")), true, _))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(platform_, Unmount(FilePath("/home/chronos/user"), true, _))
.WillOnce(Return(true));
EXPECT_CALL(platform_, Unmount(Property(&FilePath::value,
EndsWith("user/MyFiles/Downloads")),
true, _))
.WillOnce(Return(true));
EXPECT_CALL(platform_, Unmount(FilePath("/daemon-store/server/a"), true, _))
.WillOnce(Return(true));
std::vector<std::string> fake_token_list;
fake_token_list.push_back("/home/chronos/user/token");
fake_token_list.push_back("/home/user/1/token");
fake_token_list.push_back("/home/root/1/token");
EXPECT_CALL(chaps_client_, GetTokenList(_, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(fake_token_list), Return(true)));
EXPECT_CALL(chaps_client_,
UnloadToken(_, FilePath("/home/chronos/user/token")))
.Times(1);
// Expect that CleanUpStaleMounts() tells us it skipped mounts since 1 is
// still logged in.
EXPECT_TRUE(userdataauth_->CleanUpStaleMounts(false));
SetUserSecretStashExperimentForTesting(/*enabled*/ std::nullopt);
}
TEST_F(UserDataAuthTest,
CleanUpStale_FilledMap_NoOpenFiles_ShadowOnly_FirstBoot) {
constexpr char kUser[] = "foo@bar.net";
// Checks that when we have a bunch of stale shadow mounts, some active
// mounts, and no open filehandles, all inactive mounts are unmounted.
// Override the optional USS experiment flag with any value to avoid extra
// FileExists checks on USS flag files.
SetUserSecretStashExperimentForTesting(/*enabled*/ true);
EXPECT_CALL(platform_, FileExists(base::FilePath("/home/.shadow/salt")))
.WillOnce(Return(false));
EXPECT_CALL(platform_,
FileExists(base::FilePath("/run/cryptohome/not_first_boot")))
.WillOnce(Return(false));
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _)).Times(0);
EXPECT_CALL(platform_, GetAttachedLoopDevices()).Times(0);
EXPECT_CALL(platform_, GetLoopDeviceMounts(_)).Times(0);
InitializeUserDataAuth();
EXPECT_CALL(user_session_factory_, New(Username(kUser), _, _))
.WillOnce(Return(ByMove(CreateSessionAndRememberPtr())));
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(ReturnValue(true));
EXPECT_CALL(disk_cleanup_, FreeDiskSpaceDuringLogin(_));
EXPECT_CALL(*session_, MountVault(_, _, _))
.WillOnce(ReturnError<CryptohomeMountError>());
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _)).WillOnce(Return(false));
EXPECT_CALL(platform_, GetAttachedLoopDevices())
.WillRepeatedly(Return(std::vector<Platform::LoopDevice>()));
EXPECT_CALL(platform_, GetLoopDeviceMounts(_)).WillOnce(Return(false));
// StartAuthSession for new user
user_data_auth::StartAuthSessionRequest start_session_req;
start_session_req.mutable_account_id()->set_account_id(kUser);
start_session_req.set_flags(
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE);
start_session_req.set_intent(user_data_auth::AuthIntent::AUTH_INTENT_DECRYPT);
TestFuture<user_data_auth::StartAuthSessionReply> reply_future;
userdataauth_->StartAuthSession(
start_session_req,
reply_future.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::optional<base::UnguessableToken> auth_session_id =
AuthSession::GetTokenFromSerializedString(
reply_future.Get().auth_session_id());
ASSERT_TRUE(auth_session_id.has_value());
// Get the session into an authenticated state by treating it as if we just
// freshly created the user.
{
InUseAuthSession auth_session =
userdataauth_->auth_session_manager_->FindAuthSession(
auth_session_id.value());
ASSERT_THAT(auth_session.AuthSessionStatus(), IsOk());
EXPECT_THAT(auth_session->OnUserCreated(), IsOk());
}
// Mount user vault.
user_data_auth::PreparePersistentVaultRequest prepare_request;
prepare_request.set_auth_session_id(reply_future.Get().auth_session_id());
TestFuture<user_data_auth::PreparePersistentVaultReply> prepare_future;
userdataauth_->PreparePersistentVault(
prepare_request,
prepare_future
.GetCallback<const user_data_auth::PreparePersistentVaultReply&>());
ASSERT_EQ(prepare_future.Get().error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
EXPECT_CALL(platform_, GetMountsBySourcePrefix(_, _))
.Times(4)
.WillRepeatedly(Invoke(StaleShadowMounts));
EXPECT_CALL(platform_, GetAttachedLoopDevices())
.WillRepeatedly(Return(std::vector<Platform::LoopDevice>()));
EXPECT_CALL(platform_, GetLoopDeviceMounts(_)).WillOnce(Return(false));
EXPECT_CALL(
platform_,
EnumerateDirectoryEntries(
FilePath(kEphemeralCryptohomeDir).Append(kSparseFileDir), _, _))
.WillOnce(Return(false));
// Only 5 look ups: user/1 and root/1 are owned, children of these
// directories are excluded. ExpireMount is expected to run on exactly the
// same mount points that are expected to be unmounted below. But it is
// important to check the number of calls here to make sure ExpireMount
// doesn't run on any other mount points.
EXPECT_CALL(platform_, ExpireMount(_)).Times(5);
EXPECT_CALL(*session_, OwnsMountPoint(_)).WillRepeatedly(Return(false));
EXPECT_CALL(*session_, OwnsMountPoint(FilePath("/home/user/1")))
.WillOnce(Return(true));
EXPECT_CALL(*session_, OwnsMountPoint(FilePath("/home/root/1")))
.WillOnce(Return(true));
EXPECT_CALL(platform_,
Unmount(Property(&FilePath::value, EndsWith("/0")), true, _))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(platform_, Unmount(FilePath("/home/chronos/user"), true, _))
.WillOnce(Return(true));
EXPECT_CALL(platform_, Unmount(Property(&FilePath::value,
EndsWith("user/MyFiles/Downloads")),
true, _))
.WillOnce(Return(true));
EXPECT_CALL(platform_, Unmount(FilePath("/daemon-store/server/a"), true, _))
.WillOnce(Return(true));
std::vector<std::string> fake_token_list;
fake_token_list.push_back("/home/chronos/user/token");
fake_token_list.push_back("/home/user/1/token");
fake_token_list.push_back("/home/root/1/token");
EXPECT_CALL(chaps_client_, GetTokenList(_, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(fake_token_list), Return(true)));
EXPECT_CALL(chaps_client_,
UnloadToken(_, FilePath("/home/chronos/user/token")))
.Times(1);
// Expect that CleanUpStaleMounts() tells us it skipped mounts since 1 is
// still logged in.
EXPECT_TRUE(userdataauth_->CleanUpStaleMounts(false));
SetUserSecretStashExperimentForTesting(/*enabled*/ std::nullopt);
}
TEST_F(UserDataAuthTest, StartMigrateToDircryptoValidity) {
constexpr char kUsername1[] = "foo@gmail.com";
user_data_auth::StartMigrateToDircryptoRequest request;
request.mutable_account_id()->set_account_id(kUsername1);
request.set_minimal_migration(false);
SetupMount(kUsername1);
EXPECT_CALL(*session_, MigrateVault(_, MigrationType::FULL))
.WillOnce(Return(true));
int success_cnt = 0;
{
userdataauth_->StartMigrateToDircrypto(
request,
base::BindRepeating(
[](int* success_cnt_ptr,
const user_data_auth::DircryptoMigrationProgress& progress) {
EXPECT_EQ(progress.status(),
user_data_auth::DIRCRYPTO_MIGRATION_SUCCESS);
(*success_cnt_ptr)++;
},
base::Unretained(&success_cnt)));
}
EXPECT_EQ(success_cnt, 1);
}
TEST_F(UserDataAuthTest, StartMigrateToDircryptoFailure) {
constexpr char kUsername1[] = "foo@gmail.com";
user_data_auth::StartMigrateToDircryptoRequest request;
request.mutable_account_id()->set_account_id(kUsername1);
request.set_minimal_migration(false);
// Test mount non-existent.
int call_cnt = 0;
{
userdataauth_->StartMigrateToDircrypto(
request,
base::BindRepeating(
[](int* call_cnt_ptr,
const user_data_auth::DircryptoMigrationProgress& progress) {
EXPECT_EQ(progress.status(),
user_data_auth::DIRCRYPTO_MIGRATION_FAILED);
(*call_cnt_ptr)++;
},
base::Unretained(&call_cnt)));
}
EXPECT_EQ(call_cnt, 1);
// Test MigrateToDircrypto failed
SetupMount(kUsername1);
EXPECT_CALL(*session_, MigrateVault(_, MigrationType::FULL))
.WillOnce(Return(false));
call_cnt = 0;
{
userdataauth_->StartMigrateToDircrypto(
request,
base::BindRepeating(
[](int* call_cnt_ptr,
const user_data_auth::DircryptoMigrationProgress& progress) {
EXPECT_EQ(progress.status(),
user_data_auth::DIRCRYPTO_MIGRATION_FAILED);
(*call_cnt_ptr)++;
},
base::Unretained(&call_cnt)));
}
EXPECT_EQ(call_cnt, 1);
}
TEST_F(UserDataAuthTest, NeedsDircryptoMigration) {
bool result;
AccountIdentifier account;
account.set_account_id("foo@gmail.com");
// Test the case when we are forced to use eCryptfs, and thus no migration is
// needed.
userdataauth_->set_force_ecryptfs(true);
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(Return(true));
EXPECT_EQ(userdataauth_->NeedsDircryptoMigration(account, &result),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_FALSE(result);
// Test the case when dircrypto is already in use.
userdataauth_->set_force_ecryptfs(false);
EXPECT_CALL(homedirs_, NeedsDircryptoMigration(_)).WillOnce(Return(false));
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(Return(true));
EXPECT_EQ(userdataauth_->NeedsDircryptoMigration(account, &result),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_FALSE(result);
// Test the case when eCryptfs is being used.
userdataauth_->set_force_ecryptfs(false);
EXPECT_CALL(homedirs_, NeedsDircryptoMigration(_)).WillOnce(Return(true));
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(Return(true));
EXPECT_EQ(userdataauth_->NeedsDircryptoMigration(account, &result),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_TRUE(result);
// Test for account not found.
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(Return(false));
EXPECT_EQ(userdataauth_->NeedsDircryptoMigration(account, &result),
user_data_auth::CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND);
}
TEST_F(UserDataAuthTest, LowEntropyCredentialSupported) {
EXPECT_CALL(hwsec_, IsPinWeaverEnabled()).WillRepeatedly(ReturnValue(false));
EXPECT_FALSE(userdataauth_->IsLowEntropyCredentialSupported());
EXPECT_CALL(hwsec_, IsPinWeaverEnabled()).WillRepeatedly(ReturnValue(true));
EXPECT_TRUE(userdataauth_->IsLowEntropyCredentialSupported());
}
TEST_F(UserDataAuthTest, GetAccountDiskUsage) {
// Test when the user is non-existent.
AccountIdentifier account;
account.set_account_id("non_existent_user");
EXPECT_EQ(0, userdataauth_->GetAccountDiskUsage(account));
// Test when the user exists and home directory is not empty.
const Username kUsername1("foo@gmail.com");
account.set_account_id(*kUsername1);
constexpr int64_t kHomedirSize = 12345678912345;
EXPECT_CALL(homedirs_, ComputeDiskUsage(kUsername1))
.WillOnce(Return(kHomedirSize));
EXPECT_EQ(kHomedirSize, userdataauth_->GetAccountDiskUsage(account));
}
TEST_F(UserDataAuthTest, LowDiskSpaceNotificationCallback) {
EXPECT_CALL(low_disk_space_handler_, SetLowDiskSpaceCallback(_));
userdataauth_->SetLowDiskSpaceCallback(base::BindRepeating([](uint64_t) {}));
}
TEST_F(UserDataAuthTest, LowDiskSpaceHandlerStopped) {
EXPECT_CALL(low_disk_space_handler_, Stop());
}
// A test fixture with some utility functions for testing mount and keys related
// functionalities.
class UserDataAuthExTest : public UserDataAuthTest {
public:
UserDataAuthExTest() = default;
UserDataAuthExTest(const UserDataAuthExTest&) = delete;
UserDataAuthExTest& operator=(const UserDataAuthExTest&) = delete;
~UserDataAuthExTest() override = default;
std::unique_ptr<VaultKeyset> GetNiceMockVaultKeyset(
const ObfuscatedUsername& obfuscated_username,
const std::string& key_label) const {
// Note that technically speaking this is not strictly a mock, and probably
// closer to a stub. However, the underlying class is
// NiceMock<MockVaultKeyset>, thus we name the method accordingly.
std::unique_ptr<VaultKeyset> mvk(new NiceMock<MockVaultKeyset>);
mvk->SetKeyDataLabel(key_label);
SerializedVaultKeyset::SignatureChallengeInfo sig_challenge_info;
mvk->SetSignatureChallengeInfo(sig_challenge_info);
return mvk;
}
// Create a USS with wrapped keys registered for all of the given labels. Note
// that the generated USS will not contain any "real" keys.
void MakeUssWithLabels(const ObfuscatedUsername& obfuscated_username,
const std::vector<std::string>& labels) {
// Create a random USS.
UserUssStorage user_storage(*userdataauth_->uss_storage_,
obfuscated_username);
auto uss = DecryptedUss::CreateWithRandomMainKey(
user_storage, FileSystemKeyset::CreateRandom());
if (!uss.ok()) {
ADD_FAILURE() << "Making a test USS failed at CreateRandom: "
<< uss.status();
return;
}
{
auto transaction = uss->StartTransaction();
// Generate a main key and some wrap it for each label. Note that we just
// make up junk wrapping keys because we don't actually plan to decrypt
// the container.
for (const std::string& label : labels) {
SecureBlob wrapping_key(kAesGcm256KeySize, 0xC0);
CryptohomeStatus status =
transaction.InsertWrappedMainKey(label, std::move(wrapping_key));
if (!status.ok()) {
ADD_FAILURE() << "Making a test USS failed adding label " << label
<< ": " << status;
return;
}
}
auto status = std::move(transaction).Commit();
if (!status.ok()) {
ADD_FAILURE() << "Making a test USS failed during Commit: " << status;
return;
}
}
}
protected:
void PrepareArguments() {
list_keys_req_ = std::make_unique<user_data_auth::ListKeysRequest>();
remove_homedir_req_ = std::make_unique<user_data_auth::RemoveRequest>();
start_auth_session_req_ =
std::make_unique<user_data_auth::StartAuthSessionRequest>();
}
template <class ProtoBuf>
brillo::Blob BlobFromProtobuf(const ProtoBuf& pb) {
std::string serialized;
CHECK(pb.SerializeToString(&serialized));
return brillo::BlobFromString(serialized);
}
template <class ProtoBuf>
brillo::SecureBlob SecureBlobFromProtobuf(const ProtoBuf& pb) {
std::string serialized;
CHECK(pb.SerializeToString(&serialized));
return brillo::SecureBlob(serialized);
}
std::unique_ptr<user_data_auth::ListKeysRequest> list_keys_req_;
std::unique_ptr<user_data_auth::RemoveRequest> remove_homedir_req_;
std::unique_ptr<user_data_auth::StartAuthSessionRequest>
start_auth_session_req_;
const Username kUser{"chromeos-user"};
static constexpr char kKey[] = "274146c6e8886a843ddfea373e2dc71b";
};
constexpr char UserDataAuthExTest::kKey[];
TEST_F(UserDataAuthExTest,
StartMigrateToDircryptoWithAuthenticatedAuthSession) {
// Setup.
PrepareArguments();
constexpr char kUsername1[] = "foo@gmail.com";
start_auth_session_req_->mutable_account_id()->set_account_id(kUsername1);
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::optional<base::UnguessableToken> auth_session_id =
AuthSession::GetTokenFromSerializedString(
auth_session_reply_future.Get().auth_session_id());
ASSERT_TRUE(auth_session_id.has_value());
// Get the session into an authenticated state by treating it as if we just
// freshly created the user.
{
InUseAuthSession auth_session =
userdataauth_->auth_session_manager_->FindAuthSession(
auth_session_id.value());
ASSERT_THAT(auth_session.AuthSessionStatus(), IsOk());
EXPECT_THAT(auth_session->OnUserCreated(), IsOk());
}
user_data_auth::StartMigrateToDircryptoRequest request;
request.set_auth_session_id(
auth_session_reply_future.Get().auth_session_id());
request.set_minimal_migration(false);
SetupMount(kUsername1);
EXPECT_CALL(*session_, MigrateVault(_, MigrationType::FULL))
.WillOnce(Return(true));
int success_cnt = 0;
{
userdataauth_->StartMigrateToDircrypto(
request,
base::BindRepeating(
[](int* success_cnt_ptr,
const user_data_auth::DircryptoMigrationProgress& progress) {
EXPECT_EQ(progress.status(),
user_data_auth::DIRCRYPTO_MIGRATION_SUCCESS);
(*success_cnt_ptr)++;
},
base::Unretained(&success_cnt)));
}
EXPECT_EQ(success_cnt, 1);
}
TEST_F(UserDataAuthExTest,
StartMigrateToDircryptoWithUnAuthenticatedAuthSession) {
// Setup.
PrepareArguments();
constexpr char kUsername1[] = "foo@gmail.com";
start_auth_session_req_->mutable_account_id()->set_account_id(kUsername1);
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::optional<base::UnguessableToken> auth_session_id =
AuthSession::GetTokenFromSerializedString(
auth_session_reply_future.Get().auth_session_id());
ASSERT_TRUE(auth_session_id.has_value());
InUseAuthSession auth_session =
userdataauth_->auth_session_manager_->FindAuthSession(
auth_session_id.value());
ASSERT_TRUE(auth_session.AuthSessionStatus().ok());
user_data_auth::StartMigrateToDircryptoRequest request;
request.set_auth_session_id(
auth_session_reply_future.Get().auth_session_id());
request.set_minimal_migration(false);
int called_ctr = 0;
{
userdataauth_->StartMigrateToDircrypto(
request,
base::BindRepeating(
[](int* called_ctr_ptr,
const user_data_auth::DircryptoMigrationProgress& progress) {
EXPECT_EQ(progress.status(),
user_data_auth::DIRCRYPTO_MIGRATION_FAILED);
(*called_ctr_ptr)++;
},
base::Unretained(&called_ctr)));
}
EXPECT_EQ(called_ctr, 1);
}
TEST_F(UserDataAuthExTest, StartMigrateToDircryptoWithInvalidAuthSession) {
// Setup.
PrepareArguments();
constexpr char kFakeAuthSessionId[] = "foo";
user_data_auth::StartMigrateToDircryptoRequest request;
request.set_auth_session_id(kFakeAuthSessionId);
request.set_minimal_migration(false);
int called_ctr = 0;
{
userdataauth_->StartMigrateToDircrypto(
request,
base::BindRepeating(
[](int* called_ctr_ptr,
const user_data_auth::DircryptoMigrationProgress& progress) {
EXPECT_EQ(progress.status(),
user_data_auth::DIRCRYPTO_MIGRATION_FAILED);
(*called_ctr_ptr)++;
},
base::Unretained(&called_ctr)));
}
EXPECT_EQ(called_ctr, 1);
}
constexpr char ListKeysValidityTest_label1[] = "Label 1";
constexpr char ListKeysValidityTest_label2[] = "Yet another label";
TEST_F(UserDataAuthExTest, ListKeysValidity) {
PrepareArguments();
list_keys_req_->mutable_account_id()->set_account_id("foo@gmail.com");
// Note that authorization request in ListKeyRequest is currently not
// required.
// Success case.
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabels(_, _, _))
.WillOnce(
Invoke([](const ObfuscatedUsername& ignored, bool include_le_labels,
std::vector<std::string>* output) {
output->clear();
output->push_back(ListKeysValidityTest_label1);
output->push_back(ListKeysValidityTest_label2);
return true;
}));
user_data_auth::ListKeysReply reply =
userdataauth_->ListKeys(*list_keys_req_);
EXPECT_EQ(reply.error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
EXPECT_THAT(reply.labels(), ElementsAre(ListKeysValidityTest_label1,
ListKeysValidityTest_label2));
// Test for account not found case.
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(Return(false));
EXPECT_NE(
userdataauth_->ListKeys(*list_keys_req_).error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
// Test for key not found case.
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(Return(true));
EXPECT_CALL(keyset_management_, GetVaultKeysetLabels(_, _, _))
.WillOnce(Return(false));
EXPECT_NE(
userdataauth_->ListKeys(*list_keys_req_).error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
}
TEST_F(UserDataAuthExTest, ListKeysInvalidArgs) {
PrepareArguments();
// No Email.
EXPECT_NE(
userdataauth_->ListKeys(*list_keys_req_).error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
// Empty email.
list_keys_req_->mutable_account_id()->set_account_id("");
EXPECT_NE(
userdataauth_->ListKeys(*list_keys_req_).error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
}
TEST_F(UserDataAuthExTest, RemoveValidity) {
PrepareArguments();
const Username kUsername1("foo@gmail.com");
remove_homedir_req_->mutable_identifier()->set_account_id(*kUsername1);
// Test for successful case.
EXPECT_CALL(homedirs_, Remove(GetObfuscatedUsername(kUsername1)))
.WillOnce(Return(true));
EXPECT_EQ(
userdataauth_->Remove(*remove_homedir_req_).error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
// Test for unsuccessful case.
EXPECT_CALL(homedirs_, Remove(GetObfuscatedUsername(kUsername1)))
.WillOnce(Return(false));
EXPECT_NE(
userdataauth_->Remove(*remove_homedir_req_).error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
}
TEST_F(UserDataAuthExTest, RemoveBusyMounted) {
PrepareArguments();
SetupMount(*kUser);
remove_homedir_req_->mutable_identifier()->set_account_id(*kUser);
ON_CALL(*session_, IsActive()).WillByDefault(Return(true));
EXPECT_NE(
userdataauth_->Remove(*remove_homedir_req_).error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
}
TEST_F(UserDataAuthExTest, RemoveInvalidArguments) {
PrepareArguments();
// No account_id and AuthSession ID
EXPECT_NE(
userdataauth_->Remove(*remove_homedir_req_).error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
// Empty account_id
remove_homedir_req_->mutable_identifier()->set_account_id("");
EXPECT_NE(
userdataauth_->Remove(*remove_homedir_req_).error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
}
TEST_F(UserDataAuthExTest, RemoveInvalidAuthSession) {
PrepareArguments();
std::string invalid_token = "invalid_token_16";
remove_homedir_req_->set_auth_session_id(invalid_token);
// Test.
EXPECT_NE(
userdataauth_->Remove(*remove_homedir_req_).error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
}
TEST_F(UserDataAuthExTest, RemoveValidityWithAuthSession) {
PrepareArguments();
// Setup
const Username kUsername1("foo@gmail.com");
start_auth_session_req_->mutable_account_id()->set_account_id(*kUsername1);
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
const std::string auth_session_id =
auth_session_reply_future.Get().auth_session_id();
// Test
remove_homedir_req_->set_auth_session_id(auth_session_id);
EXPECT_CALL(homedirs_, Remove(GetObfuscatedUsername(kUsername1)))
.WillOnce(Return(true));
EXPECT_EQ(
userdataauth_->Remove(*remove_homedir_req_).error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
// Verify
InUseAuthSession auth_session =
userdataauth_->auth_session_manager_->FindAuthSession(auth_session_id);
EXPECT_FALSE(auth_session.AuthSessionStatus().ok());
}
TEST_F(UserDataAuthExTest, StartAuthSession) {
PrepareArguments();
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::optional<base::UnguessableToken> auth_session_id =
AuthSession::GetTokenFromSerializedString(
auth_session_reply_future.Get().auth_session_id());
ASSERT_TRUE(auth_session_id.has_value());
InUseAuthSession auth_session =
userdataauth_->auth_session_manager_->FindAuthSession(
auth_session_id.value());
ASSERT_TRUE(auth_session.AuthSessionStatus().ok());
EXPECT_EQ(auth_session->token(), *auth_session_id);
std::optional<base::UnguessableToken> broadcast_id =
AuthSession::GetTokenFromSerializedString(
auth_session_reply_future.Get().broadcast_id());
ASSERT_TRUE(broadcast_id.has_value());
EXPECT_EQ(auth_session->public_token(), *broadcast_id);
}
TEST_F(UserDataAuthExTest, StartAuthSessionUnusableClobber) {
PrepareArguments();
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
EXPECT_CALL(platform_, DirectoryExists(_)).WillOnce(Return(true));
EXPECT_CALL(platform_, GetFileEnumerator(_, _, _))
.WillOnce(Return(new NiceMock<MockFileEnumerator>));
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_UNUSABLE_VAULT);
std::optional<base::UnguessableToken> auth_session_id =
AuthSession::GetTokenFromSerializedString(
auth_session_reply_future.Get().auth_session_id());
EXPECT_TRUE(auth_session_id.has_value());
InUseAuthSession auth_session =
userdataauth_->auth_session_manager_->FindAuthSession(
auth_session_id.value());
EXPECT_TRUE(auth_session.AuthSessionStatus().ok());
}
TEST_F(UserDataAuthExTest, InvalidateAuthSession) {
// Setup.
PrepareArguments();
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::optional<base::UnguessableToken> auth_session_id =
AuthSession::GetTokenFromSerializedString(
auth_session_reply_future.Get().auth_session_id());
EXPECT_TRUE(auth_session_id.has_value());
InUseAuthSession auth_session =
userdataauth_->auth_session_manager_->FindAuthSession(
auth_session_id.value());
EXPECT_TRUE(auth_session.AuthSessionStatus().ok());
// Test.
user_data_auth::InvalidateAuthSessionRequest inv_auth_session_req;
inv_auth_session_req.set_auth_session_id(
auth_session_reply_future.Get().auth_session_id());
// Invalidate the AuthSession immediately.
TestFuture<user_data_auth::InvalidateAuthSessionReply> reply_future;
userdataauth_->InvalidateAuthSession(
inv_auth_session_req,
reply_future
.GetCallback<const user_data_auth::InvalidateAuthSessionReply&>());
EXPECT_EQ(reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
auth_session = userdataauth_->auth_session_manager_->FindAuthSession(
auth_session_id.value());
EXPECT_FALSE(auth_session.AuthSessionStatus().ok());
}
TEST_F(UserDataAuthExTest, ExtendAuthSession) {
// Setup.
PrepareArguments();
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::optional<base::UnguessableToken> auth_session_id =
AuthSession::GetTokenFromSerializedString(
auth_session_reply_future.Get().auth_session_id());
EXPECT_TRUE(auth_session_id.has_value());
// Get the session into an authenticated state by treating it as if we just
// freshly created the user.
{
InUseAuthSession auth_session =
userdataauth_->auth_session_manager_->FindAuthSession(
auth_session_id.value());
ASSERT_THAT(auth_session.AuthSessionStatus(), IsOk());
EXPECT_THAT(auth_session->OnUserCreated(), IsOk());
}
// Test.
user_data_auth::ExtendAuthSessionRequest ext_auth_session_req;
ext_auth_session_req.set_auth_session_id(
auth_session_reply_future.Get().auth_session_id());
ext_auth_session_req.set_extension_duration(kAuthSessionExtensionDuration);
// Extend the AuthSession.
TestFuture<user_data_auth::ExtendAuthSessionReply> reply_future;
userdataauth_->ExtendAuthSession(
ext_auth_session_req,
reply_future
.GetCallback<const user_data_auth::ExtendAuthSessionReply&>());
EXPECT_EQ(reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_TRUE(reply_future.Get().has_seconds_left());
EXPECT_GT(reply_future.Get().seconds_left(), kAuthSessionExtensionDuration);
// Verify that timer has changed, within a resaonsable degree of error.
InUseAuthSession auth_session =
userdataauth_->auth_session_manager_->FindAuthSession(
auth_session_id.value());
auto requested_delay = auth_session.GetRemainingTime();
auto time_difference = kAuthSessionTimeout - requested_delay;
EXPECT_LT(time_difference, base::Seconds(1));
}
TEST_F(UserDataAuthExTest, ExtendUnAuthenticatedAuthSessionFail) {
// Setup.
PrepareArguments();
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::optional<base::UnguessableToken> auth_session_id =
AuthSession::GetTokenFromSerializedString(
auth_session_reply_future.Get().auth_session_id());
EXPECT_TRUE(auth_session_id.has_value());
{
InUseAuthSession auth_session =
userdataauth_->auth_session_manager_->FindAuthSession(
auth_session_id.value());
ASSERT_TRUE(auth_session.AuthSessionStatus().ok());
}
// Test.
user_data_auth::ExtendAuthSessionRequest ext_auth_session_req;
ext_auth_session_req.set_auth_session_id(
auth_session_reply_future.Get().auth_session_id());
ext_auth_session_req.set_extension_duration(kAuthSessionExtensionDuration);
// Extend the AuthSession.
TestFuture<user_data_auth::ExtendAuthSessionReply> reply_future;
userdataauth_->ExtendAuthSession(
ext_auth_session_req,
reply_future
.GetCallback<const user_data_auth::ExtendAuthSessionReply&>());
EXPECT_EQ(reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
EXPECT_FALSE(reply_future.Get().has_seconds_left());
}
TEST_F(UserDataAuthExTest, CheckTimeoutTimerSetAfterAuthentication) {
// Setup.
PrepareArguments();
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::optional<base::UnguessableToken> auth_session_id =
AuthSession::GetTokenFromSerializedString(
auth_session_reply_future.Get().auth_session_id());
EXPECT_TRUE(auth_session_id.has_value());
InUseAuthSession auth_session =
userdataauth_->auth_session_manager_->FindAuthSession(
auth_session_id.value());
ASSERT_THAT(auth_session.AuthSessionStatus(), IsOk());
// Timer is not set before authentication.
EXPECT_TRUE(auth_session.GetRemainingTime().is_max());
// Extension only happens for authenticated auth session.
EXPECT_THAT(auth_session->OnUserCreated(), IsOk());
// Test timer is correctly set after authentication.
EXPECT_FALSE(auth_session.GetRemainingTime().is_max());
}
TEST_F(UserDataAuthExTest, StartAuthSessionReplyCheck) {
PrepareArguments();
// Setup
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
KeyData key_data;
key_data.set_label(kFakeLabel);
key_data.set_type(KeyData::KEY_TYPE_PASSWORD);
KeyLabelMap keyLabelData = {{kFakeLabel, key_data}};
EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true));
std::vector<int> vk_indicies = {0};
EXPECT_CALL(keyset_management_, GetVaultKeysets(_, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(vk_indicies), Return(true)));
EXPECT_CALL(keyset_management_, LoadVaultKeysetForUser(_, 0))
.WillRepeatedly([key_data](const ObfuscatedUsername&, int) {
auto vk = std::make_unique<VaultKeyset>();
vk->SetFlags(SerializedVaultKeyset::TPM_WRAPPED |
SerializedVaultKeyset::PCR_BOUND);
vk->SetKeyData(key_data);
vk->SetTPMKey(SecureBlob("fake tpm key"));
vk->SetExtendedTPMKey(SecureBlob("fake extended tpm key"));
return vk;
});
TestFuture<user_data_auth::StartAuthSessionReply>
start_auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
start_auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
const user_data_auth::StartAuthSessionReply& start_auth_session_reply =
start_auth_session_reply_future.Get();
EXPECT_THAT(start_auth_session_reply.auth_factors().size(), 1);
EXPECT_THAT(start_auth_session_reply.auth_factors().at(0).label(),
kFakeLabel);
EXPECT_THAT(start_auth_session_reply.auth_factors().at(0).type(),
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
EXPECT_THAT(
start_auth_session_reply.configured_auth_factors_with_status().size(), 1);
EXPECT_THAT(start_auth_session_reply.configured_auth_factors_with_status()
.at(0)
.auth_factor()
.label(),
kFakeLabel);
EXPECT_THAT(start_auth_session_reply.configured_auth_factors_with_status()
.at(0)
.auth_factor()
.type(),
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
EXPECT_THAT(start_auth_session_reply.configured_auth_factors_with_status()
.at(0)
.available_for_intents(),
UnorderedElementsAre(user_data_auth::AUTH_INTENT_VERIFY_ONLY,
user_data_auth::AUTH_INTENT_DECRYPT,
user_data_auth::AUTH_INTENT_WEBAUTHN));
}
TEST_F(UserDataAuthExTest, StartAuthSessionVerifyOnlyFactors) {
PrepareArguments();
SetupMount("foo@example.com");
// Setup
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
start_auth_session_req_->set_intent(user_data_auth::AUTH_INTENT_VERIFY_ONLY);
KeyData key_data;
key_data.set_label(kFakeLabel);
key_data.set_type(KeyData::KEY_TYPE_PASSWORD);
// Add persistent auth factors.
EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true));
std::vector<int> vk_indicies = {0};
EXPECT_CALL(keyset_management_, GetVaultKeysets(_, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(vk_indicies), Return(true)));
EXPECT_CALL(keyset_management_, LoadVaultKeysetForUser(_, 0))
.WillRepeatedly([key_data](const ObfuscatedUsername&, int) {
auto vk = std::make_unique<VaultKeyset>();
vk->SetFlags(SerializedVaultKeyset::TPM_WRAPPED |
SerializedVaultKeyset::PCR_BOUND);
vk->SetKeyData(key_data);
vk->SetTPMKey(SecureBlob("fake tpm key"));
vk->SetExtendedTPMKey(SecureBlob("fake extended tpm key"));
return vk;
});
// Add a verifier as well.
session_->AddCredentialVerifier(std::make_unique<MockCredentialVerifier>(
AuthFactorType::kPassword, kFakeLabel,
AuthFactorMetadata{.metadata = auth_factor::PasswordMetadata()}));
TestFuture<user_data_auth::StartAuthSessionReply>
start_auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
start_auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
const user_data_auth::StartAuthSessionReply& start_auth_session_reply =
start_auth_session_reply_future.Get();
EXPECT_EQ(start_auth_session_reply.error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
ASSERT_THAT(start_auth_session_reply.auth_factors().size(), 1);
// We should only find one factor, not two. There's a persistent factor and a
// verifier but they have the same label.
EXPECT_THAT(start_auth_session_reply.auth_factors().at(0).label(),
kFakeLabel);
EXPECT_THAT(start_auth_session_reply.auth_factors().at(0).type(),
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
EXPECT_THAT(
start_auth_session_reply.configured_auth_factors_with_status().size(), 1);
EXPECT_THAT(start_auth_session_reply.configured_auth_factors_with_status()
.at(0)
.auth_factor()
.label(),
kFakeLabel);
EXPECT_THAT(start_auth_session_reply.configured_auth_factors_with_status()
.at(0)
.auth_factor()
.type(),
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
EXPECT_THAT(start_auth_session_reply.configured_auth_factors_with_status()
.at(0)
.available_for_intents(),
UnorderedElementsAre(user_data_auth::AUTH_INTENT_VERIFY_ONLY,
user_data_auth::AUTH_INTENT_DECRYPT,
user_data_auth::AUTH_INTENT_WEBAUTHN));
}
TEST_F(UserDataAuthExTest, StartAuthSessionEphemeralFactors) {
PrepareArguments();
SetupMount("foo@example.com");
// Setup
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
start_auth_session_req_->set_intent(user_data_auth::AUTH_INTENT_VERIFY_ONLY);
start_auth_session_req_->set_flags(
user_data_auth::AUTH_SESSION_FLAGS_EPHEMERAL_USER);
EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true));
session_->AddCredentialVerifier(std::make_unique<MockCredentialVerifier>(
AuthFactorType::kPassword, "password-verifier-label",
AuthFactorMetadata{.metadata = auth_factor::PasswordMetadata()}));
TestFuture<user_data_auth::StartAuthSessionReply>
start_auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
start_auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
const user_data_auth::StartAuthSessionReply& start_auth_session_reply =
start_auth_session_reply_future.Get();
EXPECT_EQ(start_auth_session_reply.error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
ASSERT_THAT(start_auth_session_reply.auth_factors().size(), 1);
EXPECT_THAT(start_auth_session_reply.auth_factors().at(0).label(),
"password-verifier-label");
EXPECT_THAT(start_auth_session_reply.auth_factors().at(0).type(),
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
EXPECT_THAT(
start_auth_session_reply.configured_auth_factors_with_status().size(), 1);
EXPECT_THAT(start_auth_session_reply.configured_auth_factors_with_status()
.at(0)
.auth_factor()
.label(),
"password-verifier-label");
EXPECT_THAT(start_auth_session_reply.configured_auth_factors_with_status()
.at(0)
.auth_factor()
.type(),
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
EXPECT_THAT(start_auth_session_reply.configured_auth_factors_with_status()
.at(0)
.available_for_intents(),
UnorderedElementsAre(user_data_auth::AUTH_INTENT_VERIFY_ONLY));
}
TEST_F(UserDataAuthExTest, ListAuthFactorsUserDoesNotExist) {
EXPECT_CALL(platform_, DirectoryExists(_)).WillOnce(Return(false));
user_data_auth::ListAuthFactorsRequest list_request;
list_request.mutable_account_id()->set_account_id("foo@example.com");
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
EXPECT_EQ(list_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
}
TEST_F(UserDataAuthExTest, ListAuthFactorsUserIsPersistentButHasNoStorage) {
SetupMount("foo@example.com");
EXPECT_CALL(*session_, IsEphemeral()).WillRepeatedly(Return(false));
user_data_auth::ListAuthFactorsRequest list_request;
list_request.mutable_account_id()->set_account_id("foo@example.com");
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
const user_data_auth::ListAuthFactorsReply& list_reply =
list_reply_future.Get();
EXPECT_EQ(list_reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(list_reply.configured_auth_factors_with_status(), IsEmpty());
EXPECT_THAT(
list_reply.supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY,
user_data_auth::AUTH_FACTOR_TYPE_KIOSK,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
std::vector<user_data_auth::AuthFactorType> types_with_intents;
for (const auto& intents_for_type : list_reply.auth_intents_for_types()) {
types_with_intents.push_back(intents_for_type.type());
EXPECT_THAT(intents_for_type.current(),
UnorderedElementsAre(user_data_auth::AUTH_INTENT_DECRYPT,
user_data_auth::AUTH_INTENT_VERIFY_ONLY,
user_data_auth::AUTH_INTENT_WEBAUTHN));
}
EXPECT_THAT(
types_with_intents,
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY,
user_data_auth::AUTH_FACTOR_TYPE_KIOSK,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
}
TEST_F(UserDataAuthExTest, ListAuthFactorsUserIsEphemeralWithoutVerifier) {
EXPECT_CALL(platform_, DirectoryExists(_)).WillOnce(Return(false));
// Add a mount (and user session) for the ephemeral user.
SetupMount("foo@example.com");
EXPECT_CALL(*session_, IsEphemeral()).WillRepeatedly(Return(true));
user_data_auth::ListAuthFactorsRequest list_request;
list_request.mutable_account_id()->set_account_id("foo@example.com");
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
const user_data_auth::ListAuthFactorsReply& list_reply =
list_reply_future.Get();
EXPECT_EQ(list_reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(list_reply.configured_auth_factors_with_status(), IsEmpty());
EXPECT_THAT(
list_reply.supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
std::vector<user_data_auth::AuthFactorType> types_with_intents;
for (const auto& intents_for_type : list_reply.auth_intents_for_types()) {
types_with_intents.push_back(intents_for_type.type());
EXPECT_THAT(intents_for_type.current(),
UnorderedElementsAre(user_data_auth::AUTH_INTENT_VERIFY_ONLY));
}
EXPECT_THAT(
types_with_intents,
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
}
TEST_F(UserDataAuthExTest, ListAuthFactorsUserIsEphemeralWithVerifier) {
EXPECT_CALL(platform_, DirectoryExists(_)).WillOnce(Return(false));
// Add a mount (and user session) for the ephemeral user.
SetupMount("foo@example.com");
EXPECT_CALL(*session_, IsEphemeral()).WillRepeatedly(Return(true));
session_->AddCredentialVerifier(std::make_unique<MockCredentialVerifier>(
AuthFactorType::kPassword, "password-label",
AuthFactorMetadata{.metadata = auth_factor::PasswordMetadata()}));
user_data_auth::ListAuthFactorsRequest list_request;
list_request.mutable_account_id()->set_account_id("foo@example.com");
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
const user_data_auth::ListAuthFactorsReply& list_reply =
list_reply_future.Get();
EXPECT_EQ(list_reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
ASSERT_EQ(list_reply.configured_auth_factors_with_status_size(), 1);
EXPECT_EQ(
list_reply.configured_auth_factors_with_status(0).auth_factor().type(),
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
EXPECT_EQ(
list_reply.configured_auth_factors_with_status(0).auth_factor().label(),
"password-label");
EXPECT_TRUE(list_reply.configured_auth_factors_with_status(0)
.auth_factor()
.has_password_metadata());
EXPECT_TRUE(list_reply.configured_auth_factors_with_status(0)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(list_reply.configured_auth_factors_with_status(0)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_NONE);
EXPECT_THAT(
list_reply.supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
std::vector<user_data_auth::AuthFactorType> types_with_intents;
for (const auto& intents_for_type : list_reply.auth_intents_for_types()) {
types_with_intents.push_back(intents_for_type.type());
EXPECT_THAT(intents_for_type.current(),
UnorderedElementsAre(user_data_auth::AUTH_INTENT_VERIFY_ONLY));
}
EXPECT_THAT(
types_with_intents,
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
}
TEST_F(UserDataAuthExTest, ListAuthFactorsUserExistsWithoutPinweaver) {
EXPECT_CALL(platform_, DirectoryExists(_)).WillOnce(Return(true));
user_data_auth::ListAuthFactorsRequest list_request;
list_request.mutable_account_id()->set_account_id("foo@example.com");
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
const user_data_auth::ListAuthFactorsReply& list_reply =
list_reply_future.Get();
EXPECT_EQ(list_reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(list_reply.configured_auth_factors_with_status(), IsEmpty());
EXPECT_THAT(
list_reply.supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY,
user_data_auth::AUTH_FACTOR_TYPE_KIOSK,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
}
TEST_F(UserDataAuthExTest, ListAuthFactorsUserExistsWithPinweaver) {
EXPECT_CALL(platform_, DirectoryExists(_)).WillOnce(Return(true));
EXPECT_CALL(hwsec_, IsPinWeaverEnabled()).WillRepeatedly(ReturnValue(true));
user_data_auth::ListAuthFactorsRequest list_request;
list_request.mutable_account_id()->set_account_id("foo@example.com");
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
const user_data_auth::ListAuthFactorsReply& list_reply =
list_reply_future.Get();
EXPECT_EQ(list_reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(list_reply.configured_auth_factors_with_status(), IsEmpty());
EXPECT_THAT(
list_reply.supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_PIN,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY,
user_data_auth::AUTH_FACTOR_TYPE_KIOSK,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
}
TEST_F(UserDataAuthExTest,
ListAuthFactorsUserExistsWithNoFactorsButUssEnabled) {
SetUserSecretStashExperimentForTesting(true);
EXPECT_CALL(platform_, DirectoryExists(_)).WillOnce(Return(true));
EXPECT_CALL(hwsec_, IsPinWeaverEnabled()).WillRepeatedly(ReturnValue(true));
user_data_auth::ListAuthFactorsRequest list_request;
list_request.mutable_account_id()->set_account_id("foo@example.com");
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
const user_data_auth::ListAuthFactorsReply& list_reply =
list_reply_future.Get();
EXPECT_EQ(list_reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(list_reply.configured_auth_factors_with_status(), IsEmpty());
EXPECT_THAT(
list_reply.supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_PIN,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY,
user_data_auth::AUTH_FACTOR_TYPE_KIOSK,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
SetUserSecretStashExperimentForTesting(std::nullopt);
}
TEST_F(UserDataAuthExTest, ListAuthFactorsUserExistsWithFactorsFromVks) {
const Username kUser("foo@example.com");
const ObfuscatedUsername kObfuscatedUser = SanitizeUserName(kUser);
EXPECT_CALL(platform_, DirectoryExists(_)).WillOnce(Return(true));
// Set up mocks for a few of VKs. We deliberately have the second not work to
// test that the listing correctly skips it.
std::vector<int> vk_indicies = {0, 1, 2};
EXPECT_CALL(keyset_management_, GetVaultKeysets(kObfuscatedUser, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(vk_indicies), Return(true)));
EXPECT_CALL(keyset_management_, LoadVaultKeysetForUser(kObfuscatedUser, 0))
.WillRepeatedly([](const ObfuscatedUsername&, int) {
auto vk = std::make_unique<VaultKeyset>();
vk->SetFlags(SerializedVaultKeyset::TPM_WRAPPED |
SerializedVaultKeyset::PCR_BOUND);
KeyData key_data;
key_data.set_type(KeyData::KEY_TYPE_PASSWORD);
key_data.set_label("password-label");
vk->SetKeyData(key_data);
vk->SetTPMKey(SecureBlob("fake tpm key"));
vk->SetExtendedTPMKey(SecureBlob("fake extended tpm key"));
return vk;
});
EXPECT_CALL(keyset_management_, LoadVaultKeysetForUser(kObfuscatedUser, 1))
.WillRepeatedly([](const ObfuscatedUsername&, int) { return nullptr; });
EXPECT_CALL(keyset_management_, LoadVaultKeysetForUser(kObfuscatedUser, 2))
.WillRepeatedly([](const ObfuscatedUsername&, int) {
auto vk = std::make_unique<VaultKeyset>();
vk->SetFlags(SerializedVaultKeyset::SCRYPT_WRAPPED);
KeyData key_data;
key_data.set_type(KeyData::KEY_TYPE_PASSWORD);
key_data.set_label("password-scrypt-label");
vk->SetKeyData(key_data);
const brillo::Blob kScryptPlaintext =
brillo::BlobFromString("plaintext");
const auto blob_to_encrypt = brillo::SecureBlob(brillo::CombineBlobs(
{kScryptPlaintext, hwsec_foundation::Sha1(kScryptPlaintext)}));
brillo::SecureBlob wrapped_keyset;
brillo::SecureBlob wrapped_chaps_key;
brillo::SecureBlob wrapped_reset_seed;
brillo::SecureBlob derived_key = {
0x67, 0xeb, 0xcd, 0x84, 0x49, 0x5e, 0xa2, 0xf3, 0xb1, 0xe6, 0xe7,
0x5b, 0x13, 0xb9, 0x16, 0x2f, 0x5a, 0x39, 0xc8, 0xfe, 0x6a, 0x60,
0xd4, 0x7a, 0xd8, 0x2b, 0x44, 0xc4, 0x45, 0x53, 0x1a, 0x85, 0x4a,
0x97, 0x9f, 0x2d, 0x06, 0xf5, 0xd0, 0xd3, 0xa6, 0xe7, 0xac, 0x9b,
0x02, 0xaf, 0x3c, 0x08, 0xce, 0x43, 0x46, 0x32, 0x6d, 0xd7, 0x2b,
0xe9, 0xdf, 0x8b, 0x38, 0x0e, 0x60, 0x3d, 0x64, 0x12};
brillo::SecureBlob scrypt_salt = brillo::SecureBlob("salt");
brillo::SecureBlob chaps_salt = brillo::SecureBlob("chaps_salt");
brillo::SecureBlob reset_seed_salt =
brillo::SecureBlob("reset_seed_salt");
scrypt_salt.resize(hwsec_foundation::kLibScryptSaltSize);
chaps_salt.resize(hwsec_foundation::kLibScryptSaltSize);
reset_seed_salt.resize(hwsec_foundation::kLibScryptSaltSize);
if (hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, scrypt_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_keyset)) {
vk->SetWrappedKeyset(wrapped_keyset);
}
if (hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, chaps_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_chaps_key)) {
vk->SetWrappedChapsKey(wrapped_chaps_key);
}
if (hwsec_foundation::LibScryptCompat::Encrypt(
derived_key, reset_seed_salt, blob_to_encrypt,
hwsec_foundation::kDefaultScryptParams, &wrapped_reset_seed)) {
vk->SetWrappedResetSeed(wrapped_reset_seed);
}
return vk;
});
user_data_auth::ListAuthFactorsRequest list_request;
list_request.mutable_account_id()->set_account_id(*kUser);
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
const user_data_auth::ListAuthFactorsReply& list_reply =
list_reply_future.Get();
EXPECT_EQ(list_reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
ASSERT_EQ(list_reply.configured_auth_factors_with_status_size(), 2);
EXPECT_EQ(
list_reply.configured_auth_factors_with_status(0).auth_factor().label(),
"password-label");
EXPECT_TRUE(list_reply.configured_auth_factors_with_status(0)
.auth_factor()
.has_password_metadata());
EXPECT_TRUE(list_reply.configured_auth_factors_with_status(0)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(list_reply.configured_auth_factors_with_status(0)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_NONE);
EXPECT_EQ(
list_reply.configured_auth_factors_with_status(1).auth_factor().label(),
"password-scrypt-label");
EXPECT_TRUE(list_reply.configured_auth_factors_with_status(1)
.auth_factor()
.has_password_metadata());
EXPECT_TRUE(list_reply.configured_auth_factors_with_status(1)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(list_reply.configured_auth_factors_with_status(1)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_NONE);
EXPECT_THAT(
list_reply.supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
}
TEST_F(UserDataAuthExTest, ListAuthFactorsWithFactorsFromUss) {
const Username kUser("foo@example.com");
const ObfuscatedUsername kObfuscatedUser = SanitizeUserName(kUser);
AuthFactorManager manager(&platform_);
userdataauth_->set_auth_factor_manager_for_testing(&manager);
SetUserSecretStashExperimentForTesting(false);
EXPECT_CALL(hwsec_, IsPinWeaverEnabled()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true));
// Set up standard list auth factor parameters, we'll be calling this a few
// times during the test.
user_data_auth::ListAuthFactorsRequest list_request;
list_request.mutable_account_id()->set_account_id(*kUser);
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future_1;
// List all the auth factors, there should be none at the start.
userdataauth_->ListAuthFactors(
list_request,
list_reply_future_1
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
EXPECT_EQ(list_reply_future_1.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(list_reply_future_1.Get().configured_auth_factors_with_status(),
IsEmpty());
EXPECT_THAT(
list_reply_future_1.Get().supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_PIN,
user_data_auth::AUTH_FACTOR_TYPE_KIOSK,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
// Add uss auth factors, we should be able to list them.
auto password_factor = std::make_unique<AuthFactor>(
AuthFactorType::kPassword, "password-label",
AuthFactorMetadata{.metadata = auth_factor::PasswordMetadata()},
AuthBlockState{
.state = TpmBoundToPcrAuthBlockState{
.scrypt_derived = false,
.salt = SecureBlob("fake salt"),
.tpm_key = SecureBlob("fake tpm key"),
.extended_tpm_key = SecureBlob("fake extended tpm key"),
.tpm_public_key_hash = SecureBlob("fake tpm public key hash"),
}});
ASSERT_THAT(manager.SaveAuthFactorFile(kObfuscatedUser, *password_factor),
IsOk());
auto pin_factor = std::make_unique<AuthFactor>(
AuthFactorType::kPin, "pin-label",
AuthFactorMetadata{.metadata = auth_factor::PinMetadata()},
AuthBlockState{.state = PinWeaverAuthBlockState{
.le_label = 0xbaadf00d,
.salt = SecureBlob("fake salt"),
.chaps_iv = SecureBlob("fake chaps IV"),
.fek_iv = SecureBlob("fake file encryption IV"),
.reset_salt = SecureBlob("more fake salt"),
}});
ASSERT_THAT(manager.SaveAuthFactorFile(kObfuscatedUser, *pin_factor), IsOk());
MakeUssWithLabels(kObfuscatedUser, {"password-label", "pin-label"});
// ListAuthFactors() load the factors according to the USS experiment status.
SetUserSecretStashExperimentForTesting(true);
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future_2;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future_2
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
user_data_auth::ListAuthFactorsReply list_reply_2 =
list_reply_future_2.Take();
EXPECT_EQ(list_reply_2.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::sort(
list_reply_2.mutable_configured_auth_factors_with_status()
->pointer_begin(),
list_reply_2.mutable_configured_auth_factors_with_status()->pointer_end(),
[](const user_data_auth::AuthFactorWithStatus* lhs,
const user_data_auth::AuthFactorWithStatus* rhs) {
return lhs->auth_factor().label() < rhs->auth_factor().label();
});
ASSERT_EQ(list_reply_2.configured_auth_factors_with_status_size(), 2);
EXPECT_EQ(
list_reply_2.configured_auth_factors_with_status(0).auth_factor().label(),
"password-label");
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.has_password_metadata());
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_NONE);
EXPECT_EQ(
list_reply_2.configured_auth_factors_with_status(1).auth_factor().label(),
"pin-label");
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(1)
.auth_factor()
.has_pin_metadata());
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(1)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(list_reply_2.configured_auth_factors_with_status(1)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_ATTEMPT_LIMITED);
EXPECT_THAT(
list_reply_2.supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_PIN,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
// Remove an auth factor, we should still be able to list the remaining one.
TestFuture<CryptohomeStatus> remove_result;
manager.RemoveAuthFactor(kObfuscatedUser, *pin_factor, &auth_block_utility_,
remove_result.GetCallback());
EXPECT_THAT(remove_result.Take(), IsOk());
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future_3;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future_3
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
const user_data_auth::ListAuthFactorsReply& list_reply_3 =
list_reply_future_3.Get();
EXPECT_EQ(list_reply_3.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
ASSERT_EQ(list_reply_3.configured_auth_factors_with_status_size(), 1);
EXPECT_EQ(
list_reply_3.configured_auth_factors_with_status(0).auth_factor().label(),
"password-label");
EXPECT_TRUE(list_reply_3.configured_auth_factors_with_status(0)
.auth_factor()
.has_password_metadata());
EXPECT_TRUE(list_reply_3.configured_auth_factors_with_status(0)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(list_reply_3.configured_auth_factors_with_status(0)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_NONE);
EXPECT_THAT(
list_reply_3.supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_PIN,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
ResetUserSecretStashExperimentForTesting();
}
TEST_F(UserDataAuthExTest, ListAuthFactorsWithIncompleteFactorsFromUss) {
const Username kUser("foo@example.com");
const ObfuscatedUsername kObfuscatedUser = SanitizeUserName(kUser);
AuthFactorManager manager(&platform_);
userdataauth_->set_auth_factor_manager_for_testing(&manager);
SetUserSecretStashExperimentForTesting(false);
EXPECT_CALL(hwsec_, IsPinWeaverEnabled()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true));
// Set up standard list auth factor parameters, we'll be calling this a few
// times during the test.
user_data_auth::ListAuthFactorsRequest list_request;
list_request.mutable_account_id()->set_account_id(*kUser);
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future_1;
// List all the auth factors, there should be none at the start.
userdataauth_->ListAuthFactors(
list_request,
list_reply_future_1
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
EXPECT_EQ(list_reply_future_1.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(list_reply_future_1.Get().configured_auth_factors_with_status(),
IsEmpty());
EXPECT_THAT(
list_reply_future_1.Get().supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_PIN,
user_data_auth::AUTH_FACTOR_TYPE_KIOSK,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
// Add uss auth factors, but with only one of them having both the auth factor
// and USS components of the fact. Only the complete one should work.
auto password_factor = std::make_unique<AuthFactor>(
AuthFactorType::kPassword, "password-label",
AuthFactorMetadata{.metadata = auth_factor::PasswordMetadata()},
AuthBlockState{
.state = TpmBoundToPcrAuthBlockState{
.scrypt_derived = false,
.salt = SecureBlob("fake salt"),
.tpm_key = SecureBlob("fake tpm key"),
.extended_tpm_key = SecureBlob("fake extended tpm key"),
.tpm_public_key_hash = SecureBlob("fake tpm public key hash"),
}});
ASSERT_THAT(manager.SaveAuthFactorFile(kObfuscatedUser, *password_factor),
IsOk());
auto pin_factor = std::make_unique<AuthFactor>(
AuthFactorType::kPin, "pin-label",
AuthFactorMetadata{.metadata = auth_factor::PinMetadata()},
AuthBlockState{.state = PinWeaverAuthBlockState{
.le_label = 0xbaadf00d,
.salt = SecureBlob("fake salt"),
.chaps_iv = SecureBlob("fake chaps IV"),
.fek_iv = SecureBlob("fake file encryption IV"),
.reset_salt = SecureBlob("more fake salt"),
}});
ASSERT_THAT(manager.SaveAuthFactorFile(kObfuscatedUser, *pin_factor), IsOk());
MakeUssWithLabels(kObfuscatedUser, {"password-label"});
// ListAuthFactors() should just list the single complete factor.
SetUserSecretStashExperimentForTesting(true);
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future_2;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future_2
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
user_data_auth::ListAuthFactorsReply list_reply_2 =
list_reply_future_2.Take();
EXPECT_EQ(list_reply_2.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::sort(
list_reply_2.mutable_configured_auth_factors_with_status()
->pointer_begin(),
list_reply_2.mutable_configured_auth_factors_with_status()->pointer_end(),
[](const user_data_auth::AuthFactorWithStatus* lhs,
const user_data_auth::AuthFactorWithStatus* rhs) {
return lhs->auth_factor().label() < rhs->auth_factor().label();
});
ASSERT_EQ(list_reply_2.configured_auth_factors_with_status_size(), 1);
EXPECT_EQ(
list_reply_2.configured_auth_factors_with_status(0).auth_factor().label(),
"password-label");
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.has_password_metadata());
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_NONE);
EXPECT_THAT(
list_reply_2.supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_PIN,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
}
TEST_F(UserDataAuthExTest, StartAuthSessionPinLockedLegacy) {
// Setup.
const Username kUser("foo@example.com");
const ObfuscatedUsername kObfuscatedUser = SanitizeUserName(kUser);
AuthFactorManager manager(&platform_);
auto mock_le_manager = std::make_unique<MockLECredentialManager>();
MockLECredentialManager* mock_le_manager_ptr = mock_le_manager.get();
SetUserSecretStashExperimentForTesting(true);
userdataauth_->set_auth_factor_manager_for_testing(&manager);
crypto_.set_le_manager_for_testing(std::move(mock_le_manager));
EXPECT_CALL(hwsec_, IsPinWeaverEnabled()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(false));
// Set up standard start authsession parameters, we'll be calling this a few
// times during the test.
user_data_auth::StartAuthSessionRequest start_request;
start_request.mutable_account_id()->set_account_id(*kUser);
TestFuture<user_data_auth::StartAuthSessionReply> start_reply_future_1;
// List all the auth factors, there should be none at the start.
userdataauth_->StartAuthSession(
start_request,
start_reply_future_1
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(start_reply_future_1.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(start_reply_future_1.Get().configured_auth_factors_with_status(),
IsEmpty());
EXPECT_THAT(start_reply_future_1.Get().user_exists(), false);
// Now that we are starting to save AuthFactors, let's assume user exists.
EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true));
// Add uss auth factors, we should be able to list them.
auto password_factor = std::make_unique<AuthFactor>(
AuthFactorType::kPassword, "password-label",
AuthFactorMetadata{.metadata = auth_factor::PasswordMetadata()},
AuthBlockState{
.state = TpmBoundToPcrAuthBlockState{
.scrypt_derived = false,
.salt = SecureBlob("fake salt"),
.tpm_key = SecureBlob("fake tpm key"),
.extended_tpm_key = SecureBlob("fake extended tpm key"),
.tpm_public_key_hash = SecureBlob("fake tpm public key hash"),
}});
ASSERT_THAT(manager.SaveAuthFactorFile(kObfuscatedUser, *password_factor),
IsOk());
auto pin_factor = std::make_unique<AuthFactor>(
AuthFactorType::kPin, "pin-label",
AuthFactorMetadata{.metadata = auth_factor::PinMetadata()},
AuthBlockState{.state = PinWeaverAuthBlockState{
.le_label = 0xbaadf00d,
.salt = SecureBlob("fake salt"),
.chaps_iv = SecureBlob("fake chaps IV"),
.fek_iv = SecureBlob("fake file encryption IV"),
.reset_salt = SecureBlob("more fake salt"),
}});
ASSERT_THAT(manager.SaveAuthFactorFile(kObfuscatedUser, *pin_factor), IsOk());
MakeUssWithLabels(kObfuscatedUser, {"password-label", "pin-label"});
EXPECT_CALL(*mock_le_manager_ptr, GetDelayInSeconds).WillRepeatedly([](auto) {
return UINT32_MAX;
});
TestFuture<user_data_auth::StartAuthSessionReply> start_reply_future_2;
userdataauth_->StartAuthSession(
start_request,
start_reply_future_2
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
user_data_auth::StartAuthSessionReply start_reply =
start_reply_future_2.Take();
EXPECT_EQ(start_reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::sort(
start_reply.mutable_configured_auth_factors_with_status()
->pointer_begin(),
start_reply.mutable_configured_auth_factors_with_status()->pointer_end(),
[](const user_data_auth::AuthFactorWithStatus* lhs,
const user_data_auth::AuthFactorWithStatus* rhs) {
return lhs->auth_factor().label() < rhs->auth_factor().label();
});
ASSERT_EQ(start_reply.configured_auth_factors_with_status_size(), 2);
EXPECT_EQ(
start_reply.configured_auth_factors_with_status(0).auth_factor().label(),
"password-label");
EXPECT_TRUE(start_reply.configured_auth_factors_with_status(0)
.auth_factor()
.has_password_metadata());
EXPECT_TRUE(start_reply.configured_auth_factors_with_status(0)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(start_reply.configured_auth_factors_with_status(0)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_NONE);
EXPECT_EQ(
start_reply.configured_auth_factors_with_status(1).auth_factor().label(),
"pin-label");
EXPECT_TRUE(start_reply.configured_auth_factors_with_status(1)
.auth_factor()
.has_pin_metadata());
EXPECT_TRUE(start_reply.configured_auth_factors_with_status(1)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(start_reply.configured_auth_factors_with_status(1)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_ATTEMPT_LIMITED);
EXPECT_TRUE(
start_reply.configured_auth_factors_with_status(1).has_status_info());
EXPECT_EQ(start_reply.configured_auth_factors_with_status(1)
.status_info()
.time_available_in(),
std::numeric_limits<uint64_t>::max());
EXPECT_TRUE(start_reply.user_exists());
ResetUserSecretStashExperimentForTesting();
}
TEST_F(UserDataAuthExTest, StartAuthSessionPinLockedModern) {
// Setup.
const Username kUser("foo@example.com");
const ObfuscatedUsername kObfuscatedUser = SanitizeUserName(kUser);
AuthFactorManager manager(&platform_);
auto mock_le_manager = std::make_unique<MockLECredentialManager>();
MockLECredentialManager* mock_le_manager_ptr = mock_le_manager.get();
SetUserSecretStashExperimentForTesting(true);
userdataauth_->set_auth_factor_manager_for_testing(&manager);
crypto_.set_le_manager_for_testing(std::move(mock_le_manager));
features_.SetDefaultForFeature(Features::kModernPin, true);
EXPECT_CALL(hwsec_, IsPinWeaverEnabled()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(false));
// Set up standard start authsession parameters, we'll be calling this a few
// times during the test.
user_data_auth::StartAuthSessionRequest start_request;
start_request.mutable_account_id()->set_account_id(*kUser);
TestFuture<user_data_auth::StartAuthSessionReply> start_reply_future_1;
// List all the auth factors, there should be none at the start.
userdataauth_->StartAuthSession(
start_request,
start_reply_future_1
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(start_reply_future_1.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(start_reply_future_1.Get().configured_auth_factors_with_status(),
IsEmpty());
EXPECT_THAT(start_reply_future_1.Get().user_exists(), false);
// Now that we are starting to save AuthFactors, let's assume user exists.
EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true));
// Add uss auth factors, we should be able to list them.
// Add uss auth factors, we should be able to list them.
auto password_factor = std::make_unique<AuthFactor>(
AuthFactorType::kPassword, "password-label",
AuthFactorMetadata{.metadata = auth_factor::PasswordMetadata()},
AuthBlockState{
.state = TpmBoundToPcrAuthBlockState{
.scrypt_derived = false,
.salt = SecureBlob("fake salt"),
.tpm_key = SecureBlob("fake tpm key"),
.extended_tpm_key = SecureBlob("fake extended tpm key"),
.tpm_public_key_hash = SecureBlob("fake tpm public key hash"),
}});
ASSERT_THAT(manager.SaveAuthFactorFile(kObfuscatedUser, *password_factor),
IsOk());
auto pin_factor = std::make_unique<AuthFactor>(
AuthFactorType::kPin, "pin-label",
AuthFactorMetadata{
.common =
auth_factor::CommonMetadata{
.lockout_policy = auth_factor::LockoutPolicy::TIME_LIMITED},
.metadata = auth_factor::PinMetadata()},
AuthBlockState{.state = PinWeaverAuthBlockState{
.le_label = 0xbaadf00d,
.salt = SecureBlob("fake salt"),
.chaps_iv = SecureBlob("fake chaps IV"),
.fek_iv = SecureBlob("fake file encryption IV"),
.reset_salt = SecureBlob("more fake salt"),
}});
ASSERT_THAT(manager.SaveAuthFactorFile(kObfuscatedUser, *pin_factor), IsOk());
MakeUssWithLabels(kObfuscatedUser, {"password-label", "pin-label"});
EXPECT_CALL(*mock_le_manager_ptr, GetDelayInSeconds).WillRepeatedly([](auto) {
return 30;
});
TestFuture<user_data_auth::StartAuthSessionReply> start_reply_future_2;
userdataauth_->StartAuthSession(
start_request,
start_reply_future_2
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
user_data_auth::StartAuthSessionReply start_reply =
start_reply_future_2.Take();
EXPECT_EQ(start_reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::sort(
start_reply.mutable_configured_auth_factors_with_status()
->pointer_begin(),
start_reply.mutable_configured_auth_factors_with_status()->pointer_end(),
[](const user_data_auth::AuthFactorWithStatus* lhs,
const user_data_auth::AuthFactorWithStatus* rhs) {
return lhs->auth_factor().label() < rhs->auth_factor().label();
});
ASSERT_EQ(start_reply.configured_auth_factors_with_status_size(), 2);
EXPECT_EQ(
start_reply.configured_auth_factors_with_status(0).auth_factor().label(),
"password-label");
EXPECT_TRUE(start_reply.configured_auth_factors_with_status(0)
.auth_factor()
.has_password_metadata());
EXPECT_TRUE(start_reply.configured_auth_factors_with_status(0)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(start_reply.configured_auth_factors_with_status(0)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_NONE);
EXPECT_EQ(
start_reply.configured_auth_factors_with_status(1).auth_factor().label(),
"pin-label");
EXPECT_TRUE(start_reply.configured_auth_factors_with_status(1)
.auth_factor()
.has_pin_metadata());
EXPECT_TRUE(start_reply.configured_auth_factors_with_status(1)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(start_reply.configured_auth_factors_with_status(1)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_TIME_LIMITED);
EXPECT_TRUE(
start_reply.configured_auth_factors_with_status(1).has_status_info());
EXPECT_EQ(start_reply.configured_auth_factors_with_status(1)
.status_info()
.time_available_in(),
30000);
ResetUserSecretStashExperimentForTesting();
}
TEST_F(UserDataAuthExTest, ListAuthFactorsWithFactorsFromUssPinLockedLegacy) {
// Setup.
const Username kUser("foo@example.com");
const ObfuscatedUsername kObfuscatedUser = SanitizeUserName(kUser);
AuthFactorManager manager(&platform_);
auto mock_le_manager = std::make_unique<MockLECredentialManager>();
MockLECredentialManager* mock_le_manager_ptr = mock_le_manager.get();
SetUserSecretStashExperimentForTesting(true);
userdataauth_->set_auth_factor_manager_for_testing(&manager);
crypto_.set_le_manager_for_testing(std::move(mock_le_manager));
EXPECT_CALL(hwsec_, IsPinWeaverEnabled()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true));
// Set up standard list auth factor parameters, we'll be calling this a few
// times during the test.
user_data_auth::ListAuthFactorsRequest list_request;
list_request.mutable_account_id()->set_account_id(*kUser);
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future_1;
// List all the auth factors, there should be none at the start.
userdataauth_->ListAuthFactors(
list_request,
list_reply_future_1
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
EXPECT_EQ(list_reply_future_1.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(list_reply_future_1.Get().configured_auth_factors_with_status(),
IsEmpty());
EXPECT_THAT(list_reply_future_1.Get().supported_auth_factors(),
UnorderedElementsAre(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_PIN,
user_data_auth::AUTH_FACTOR_TYPE_KIOSK,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY));
// Add uss auth factors, we should be able to list them.
auto password_factor = std::make_unique<AuthFactor>(
AuthFactorType::kPassword, "password-label",
AuthFactorMetadata{.metadata = auth_factor::PasswordMetadata()},
AuthBlockState{
.state = TpmBoundToPcrAuthBlockState{
.scrypt_derived = false,
.salt = SecureBlob("fake salt"),
.tpm_key = SecureBlob("fake tpm key"),
.extended_tpm_key = SecureBlob("fake extended tpm key"),
.tpm_public_key_hash = SecureBlob("fake tpm public key hash"),
}});
ASSERT_THAT(manager.SaveAuthFactorFile(kObfuscatedUser, *password_factor),
IsOk());
auto pin_factor = std::make_unique<AuthFactor>(
AuthFactorType::kPin, "pin-label",
AuthFactorMetadata{.metadata = auth_factor::PinMetadata()},
AuthBlockState{.state = PinWeaverAuthBlockState{
.le_label = 0xbaadf00d,
.salt = SecureBlob("fake salt"),
.chaps_iv = SecureBlob("fake chaps IV"),
.fek_iv = SecureBlob("fake file encryption IV"),
.reset_salt = SecureBlob("more fake salt"),
}});
ASSERT_THAT(manager.SaveAuthFactorFile(kObfuscatedUser, *pin_factor), IsOk());
MakeUssWithLabels(kObfuscatedUser, {"password-label", "pin-label"});
EXPECT_CALL(*mock_le_manager_ptr, GetDelayInSeconds).WillRepeatedly([](auto) {
return UINT32_MAX;
});
// ListAuthFactors() load the factors according to the USS experiment status.
SetUserSecretStashExperimentForTesting(true);
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future_2;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future_2
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
user_data_auth::ListAuthFactorsReply list_reply_2 =
list_reply_future_2.Take();
EXPECT_EQ(list_reply_2.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::sort(
list_reply_2.mutable_configured_auth_factors_with_status()
->pointer_begin(),
list_reply_2.mutable_configured_auth_factors_with_status()->pointer_end(),
[](const user_data_auth::AuthFactorWithStatus* lhs,
const user_data_auth::AuthFactorWithStatus* rhs) {
return lhs->auth_factor().label() < rhs->auth_factor().label();
});
ASSERT_EQ(list_reply_2.configured_auth_factors_with_status_size(), 2);
EXPECT_EQ(
list_reply_2.configured_auth_factors_with_status(0).auth_factor().label(),
"password-label");
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.has_password_metadata());
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_NONE);
EXPECT_EQ(
list_reply_2.configured_auth_factors_with_status(1).auth_factor().label(),
"pin-label");
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(1)
.auth_factor()
.has_pin_metadata());
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(1)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(list_reply_2.configured_auth_factors_with_status(1)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_ATTEMPT_LIMITED);
EXPECT_TRUE(
list_reply_2.configured_auth_factors_with_status(1).has_status_info());
EXPECT_EQ(list_reply_2.configured_auth_factors_with_status(1)
.status_info()
.time_available_in(),
std::numeric_limits<uint64_t>::max());
EXPECT_THAT(
list_reply_2.supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_PIN,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
ResetUserSecretStashExperimentForTesting();
}
TEST_F(UserDataAuthExTest, ListAuthFactorsWithFactorsFromUssPinLockedModern) {
// Setup.
const Username kUser("foo@example.com");
const ObfuscatedUsername kObfuscatedUser = SanitizeUserName(kUser);
auto mock_le_manager = std::make_unique<MockLECredentialManager>();
MockLECredentialManager* mock_le_manager_ptr = mock_le_manager.get();
AuthFactorManager manager(&platform_);
SetUserSecretStashExperimentForTesting(true);
userdataauth_->set_auth_factor_manager_for_testing(&manager);
crypto_.set_le_manager_for_testing(std::move(mock_le_manager));
features_.SetDefaultForFeature(Features::kModernPin, true);
EXPECT_CALL(hwsec_, IsPinWeaverEnabled()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true));
// Set up standard list auth factor parameters, we'll be calling this a few
// times during the test.
user_data_auth::ListAuthFactorsRequest list_request;
list_request.mutable_account_id()->set_account_id(*kUser);
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future_1;
// List all the auth factors, there should be none at the start.
userdataauth_->ListAuthFactors(
list_request,
list_reply_future_1
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
EXPECT_EQ(list_reply_future_1.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(list_reply_future_1.Get().configured_auth_factors_with_status(),
IsEmpty());
EXPECT_THAT(list_reply_future_1.Get().supported_auth_factors(),
UnorderedElementsAre(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_PIN,
user_data_auth::AUTH_FACTOR_TYPE_KIOSK,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY));
// Add uss auth factors, we should be able to list them.
auto password_factor = std::make_unique<AuthFactor>(
AuthFactorType::kPassword, "password-label",
AuthFactorMetadata{.metadata = auth_factor::PasswordMetadata()},
AuthBlockState{
.state = TpmBoundToPcrAuthBlockState{
.scrypt_derived = false,
.salt = SecureBlob("fake salt"),
.tpm_key = SecureBlob("fake tpm key"),
.extended_tpm_key = SecureBlob("fake extended tpm key"),
.tpm_public_key_hash = SecureBlob("fake tpm public key hash"),
}});
ASSERT_THAT(manager.SaveAuthFactorFile(kObfuscatedUser, *password_factor),
IsOk());
auto pin_factor = std::make_unique<AuthFactor>(
AuthFactorType::kPin, "pin-label",
AuthFactorMetadata{
.common =
auth_factor::CommonMetadata{
.lockout_policy = auth_factor::LockoutPolicy::TIME_LIMITED},
.metadata = auth_factor::PinMetadata()},
AuthBlockState{.state = PinWeaverAuthBlockState{
.le_label = 0xbaadf00d,
.salt = SecureBlob("fake salt"),
.chaps_iv = SecureBlob("fake chaps IV"),
.fek_iv = SecureBlob("fake file encryption IV"),
.reset_salt = SecureBlob("more fake salt"),
}});
ASSERT_THAT(manager.SaveAuthFactorFile(kObfuscatedUser, *pin_factor), IsOk());
MakeUssWithLabels(kObfuscatedUser, {"password-label", "pin-label"});
EXPECT_CALL(*mock_le_manager_ptr, GetDelayInSeconds).WillRepeatedly([](auto) {
return 30;
});
// ListAuthFactors() load the factors according to the USS experiment status.
SetUserSecretStashExperimentForTesting(true);
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future_2;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future_2
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
user_data_auth::ListAuthFactorsReply list_reply_2 =
list_reply_future_2.Take();
EXPECT_EQ(list_reply_2.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::sort(
list_reply_2.mutable_configured_auth_factors_with_status()
->pointer_begin(),
list_reply_2.mutable_configured_auth_factors_with_status()->pointer_end(),
[](const user_data_auth::AuthFactorWithStatus* lhs,
const user_data_auth::AuthFactorWithStatus* rhs) {
return lhs->auth_factor().label() < rhs->auth_factor().label();
});
ASSERT_EQ(list_reply_2.configured_auth_factors_with_status_size(), 2);
EXPECT_EQ(
list_reply_2.configured_auth_factors_with_status(0).auth_factor().label(),
"password-label");
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.has_password_metadata());
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_NONE);
EXPECT_EQ(
list_reply_2.configured_auth_factors_with_status(1).auth_factor().label(),
"pin-label");
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(1)
.auth_factor()
.has_pin_metadata());
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(1)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(list_reply_2.configured_auth_factors_with_status(1)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_TIME_LIMITED);
EXPECT_TRUE(
list_reply_2.configured_auth_factors_with_status(1).has_status_info());
EXPECT_EQ(list_reply_2.configured_auth_factors_with_status(1)
.status_info()
.time_available_in(),
30000);
EXPECT_THAT(
list_reply_2.supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_PIN,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
ResetUserSecretStashExperimentForTesting();
}
TEST_F(UserDataAuthExTest, ListAuthFactorsWithFactorsFromUssAndVk) {
const Username kUser("foo@example.com");
const ObfuscatedUsername kObfuscatedUser = SanitizeUserName(kUser);
AuthFactorManager manager(&platform_);
userdataauth_->set_auth_factor_manager_for_testing(&manager);
SetUserSecretStashExperimentForTesting(false);
EXPECT_CALL(hwsec_, IsPinWeaverEnabled()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(platform_, DirectoryExists(_)).WillRepeatedly(Return(true));
// Set up standard list auth factor parameters, we'll be calling this a few
// times during the test.
user_data_auth::ListAuthFactorsRequest list_request;
list_request.mutable_account_id()->set_account_id(*kUser);
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future_1;
// List all the auth factors, there should be none at the start.
userdataauth_->ListAuthFactors(
list_request,
list_reply_future_1
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
EXPECT_EQ(list_reply_future_1.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(list_reply_future_1.Get().configured_auth_factors_with_status(),
IsEmpty());
EXPECT_THAT(
list_reply_future_1.Get().supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_PIN,
user_data_auth::AUTH_FACTOR_TYPE_KIOSK,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
// Set up mocks for a VK.
std::vector<int> vk_indice = {0};
EXPECT_CALL(keyset_management_, GetVaultKeysets(kObfuscatedUser, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(vk_indice), Return(true)));
EXPECT_CALL(keyset_management_, LoadVaultKeysetForUser(kObfuscatedUser, 0))
.WillRepeatedly([](const ObfuscatedUsername&, int) {
auto vk = std::make_unique<VaultKeyset>();
vk->SetFlags(SerializedVaultKeyset::TPM_WRAPPED |
SerializedVaultKeyset::PCR_BOUND);
KeyData key_data;
key_data.set_type(KeyData::KEY_TYPE_PASSWORD);
key_data.set_label("password-label");
vk->SetKeyData(key_data);
vk->SetTPMKey(SecureBlob("fake tpm key"));
vk->SetExtendedTPMKey(SecureBlob("fake extended tpm key"));
return vk;
});
// Add an AuthFactor backed by USS.
auto pin_factor = std::make_unique<AuthFactor>(
AuthFactorType::kPin, "pin-label",
AuthFactorMetadata{.metadata = auth_factor::PinMetadata()},
AuthBlockState{.state = PinWeaverAuthBlockState{
.le_label = 0xbaadf00d,
.salt = SecureBlob("fake salt"),
.chaps_iv = SecureBlob("fake chaps IV"),
.fek_iv = SecureBlob("fake file encryption IV"),
.reset_salt = SecureBlob("more fake salt"),
}});
ASSERT_THAT(manager.SaveAuthFactorFile(kObfuscatedUser, *pin_factor), IsOk());
MakeUssWithLabels(kObfuscatedUser, {"pin-label"});
// ListAuthFactors() load the factors according to the USS experiment status.
SetUserSecretStashExperimentForTesting(true);
TestFuture<user_data_auth::ListAuthFactorsReply> list_reply_future_2;
userdataauth_->ListAuthFactors(
list_request,
list_reply_future_2
.GetCallback<const user_data_auth::ListAuthFactorsReply&>());
user_data_auth::ListAuthFactorsReply list_reply_2 =
list_reply_future_2.Take();
EXPECT_EQ(list_reply_2.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
std::sort(
list_reply_2.mutable_configured_auth_factors_with_status()
->pointer_begin(),
list_reply_2.mutable_configured_auth_factors_with_status()->pointer_end(),
[](const user_data_auth::AuthFactorWithStatus* lhs,
const user_data_auth::AuthFactorWithStatus* rhs) {
return lhs->auth_factor().label() < rhs->auth_factor().label();
});
ASSERT_EQ(list_reply_2.configured_auth_factors_with_status_size(), 2);
EXPECT_EQ(
list_reply_2.configured_auth_factors_with_status(0).auth_factor().label(),
"password-label");
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.has_password_metadata());
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(list_reply_2.configured_auth_factors_with_status(0)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_NONE);
EXPECT_EQ(
list_reply_2.configured_auth_factors_with_status(1).auth_factor().label(),
"pin-label");
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(1)
.auth_factor()
.has_pin_metadata());
EXPECT_TRUE(list_reply_2.configured_auth_factors_with_status(1)
.auth_factor()
.has_common_metadata());
EXPECT_EQ(list_reply_2.configured_auth_factors_with_status(1)
.auth_factor()
.common_metadata()
.lockout_policy(),
user_data_auth::LOCKOUT_POLICY_ATTEMPT_LIMITED);
EXPECT_THAT(
list_reply_2.supported_auth_factors(),
UnorderedElementsAre(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD,
user_data_auth::AUTH_FACTOR_TYPE_PIN,
user_data_auth::AUTH_FACTOR_TYPE_CRYPTOHOME_RECOVERY,
user_data_auth::AUTH_FACTOR_TYPE_SMART_CARD));
ResetUserSecretStashExperimentForTesting();
}
TEST_F(UserDataAuthExTest, PrepareAuthFactorFingerprintSuccess) {
// Setup.
PrepareArguments();
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
const std::string auth_session_id =
auth_session_reply_future.Get().auth_session_id();
EXPECT_TRUE(
AuthSession::GetTokenFromSerializedString(auth_session_id).has_value());
// Prepare the request and set up the mock components.
user_data_auth::PrepareAuthFactorRequest prepare_auth_factor_req;
prepare_auth_factor_req.set_auth_session_id(auth_session_id);
prepare_auth_factor_req.set_auth_factor_type(
user_data_auth::AUTH_FACTOR_TYPE_FINGERPRINT);
prepare_auth_factor_req.set_purpose(
user_data_auth::PURPOSE_AUTHENTICATE_AUTH_FACTOR);
EXPECT_CALL(*bio_processor_, StartAuthenticateSession(_, _))
.WillOnce([](auto&&, auto&& callback) { std::move(callback).Run(true); });
// Test.
TestFuture<user_data_auth::PrepareAuthFactorReply>
prepare_auth_factor_reply_future;
userdataauth_->PrepareAuthFactor(
prepare_auth_factor_req,
prepare_auth_factor_reply_future
.GetCallback<const user_data_auth::PrepareAuthFactorReply&>());
// Verify.
EXPECT_EQ(prepare_auth_factor_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
}
TEST_F(UserDataAuthExTest, PrepareAuthFactorFingerprintFailure) {
// Setup.
PrepareArguments();
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
const std::string auth_session_id =
auth_session_reply_future.Get().auth_session_id();
EXPECT_TRUE(
AuthSession::GetTokenFromSerializedString(auth_session_id).has_value());
// Prepare the request and set up the mock components.
user_data_auth::PrepareAuthFactorRequest prepare_auth_factor_req;
prepare_auth_factor_req.set_auth_session_id(auth_session_id);
prepare_auth_factor_req.set_auth_factor_type(
user_data_auth::AUTH_FACTOR_TYPE_FINGERPRINT);
prepare_auth_factor_req.set_purpose(
user_data_auth::PURPOSE_AUTHENTICATE_AUTH_FACTOR);
EXPECT_CALL(*bio_processor_, StartAuthenticateSession(_, _))
.WillOnce(
[](auto&&, auto&& callback) { std::move(callback).Run(false); });
// Test.
TestFuture<user_data_auth::PrepareAuthFactorReply>
prepare_auth_factor_reply_future;
userdataauth_->PrepareAuthFactor(
prepare_auth_factor_req,
prepare_auth_factor_reply_future
.GetCallback<const user_data_auth::PrepareAuthFactorReply&>());
// Verify.
EXPECT_EQ(prepare_auth_factor_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_FINGERPRINT_ERROR_INTERNAL);
}
TEST_F(UserDataAuthExTest, PrepareAuthFactorNoAuthSessionIdFailure) {
// Setup.
PrepareArguments();
// Prepare the request and set up the mock components.
user_data_auth::PrepareAuthFactorRequest prepare_auth_factor_req;
prepare_auth_factor_req.set_auth_factor_type(
user_data_auth::AUTH_FACTOR_TYPE_FINGERPRINT);
prepare_auth_factor_req.set_purpose(
user_data_auth::PURPOSE_AUTHENTICATE_AUTH_FACTOR);
// Test.
TestFuture<user_data_auth::PrepareAuthFactorReply>
prepare_auth_factor_reply_future;
userdataauth_->PrepareAuthFactor(
prepare_auth_factor_req,
prepare_auth_factor_reply_future
.GetCallback<const user_data_auth::PrepareAuthFactorReply&>());
// Verify.
EXPECT_EQ(prepare_auth_factor_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN);
}
TEST_F(UserDataAuthExTest, PrepareAuthFactorPasswordFailure) {
// Setup.
PrepareArguments();
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
const std::string auth_session_id =
auth_session_reply_future.Get().auth_session_id();
EXPECT_TRUE(
AuthSession::GetTokenFromSerializedString(auth_session_id).has_value());
// Prepare the request and set up the mock components.
user_data_auth::PrepareAuthFactorRequest prepare_auth_factor_req;
prepare_auth_factor_req.set_auth_session_id(auth_session_id);
prepare_auth_factor_req.set_auth_factor_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
prepare_auth_factor_req.set_purpose(
user_data_auth::PURPOSE_AUTHENTICATE_AUTH_FACTOR);
// Test.
TestFuture<user_data_auth::PrepareAuthFactorReply>
prepare_auth_factor_reply_future;
userdataauth_->PrepareAuthFactor(
prepare_auth_factor_req,
prepare_auth_factor_reply_future
.GetCallback<const user_data_auth::PrepareAuthFactorReply&>());
// Verify.
EXPECT_EQ(prepare_auth_factor_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
}
TEST_F(UserDataAuthExTest, TerminateAuthFactorFingerprintSuccess) {
// Setup.
PrepareArguments();
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
const std::string auth_session_id =
auth_session_reply_future.Get().auth_session_id();
EXPECT_TRUE(
AuthSession::GetTokenFromSerializedString(auth_session_id).has_value());
// Execute a successful PrepareAuthFactor with mocked response.
user_data_auth::PrepareAuthFactorRequest prepare_auth_factor_req;
prepare_auth_factor_req.set_auth_session_id(auth_session_id);
prepare_auth_factor_req.set_auth_factor_type(
user_data_auth::AUTH_FACTOR_TYPE_FINGERPRINT);
prepare_auth_factor_req.set_purpose(
user_data_auth::PURPOSE_AUTHENTICATE_AUTH_FACTOR);
EXPECT_CALL(*bio_processor_, StartAuthenticateSession(_, _))
.WillOnce([](auto&&, auto&& callback) { std::move(callback).Run(true); });
TestFuture<user_data_auth::PrepareAuthFactorReply>
prepare_auth_factor_reply_future;
userdataauth_->PrepareAuthFactor(
prepare_auth_factor_req,
prepare_auth_factor_reply_future
.GetCallback<const user_data_auth::PrepareAuthFactorReply&>());
EXPECT_EQ(prepare_auth_factor_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Test.
user_data_auth::TerminateAuthFactorRequest terminate_auth_factor_req;
terminate_auth_factor_req.set_auth_session_id(auth_session_id);
terminate_auth_factor_req.set_auth_factor_type(
user_data_auth::AUTH_FACTOR_TYPE_FINGERPRINT);
TestFuture<user_data_auth::TerminateAuthFactorReply>
terminate_auth_factor_reply_future;
userdataauth_->TerminateAuthFactor(
terminate_auth_factor_req,
terminate_auth_factor_reply_future
.GetCallback<const user_data_auth::TerminateAuthFactorReply&>());
// Verify.
EXPECT_EQ(terminate_auth_factor_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
}
TEST_F(UserDataAuthExTest, TerminateAuthFactorInactiveFactorFailure) {
// Setup.
PrepareArguments();
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
const std::string auth_session_id =
auth_session_reply_future.Get().auth_session_id();
EXPECT_TRUE(
AuthSession::GetTokenFromSerializedString(auth_session_id).has_value());
// Test. TerminateAuthFactor fails when there is
// no pending fingerprint auth factor to be terminated.
user_data_auth::TerminateAuthFactorRequest terminate_auth_factor_req;
terminate_auth_factor_req.set_auth_session_id(auth_session_id);
terminate_auth_factor_req.set_auth_factor_type(
user_data_auth::AUTH_FACTOR_TYPE_FINGERPRINT);
TestFuture<user_data_auth::TerminateAuthFactorReply>
terminate_auth_factor_reply_future;
userdataauth_->TerminateAuthFactor(
terminate_auth_factor_req,
terminate_auth_factor_reply_future
.GetCallback<const user_data_auth::TerminateAuthFactorReply&>());
// Verify.
EXPECT_EQ(terminate_auth_factor_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
}
TEST_F(UserDataAuthExTest, TerminateAuthFactorBadTypeFailure) {
// Setup.
PrepareArguments();
start_auth_session_req_->mutable_account_id()->set_account_id(
"foo@example.com");
TestFuture<user_data_auth::StartAuthSessionReply> auth_session_reply_future;
userdataauth_->StartAuthSession(
*start_auth_session_req_,
auth_session_reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
EXPECT_EQ(auth_session_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
const std::string auth_session_id =
auth_session_reply_future.Get().auth_session_id();
EXPECT_TRUE(
AuthSession::GetTokenFromSerializedString(auth_session_id).has_value());
// Test. TerminateAuthFactor fails when the auth factor type
// does not support PrepareAuthFactor.
user_data_auth::TerminateAuthFactorRequest terminate_auth_factor_req;
terminate_auth_factor_req.set_auth_session_id(auth_session_id);
terminate_auth_factor_req.set_auth_factor_type(
user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
TestFuture<user_data_auth::TerminateAuthFactorReply>
terminate_auth_factor_reply_future;
userdataauth_->TerminateAuthFactor(
terminate_auth_factor_req,
terminate_auth_factor_reply_future
.GetCallback<const user_data_auth::TerminateAuthFactorReply&>());
// Verify.
EXPECT_EQ(terminate_auth_factor_reply_future.Get().error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
}
// ================ Tests requiring fully threaded environment ================
// Test fixture that implements fully threaded environment in UserDataAuth.
// Note that this does not initialize |userdataauth_|.
class UserDataAuthTestThreaded : public UserDataAuthTestBase {
public:
UserDataAuthTestThreaded() : origin_thread_("origin_thread") {}
UserDataAuthTestThreaded(const UserDataAuthTestThreaded&) = delete;
UserDataAuthTestThreaded& operator=(const UserDataAuthTestThreaded&) = delete;
~UserDataAuthTestThreaded() override = default;
// Post a task to the origin thread, then wait for it to finish.
void PostToOriginAndBlock(base::OnceClosure task) {
base::WaitableEvent done(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
origin_thread_.task_runner()->PostTask(
FROM_HERE, base::BindOnce(
[](base::OnceClosure task, base::WaitableEvent* done) {
std::move(task).Run();
done->Signal();
},
std::move(task), base::Unretained(&done)));
done.Wait();
}
void SetUp() override {
origin_thread_.Start();
PostToOriginAndBlock(base::BindOnce(
&UserDataAuthTestThreaded::SetUpInOrigin, base::Unretained(this)));
}
void SetUpInOrigin() {
// Create the |userdataauth_| object.
userdataauth_ = std::make_unique<UserDataAuth>();
// Setup the usual stuff
UserDataAuthTestBase::SetUp();
}
void TearDown() override {
PostToOriginAndBlock(base::BindOnce(
&UserDataAuthTestThreaded::TearDownInOrigin, base::Unretained(this)));
origin_thread_.Stop();
}
void TearDownInOrigin() {
// Destruct the |userdataauth_| object.
userdataauth_.reset();
}
// Initialize |userdataauth_| in |origin_thread_|
void InitializeUserDataAuth() {
PostToOriginAndBlock(
base::BindOnce(base::IgnoreResult(&UserDataAuth::Initialize),
base::Unretained(userdataauth_.get()), mount_bus_));
}
protected:
// The thread on which the |userdataauth_| object is created. This is the same
// as |userdataauth_->origin_thread_|.
base::Thread origin_thread_;
};
TEST_F(UserDataAuthTestThreaded, DetectEnterpriseOwnership) {
// If asked, this machine is enterprise owned.
static const std::string true_str = "true";
brillo::Blob true_value(true_str.begin(), true_str.end());
true_value.push_back(0);
EXPECT_CALL(*attrs_, Get("enterprise.owned", _))
.WillOnce(DoAll(SetArgPointee<1>(true_value), Return(true)));
EXPECT_CALL(homedirs_, set_enterprise_owned(true)).WillOnce(Return());
InitializeUserDataAuth();
}
TEST_F(UserDataAuthTestThreaded, ShutdownTask) {
InitializeUserDataAuth();
EXPECT_CALL(*mount_bus_, ShutdownAndBlock()).Times(1);
PostToOriginAndBlock(base::BindLambdaForTesting([this]() {
// Destruct the |userdataauth_| object.
userdataauth_.reset();
}));
}
// ============== Full API Behaviour Test for Negative Testing ==============
// This section holds tests that simulate API calls so that we can test that the
// right error comes up in error conditions.
// This serves as the base class for all full API behaviour tests. It is for a
// set of integration-style unit tests that is aimed at stressing the negative
// cases from an API usage perspective. This differs from other unit tests in
// which it is written in more of a integration test style and verifies the
// behaviour of cryptohomed APIs rather than the UserDataAuth class.
class UserDataAuthApiTest : public UserDataAuthTest {
public:
UserDataAuthApiTest() = default;
void SetUp() override {
// We need to simulate manufacturer to allow ECC auth blocks.
ON_CALL(sim_factory_.GetMockBackend().GetMock().vendor, GetManufacturer)
.WillByDefault(ReturnValue(0x43524F53));
// Assume that TPM is ready.
ON_CALL(sim_factory_.GetMockBackend().GetMock().state, IsReady)
.WillByDefault(ReturnValue(true));
// Sealing is supported.
ON_CALL(sim_factory_.GetMockBackend().GetMock().sealing, IsSupported)
.WillByDefault(ReturnValue(true));
userdataauth_ = std::make_unique<UserDataAuth>();
userdataauth_->set_hwsec_factory(&sim_factory_);
SetupDefaultUserDataAuth();
SetupMountFactory();
// Note: We skip SetupHwsec() because we use the simulated libhwsec layer.
SetupTasks();
InitializeUserDataAuth();
}
void SetupMountFactory() {
userdataauth_->set_mount_factory_for_testing(&mount_factory_);
ON_CALL(mount_factory_, New(_, _, _, _))
.WillByDefault(Invoke([this](Platform* platform, HomeDirs* homedirs,
bool legacy_mount,
bool bind_mount_downloads) -> Mount* {
if (new_mounts_.empty()) {
ADD_FAILURE() << "Not enough objects in new_mounts_";
return nullptr;
}
Mount* result = new_mounts_[0];
new_mounts_.pop_front();
return result;
}));
}
// Simply the Sync() version of StartAuthSession(). Caller should check that
// the returned value is not nullopt, which indicates that the call did not
// finish.
std::optional<user_data_auth::StartAuthSessionReply> StartAuthSessionSync(
const user_data_auth::StartAuthSessionRequest& in_request) {
TestFuture<user_data_auth::StartAuthSessionReply> reply_future;
userdataauth_->StartAuthSession(
in_request,
reply_future
.GetCallback<const user_data_auth::StartAuthSessionReply&>());
RunUntilIdle();
return reply_future.Get();
}
// Obtain a test auth session for kUsername1. Result is nullopt if it's
// unsuccessful.
std::optional<std::string> GetTestUnauthedAuthSession(
user_data_auth::AuthIntent intent =
user_data_auth::AuthIntent::AUTH_INTENT_DECRYPT,
uint32_t flags =
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE) {
user_data_auth::StartAuthSessionRequest req;
req.mutable_account_id()->set_account_id(*kUsername1);
req.set_intent(intent);
req.set_flags(flags);
std::optional<user_data_auth::StartAuthSessionReply> reply =
StartAuthSessionSync(req);
if (!reply.has_value()) {
LOG(ERROR) << "GetTestUnauthedAuthSession() failed because "
"StartAuthSession() did not complete.";
return std::nullopt;
}
if (reply.value().error_info().primary_action() !=
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR) {
LOG(ERROR) << "GetTestUnauthedAuthSession() failed because "
"StartAuthSession() failed.";
return std::nullopt;
}
return reply.value().auth_session_id();
}
// Create a test user named kUsername1 with kPassword1. Return true if
// successful. This doesn't create the vault.
bool CreateTestUser() {
std::optional<std::string> session_id = GetTestUnauthedAuthSession();
if (!session_id.has_value()) {
LOG(ERROR) << "No session ID in CreateTestUser().";
return false;
}
EXPECT_CALL(homedirs_, CryptohomeExists(_)).WillOnce(ReturnValue(false));
EXPECT_CALL(homedirs_, Create(_)).WillOnce(Return(true));
// Create the user.
user_data_auth::CreatePersistentUserRequest create_request;
create_request.set_auth_session_id(session_id.value());
std::optional<user_data_auth::CreatePersistentUserReply> create_reply =
CreatePersistentUserSync(create_request);
if (!create_reply.has_value()) {
LOG(ERROR) << "Call to CreatePersistentUser() did not complete in "
"CreateTestUser().";
return false;
}
if (create_reply->error_info().primary_action() !=
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR) {
LOG(ERROR)
<< "Call to CreatePersistentUser() failed in CreateTestUser(): "
<< GetProtoDebugString(create_reply.value());
return false;
}
EXPECT_THAT(create_reply->auth_properties().authorized_for(),
UnorderedElementsAre(user_data_auth::AUTH_INTENT_DECRYPT,
user_data_auth::AUTH_INTENT_VERIFY_ONLY));
// Add the password auth factor.
user_data_auth::AddAuthFactorRequest add_factor_request;
add_factor_request.set_auth_session_id(session_id.value());
add_factor_request.mutable_auth_factor()->set_type(
user_data_auth::AuthFactorType::AUTH_FACTOR_TYPE_PASSWORD);
add_factor_request.mutable_auth_factor()->set_label(kPasswordLabel);
add_factor_request.mutable_auth_factor()->mutable_password_metadata();
add_factor_request.mutable_auth_input()
->mutable_password_input()
->set_secret(kPassword1);
std::optional<user_data_auth::AddAuthFactorReply> add_factor_reply =
AddAuthFactorSync(add_factor_request);
if (!add_factor_reply.has_value()) {
LOG(ERROR)
<< "Call to AddAuthFactor() did not complete in CreateTestUser().";
return false;
}
if (add_factor_reply->error_info().primary_action() !=
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR) {
LOG(ERROR) << "Call to AddAuthFactor() failed in CreateTestUser(): "
<< GetProtoDebugString(add_factor_reply.value());
return false;
}
// Invalidate the session.
user_data_auth::InvalidateAuthSessionRequest invalidate_request;
invalidate_request.set_auth_session_id(session_id.value());
std::optional<user_data_auth::InvalidateAuthSessionReply> invalidate_reply =
InvalidateAuthSessionSync(invalidate_request);
if (!invalidate_reply.has_value()) {
LOG(ERROR) << "Call to InvalidateAuthSession() did not complete in "
"CreateTestUser().";
return false;
}
if (invalidate_reply->error_info().primary_action() !=
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR) {
LOG(ERROR)
<< "Call to InvalidateAuthSession() failed in CreateTestUser(): "
<< GetProtoDebugString(invalidate_reply.value());
return false;
}
return true;
}
std::optional<std::string> GetTestAuthedAuthSession(
user_data_auth::AuthIntent intent =
user_data_auth::AuthIntent::AUTH_INTENT_DECRYPT) {
std::optional<std::string> session_id = GetTestUnauthedAuthSession(intent);
if (!session_id.has_value()) {
LOG(ERROR) << "No session ID in GetTestAuthedAuthSession().";
return std::nullopt;
}
user_data_auth::AuthenticateAuthFactorRequest auth_request;
auth_request.set_auth_session_id(session_id.value());
auth_request.add_auth_factor_labels(kPasswordLabel);
auth_request.mutable_auth_input()->mutable_password_input()->set_secret(
kPassword1);
std::optional<user_data_auth::AuthenticateAuthFactorReply> auth_reply =
AuthenticateAuthFactorSync(auth_request);
if (!auth_reply.has_value()) {
LOG(ERROR) << "Call to AuthenticateAuthFactor() did not complete in "
"GetTestAuthedAuthSession().";
return std::nullopt;
}
if (auth_reply->error_info().primary_action() !=
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR) {
LOG(ERROR) << "Call to AuthenticateAuthFactor() failed in "
"GetTestAuthedAuthSession(): "
<< GetProtoDebugString(auth_reply.value());
return std::nullopt;
}
return session_id.value();
}
std::optional<user_data_auth::AuthenticateAuthFactorReply>
AuthenticateAuthFactorSync(
const user_data_auth::AuthenticateAuthFactorRequest& in_request) {
TestFuture<user_data_auth::AuthenticateAuthFactorReply> reply_future;
userdataauth_->AuthenticateAuthFactor(
in_request,
reply_future
.GetCallback<const user_data_auth::AuthenticateAuthFactorReply&>());
RunUntilIdle();
return reply_future.Get();
}
std::optional<user_data_auth::CreatePersistentUserReply>
CreatePersistentUserSync(
const user_data_auth::CreatePersistentUserRequest& in_request) {
TestFuture<user_data_auth::CreatePersistentUserReply> reply_future;
userdataauth_->CreatePersistentUser(
in_request,
reply_future
.GetCallback<const user_data_auth::CreatePersistentUserReply&>());
RunUntilIdle();
return reply_future.Get();
}
std::optional<user_data_auth::AddAuthFactorReply> AddAuthFactorSync(
const user_data_auth::AddAuthFactorRequest& in_request) {
TestFuture<user_data_auth::AddAuthFactorReply> reply_future;
userdataauth_->AddAuthFactor(
in_request,
reply_future.GetCallback<const user_data_auth::AddAuthFactorReply&>());
RunUntilIdle();
return reply_future.Get();
}
std::optional<user_data_auth::InvalidateAuthSessionReply>
InvalidateAuthSessionSync(
const user_data_auth::InvalidateAuthSessionRequest& in_request) {
TestFuture<user_data_auth::InvalidateAuthSessionReply> reply_future;
userdataauth_->InvalidateAuthSession(
in_request,
reply_future
.GetCallback<const user_data_auth::InvalidateAuthSessionReply&>());
RunUntilIdle();
return reply_future.Get();
}
std::optional<user_data_auth::PreparePersistentVaultReply>
PreparePersistentVaultSync(
const user_data_auth::PreparePersistentVaultRequest& in_request) {
TestFuture<user_data_auth::PreparePersistentVaultReply> reply_future;
userdataauth_->PreparePersistentVault(
in_request,
reply_future
.GetCallback<const user_data_auth::PreparePersistentVaultReply&>());
RunUntilIdle();
return reply_future.Get();
}
std::optional<user_data_auth::ModifyAuthFactorIntentsReply>
ModifyAuthFactorIntentsSync(
const user_data_auth::ModifyAuthFactorIntentsRequest& in_request) {
TestFuture<user_data_auth::ModifyAuthFactorIntentsReply> reply_future;
userdataauth_->ModifyAuthFactorIntents(
in_request, reply_future.GetCallback<
const user_data_auth::ModifyAuthFactorIntentsReply&>());
RunUntilIdle();
return reply_future.Get();
}
std::optional<user_data_auth::PrepareEphemeralVaultReply>
PrepareEphemeralVaultSync(
const user_data_auth::PrepareEphemeralVaultRequest& in_request) {
TestFuture<user_data_auth::PrepareEphemeralVaultReply> reply_future;
userdataauth_->PrepareEphemeralVault(
in_request,
reply_future
.GetCallback<const user_data_auth::PrepareEphemeralVaultReply&>());
RunUntilIdle();
return reply_future.Get();
}
std::optional<user_data_auth::PrepareGuestVaultReply> PrepareGuestVaultSync(
const user_data_auth::PrepareGuestVaultRequest& in_request) {
TestFuture<user_data_auth::PrepareGuestVaultReply> reply_future;
userdataauth_->PrepareGuestVault(
in_request,
reply_future
.GetCallback<const user_data_auth::PrepareGuestVaultReply&>());
RunUntilIdle();
return reply_future.Get();
}
protected:
// Mock mount factory for mocking Mount objects.
MockMountFactory mount_factory_;
// Any elements added to this queue will be returned when mount_factory_.New()
// is called.
std::deque<Mount*> new_mounts_;
const Username kUsername1{"foo@gmail.com"};
const Username kUsername2{"bar@gmail.com"};
static constexpr char kPassword1[] = "MyP@ssW0rd!!";
static constexpr char kPasswordLabel[] = "Password1";
static constexpr char kSmartCardLabel[] = "SmartCard1";
static constexpr char kTestErrorString[] = "ErrorForTestingOnly";
hwsec::Tpm2SimulatorFactoryForTest sim_factory_;
};
// Matches against user_data_auth::CryptohomeErrorInfo to see if it contains an
// active recommendation for the specified PossibleAction |action|. "Active
// recommendation" here refers to a correct PrimaryAction value such that the
// PossibleAction field is active and not disregarded.
MATCHER_P(HasPossibleAction, action, "") {
if (arg.primary_action() != user_data_auth::PrimaryAction::PRIMARY_NONE) {
*result_listener
<< "Invalid PrimaryAction when checking for PossibleAction: "
<< user_data_auth::PrimaryAction_Name(arg.primary_action());
return false;
}
for (int i = 0; i < arg.possible_actions_size(); i++) {
if (arg.possible_actions(i) == action)
return true;
}
return false;
}
// Same as multiple invocation of HasPossibleAction. This matcher checks that
// the CryptohomeErrorInfo contains a correct PrimaryAction and the list of
// recommended PossibleAction(s) contains the specified actions. |actions|
// should be set<user_data_auth::PossibleAction>.
MATCHER_P(HasPossibleActions, actions, "") {
// We need to copy the actions to strip off the constness.
std::set<user_data_auth::PossibleAction> to_match = actions;
if (arg.primary_action() != user_data_auth::PrimaryAction::PRIMARY_NONE) {
*result_listener
<< "Invalid PrimaryAction when checking for PossibleAction: "
<< user_data_auth::PrimaryAction_Name(arg.primary_action());
return false;
}
for (int i = 0; i < arg.possible_actions_size(); i++) {
const auto current_action = arg.possible_actions(i);
if (to_match.count(current_action) != 0) {
to_match.erase(current_action);
}
}
for (const auto& action : to_match) {
*result_listener << "Action " << user_data_auth::PossibleAction_Name(action)
<< " not found";
}
return to_match.size() == 0;
}
TEST_F(UserDataAuthApiTest, RemoveStillMounted) {
// If a home directory is mounted it'll return false for Remove().
EXPECT_CALL(homedirs_, Remove(_)).WillOnce(Return(false));
std::optional<std::string> session_id = GetTestUnauthedAuthSession();
ASSERT_TRUE(session_id.has_value());
user_data_auth::RemoveRequest req;
req.set_auth_session_id(session_id.value());
user_data_auth::RemoveReply reply;
reply = userdataauth_->Remove(req);
// Failure to Remove() due to still mounted vault should result in Reboot and
// Powerwash recommendation.
EXPECT_THAT(reply.error_info(),
HasPossibleActions(std::set(
{user_data_auth::PossibleAction::POSSIBLY_REBOOT,
user_data_auth::PossibleAction::POSSIBLY_POWERWASH})));
}
TEST_F(UserDataAuthApiTest, RemoveNoID) {
user_data_auth::RemoveRequest req;
user_data_auth::RemoveReply reply;
reply = userdataauth_->Remove(req);
// Failure to Remove() due to the lack of username in the request is
// unexpected, and should result in POSSIBLY_DEV_CHECK_UNEXPECTED_STATE.
EXPECT_THAT(
reply.error_info(),
HasPossibleAction(
user_data_auth::PossibleAction::POSSIBLY_DEV_CHECK_UNEXPECTED_STATE));
}
TEST_F(UserDataAuthApiTest, AuthAuthFactorNoSession) {
user_data_auth::AuthenticateAuthFactorRequest req;
req.set_auth_session_id("NOT_A_VALID_AUTH_SESSION!");
user_data_auth::AuthenticateAuthFactorReply reply;
std::optional<user_data_auth::AuthenticateAuthFactorReply> result =
AuthenticateAuthFactorSync(req);
ASSERT_TRUE(result.has_value());
reply = *result;
// Failure to AuthenticateAuthFactor() due to missing session should result in
// recommendation to reboot, because we'll need to restart the session after
// reboot so the problem might go away.
EXPECT_THAT(
reply.error_info(),
HasPossibleAction(user_data_auth::PossibleAction::POSSIBLY_REBOOT));
}
TEST_F(UserDataAuthApiTest, ChalCredBadSRKROCA) {
ASSERT_TRUE(CreateTestUser());
std::optional<std::string> session_id = GetTestAuthedAuthSession();
ASSERT_TRUE(session_id.has_value());
ON_CALL(sim_factory_.GetMockBackend().GetMock().vendor, IsSrkRocaVulnerable)
.WillByDefault(ReturnValue(true));
ON_CALL(key_challenge_service_factory_, New(_))
.WillByDefault(
Return(ByMove(std::make_unique<MockKeyChallengeService>())));
user_data_auth::AddAuthFactorRequest add_factor_request;
add_factor_request.set_auth_session_id(session_id.value());
add_factor_request.mutable_auth_factor()->set_type(
user_data_auth::AuthFactorType::AUTH_FACTOR_TYPE_SMART_CARD);
add_factor_request.mutable_auth_factor()->set_label(kSmartCardLabel);
add_factor_request.mutable_auth_factor()
->mutable_smart_card_metadata()
->set_public_key_spki_der("test_pubkey_spki_der");
add_factor_request.mutable_auth_input()
->mutable_smart_card_input()
->add_signature_algorithms(user_data_auth::SmartCardSignatureAlgorithm::
CHALLENGE_RSASSA_PKCS1_V1_5_SHA256);
add_factor_request.mutable_auth_input()
->mutable_smart_card_input()
->set_key_delegate_dbus_service_name("test_challenge_dbus");
std::optional<user_data_auth::AddAuthFactorReply> add_factor_reply =
AddAuthFactorSync(add_factor_request);
ASSERT_TRUE(add_factor_reply.has_value());
EXPECT_EQ(add_factor_reply->error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_TPM_UDPATE_REQUIRED);
EXPECT_FALSE(add_factor_reply->has_added_auth_factor());
}
TEST_F(UserDataAuthApiTest, MountFailed) {
// Prepare an account.
ASSERT_TRUE(CreateTestUser());
std::optional<std::string> session_id = GetTestAuthedAuthSession();
ASSERT_TRUE(session_id.has_value());
// Ensure that the mount fails.
scoped_refptr<MockMount> mount = new MockMount();
EXPECT_CALL(*mount, MountCryptohome(_, _, _))
.WillOnce(ReturnError<StorageError>(FROM_HERE, kTestErrorString,
MOUNT_ERROR_FATAL, false));
new_mounts_.push_back(mount.get());
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(Return(true));
EXPECT_CALL(disk_cleanup_, FreeDiskSpaceDuringLogin(_))
.WillRepeatedly(Return(true));
// Make the call to check that the result is correct.
user_data_auth::PreparePersistentVaultRequest prepare_req;
prepare_req.set_auth_session_id(session_id.value());
std::optional<user_data_auth::PreparePersistentVaultReply> prepare_reply =
PreparePersistentVaultSync(prepare_req);
ASSERT_TRUE(prepare_reply.has_value());
EXPECT_THAT(prepare_reply->error_info(),
HasPossibleActions(std::set(
{user_data_auth::PossibleAction::POSSIBLY_RETRY,
user_data_auth::PossibleAction::POSSIBLY_REBOOT,
user_data_auth::PossibleAction::POSSIBLY_DELETE_VAULT,
user_data_auth::PossibleAction::POSSIBLY_POWERWASH})));
}
TEST_F(UserDataAuthApiTest, GuestMountFailed) {
// Ensure that the guest mount fails.
scoped_refptr<MockMount> mount = new MockMount();
EXPECT_CALL(*mount, MountEphemeralCryptohome(_))
.WillOnce(ReturnError<StorageError>(FROM_HERE, kTestErrorString,
MOUNT_ERROR_FATAL, false));
new_mounts_.push_back(mount.get());
// Make the call to check that it failed correctly.
user_data_auth::PrepareGuestVaultRequest prepare_req;
std::optional<user_data_auth::PrepareGuestVaultReply> prepare_reply =
PrepareGuestVaultSync(prepare_req);
ASSERT_TRUE(prepare_reply.has_value());
EXPECT_THAT(prepare_reply->error_info(),
HasPossibleActions(std::set(
{user_data_auth::PossibleAction::POSSIBLY_RETRY,
user_data_auth::PossibleAction::POSSIBLY_REBOOT,
user_data_auth::PossibleAction::POSSIBLY_POWERWASH})));
}
TEST_F(UserDataAuthApiTest, EphemeralMountFailed) {
// Prepare an auth session for ephemeral mount.
std::optional<std::string> session_id = GetTestUnauthedAuthSession(
user_data_auth::AuthIntent::AUTH_INTENT_DECRYPT,
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_EPHEMERAL_USER);
ASSERT_TRUE(session_id.has_value());
// Ensure that the mount fails.
scoped_refptr<MockMount> mount = new MockMount();
EXPECT_CALL(*mount, MountEphemeralCryptohome(_))
.WillOnce(ReturnError<StorageError>(FROM_HERE, kTestErrorString,
MOUNT_ERROR_FATAL, false));
new_mounts_.push_back(mount.get());
EXPECT_CALL(homedirs_, GetPlainOwner(_))
.WillRepeatedly(DoAll(SetArgPointee<0>(kUsername2), Return(true)));
// Make the call to check that the result is correct.
user_data_auth::PrepareEphemeralVaultRequest prepare_req;
prepare_req.set_auth_session_id(session_id.value());
std::optional<user_data_auth::PrepareEphemeralVaultReply> prepare_reply =
PrepareEphemeralVaultSync(prepare_req);
ASSERT_TRUE(prepare_reply.has_value());
EXPECT_THAT(prepare_reply->error_info(),
HasPossibleActions(std::set(
{user_data_auth::PossibleAction::POSSIBLY_RETRY,
user_data_auth::PossibleAction::POSSIBLY_REBOOT,
user_data_auth::PossibleAction::POSSIBLY_POWERWASH})));
EXPECT_THAT(prepare_reply->auth_properties().authorized_for(), IsEmpty());
}
// This is designed to trigger the unrecoverable vault flow.
TEST_F(UserDataAuthApiTest, VaultWithoutAuth) {
// Mock that the user exists.
base::FilePath upath = UserPath(SanitizeUserName(kUsername1));
EXPECT_CALL(platform_, DirectoryExists(upath)).WillOnce(Return(true));
// Call StartAuthSession and it should fail.
user_data_auth::StartAuthSessionRequest req;
req.mutable_account_id()->set_account_id(*kUsername1);
req.set_intent(user_data_auth::AuthIntent::AUTH_INTENT_DECRYPT);
req.set_flags(user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE);
std::optional<user_data_auth::StartAuthSessionReply> reply =
StartAuthSessionSync(req);
ASSERT_TRUE(reply.has_value());
EXPECT_THAT(
reply->error_info(),
HasPossibleAction(user_data_auth::PossibleAction::POSSIBLY_DELETE_VAULT));
}
// This is designed to trigger FailureReason::COULD_NOT_MOUNT_CRYPTOHOME on
// Chromium side for AuthenticateAuthFactor().
TEST_F(UserDataAuthApiTest, AuthAuthFactorWithoutLabel) {
// Prepare an account.
ASSERT_TRUE(CreateTestUser());
// Call AuthenticateAuthFactor with an empty label.
std::optional<std::string> session_id = GetTestUnauthedAuthSession();
ASSERT_TRUE(session_id.has_value());
user_data_auth::AuthenticateAuthFactorRequest auth_request;
auth_request.set_auth_session_id(session_id.value());
// Intentionally set empty label.
auth_request.set_auth_factor_label("");
auth_request.mutable_auth_input()->mutable_password_input()->set_secret(
kPassword1);
std::optional<user_data_auth::AuthenticateAuthFactorReply> auth_reply =
AuthenticateAuthFactorSync(auth_request);
// Should result is POSSIBLY_DEV_CHECK_UNEXPECTED_STATE.
ASSERT_TRUE(auth_reply.has_value());
EXPECT_THAT(
auth_reply->error_info(),
HasPossibleAction(
user_data_auth::PossibleAction::POSSIBLY_DEV_CHECK_UNEXPECTED_STATE));
}
// This is designed to trigger FailureReason::COULD_NOT_MOUNT_CRYPTOHOME on
// Chromium side for CreatePersistentUserAlreadyExist().
TEST_F(UserDataAuthApiTest, CreatePeristentUserAlreadyExist) {
// Setup auth session.
std::optional<std::string> session_id = GetTestUnauthedAuthSession();
ASSERT_TRUE(session_id.has_value());
// Call CreatePersistentUser() while the user already exists.
EXPECT_CALL(homedirs_, CryptohomeExists(_)).WillOnce(ReturnValue(true));
user_data_auth::CreatePersistentUserRequest create_request;
create_request.set_auth_session_id(session_id.value());
std::optional<user_data_auth::CreatePersistentUserReply> create_reply =
CreatePersistentUserSync(create_request);
ASSERT_TRUE(create_reply.has_value());
EXPECT_THAT(
create_reply->error_info(),
HasPossibleActions(std::set(
{user_data_auth::PossibleAction::POSSIBLY_DEV_CHECK_UNEXPECTED_STATE,
user_data_auth::PossibleAction::POSSIBLY_DELETE_VAULT})));
EXPECT_THAT(create_reply->auth_properties().authorized_for(), IsEmpty());
}
// This is designed to check if modifying auth factor intents results in
// enabling/disabling a configurable intent. And also that non-configurable
// intents are not configured.
TEST_F(UserDataAuthApiTest, ModifyAuthFactorIntents) {
// Setup auth session.
auto mock_processor =
std::make_unique<NiceMock<MockBiometricsCommandProcessor>>();
MockBiometricsCommandProcessor* bio_command_processor_ = mock_processor.get();
EXPECT_CALL(*bio_command_processor_, SetEnrollScanDoneCallback(_));
EXPECT_CALL(*bio_command_processor_, SetAuthScanDoneCallback(_));
EXPECT_CALL(*bio_command_processor_, SetSessionFailedCallback(_));
bio_service_ = std::make_unique<BiometricsAuthBlockService>(
std::move(mock_processor),
/*enroll_signal_sender=*/base::DoNothing(),
/*auth_signal_sender=*/base::DoNothing());
userdataauth_->set_biometrics_service(bio_service_.get());
userdataauth_->set_crypto(&crypto_);
userdataauth_->set_fingerprint_manager(&fingerprint_manager_);
ASSERT_TRUE(CreateTestUser());
std::optional<std::string> session_id = GetTestAuthedAuthSession();
ASSERT_TRUE(session_id.has_value());
// CreateModifyAuthFactorIntentRequest
user_data_auth::ModifyAuthFactorIntentsRequest modify_req;
modify_req.set_auth_session_id(session_id.value());
modify_req.set_type(
user_data_auth::AuthFactorType::AUTH_FACTOR_TYPE_FINGERPRINT);
// This would both test that Decrypt is enabled and that webauthn is not
// disabled.
modify_req.add_intents(user_data_auth::AuthIntent::AUTH_INTENT_DECRYPT);
modify_req.add_intents(user_data_auth::AuthIntent::AUTH_INTENT_VERIFY_ONLY);
EXPECT_CALL(*bio_command_processor_, IsReady()).WillOnce(Return(true));
std::optional<user_data_auth::ModifyAuthFactorIntentsReply> modify_reply =
ModifyAuthFactorIntentsSync(modify_req);
EXPECT_TRUE(modify_reply.has_value());
EXPECT_THAT(modify_reply->auth_intents().type(),
user_data_auth::AuthFactorType::AUTH_FACTOR_TYPE_FINGERPRINT);
EXPECT_THAT(modify_reply->auth_intents().current(),
UnorderedElementsAre(user_data_auth::AUTH_INTENT_VERIFY_ONLY,
user_data_auth::AUTH_INTENT_DECRYPT,
user_data_auth::AUTH_INTENT_WEBAUTHN));
EXPECT_THAT(modify_reply->auth_intents().minimum(),
UnorderedElementsAre(user_data_auth::AUTH_INTENT_WEBAUTHN));
EXPECT_THAT(modify_reply->auth_intents().maximum(),
UnorderedElementsAre(user_data_auth::AUTH_INTENT_VERIFY_ONLY,
user_data_auth::AUTH_INTENT_DECRYPT,
user_data_auth::AUTH_INTENT_WEBAUTHN));
}
// This is designed to trigger FailureReason::COULD_NOT_MOUNT_CRYPTOHOME on
// Chromium side for PreparePersistentVault().
TEST_F(UserDataAuthApiTest, PreparePersistentVaultWithoutUser) {
// Prepare an account.
ASSERT_TRUE(CreateTestUser());
std::optional<std::string> session_id = GetTestAuthedAuthSession();
ASSERT_TRUE(session_id.has_value());
// Vault doesn't exist.
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(Return(false));
// Make the call to check that the result is correct.
user_data_auth::PreparePersistentVaultRequest prepare_req;
prepare_req.set_auth_session_id(session_id.value());
std::optional<user_data_auth::PreparePersistentVaultReply> prepare_reply =
PreparePersistentVaultSync(prepare_req);
ASSERT_TRUE(prepare_reply.has_value());
EXPECT_THAT(
prepare_reply->error_info(),
HasPossibleActions(std::set(
{user_data_auth::PossibleAction::POSSIBLY_DEV_CHECK_UNEXPECTED_STATE,
user_data_auth::PossibleAction::POSSIBLY_REBOOT,
user_data_auth::PossibleAction::POSSIBLY_DELETE_VAULT,
user_data_auth::PossibleAction::POSSIBLY_POWERWASH})));
}
// This is designed to trigger FailureReason::COULD_NOT_MOUNT_CRYPTOHOME on
// Chromium side for PrepareEphemeralVault().
TEST_F(UserDataAuthApiTest, EphemeralMountWithRegularSession) {
// Prepare an auth session for ephemeral mount, note that we intentionally
// does not specify it as ephemeral.
std::optional<std::string> session_id = GetTestUnauthedAuthSession(
user_data_auth::AuthIntent::AUTH_INTENT_DECRYPT,
user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_NONE);
ASSERT_TRUE(session_id.has_value());
// Make the call to check that it fails due to the session not being
// ephemeral.
user_data_auth::PrepareEphemeralVaultRequest prepare_req;
prepare_req.set_auth_session_id(session_id.value());
std::optional<user_data_auth::PrepareEphemeralVaultReply> prepare_reply =
PrepareEphemeralVaultSync(prepare_req);
ASSERT_TRUE(prepare_reply.has_value());
EXPECT_THAT(
prepare_reply->error_info(),
HasPossibleActions(std::set(
{user_data_auth::PossibleAction::POSSIBLY_DEV_CHECK_UNEXPECTED_STATE,
user_data_auth::PossibleAction::POSSIBLY_REBOOT,
user_data_auth::PossibleAction::POSSIBLY_POWERWASH})));
EXPECT_THAT(prepare_reply->auth_properties().authorized_for(), IsEmpty());
}
// This is designed to trigger FailureReason::COULD_NOT_MOUNT_CRYPTOHOME on
// Chromium side for PrepareGuestVault().
TEST_F(UserDataAuthApiTest, MountGuestWithOtherMounts) {
// Create test user and mount the vault.
ASSERT_TRUE(CreateTestUser());
std::optional<std::string> session_id = GetTestAuthedAuthSession();
ASSERT_TRUE(session_id.has_value());
// Setup the mount.
scoped_refptr<MockMount> mount = new MockMount();
EXPECT_CALL(*mount, MountCryptohome(_, _, _))
.WillOnce(ReturnOk<StorageError>());
new_mounts_.push_back(mount.get());
EXPECT_CALL(homedirs_, Exists(_)).WillOnce(Return(true));
EXPECT_CALL(disk_cleanup_, FreeDiskSpaceDuringLogin(_))
.WillRepeatedly(Return(true));
user_data_auth::PreparePersistentVaultRequest prepare_req;
prepare_req.set_auth_session_id(session_id.value());
std::optional<user_data_auth::PreparePersistentVaultReply> prepare_reply =
PreparePersistentVaultSync(prepare_req);
ASSERT_TRUE(prepare_reply.has_value());
ASSERT_EQ(prepare_reply->error_info().primary_action(),
user_data_auth::PrimaryAction::PRIMARY_NO_ERROR);
// Try to mount the guest vault and it should fail.
user_data_auth::PrepareGuestVaultRequest guest_req;
std::optional<user_data_auth::PrepareGuestVaultReply> guest_reply =
PrepareGuestVaultSync(guest_req);
ASSERT_TRUE(guest_reply.has_value());
EXPECT_THAT(guest_reply->error_info(),
HasPossibleActions(
std::set({user_data_auth::PossibleAction::POSSIBLY_REBOOT})));
}
} // namespace cryptohome