blob: 94d5527d01745da330611505df96c2d79e6c4178 [file] [log] [blame]
// Copyright 2021 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 <memory>
#include <utility>
#include <base/containers/span.h>
#include <base/memory/scoped_refptr.h>
#include <base/test/mock_callback.h>
#include <base/test/task_environment.h>
#include <base/test/test_future.h>
#include <brillo/cryptohome.h>
#include <brillo/secure_blob.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <libhwsec/frontend/cryptohome/mock_frontend.h>
#include <libhwsec/frontend/pinweaver/mock_frontend.h>
#include <libhwsec-foundation/error/testing_helper.h>
#include "cryptohome/auth_blocks/auth_block_utility_impl.h"
#include "cryptohome/auth_blocks/mock_auth_block_utility.h"
#include "cryptohome/auth_factor/auth_factor_manager.h"
#include "cryptohome/auth_session.h"
#include "cryptohome/auth_session_manager.h"
#include "cryptohome/cleanup/mock_user_oldest_activity_timestamp_manager.h"
#include "cryptohome/credentials.h"
#include "cryptohome/crypto.h"
#include "cryptohome/crypto_error.h"
#include "cryptohome/error/cryptohome_crypto_error.h"
#include "cryptohome/error/cryptohome_error.h"
#include "cryptohome/mock_cryptohome_keys_manager.h"
#include "cryptohome/mock_install_attributes.h"
#include "cryptohome/mock_keyset_management.h"
#include "cryptohome/mock_le_credential_manager.h"
#include "cryptohome/mock_platform.h"
#include "cryptohome/pkcs11/mock_pkcs11_token_factory.h"
#include "cryptohome/storage/error.h"
#include "cryptohome/storage/mock_homedirs.h"
#include "cryptohome/storage/mock_mount.h"
#include "cryptohome/user_secret_stash_storage.h"
#include "cryptohome/user_session/mock_user_session.h"
#include "cryptohome/user_session/mock_user_session_factory.h"
#include "cryptohome/user_session/real_user_session.h"
#include "cryptohome/user_session/user_session_map.h"
#include "cryptohome/vault_keyset.h"
namespace cryptohome {
using ::testing::_;
using ::testing::An;
using ::testing::ByMove;
using ::testing::DoAll;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::IsEmpty;
using ::testing::IsNull;
using ::testing::NiceMock;
using ::testing::NotNull;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::SetArgPointee;
using ::testing::UnorderedElementsAre;
using base::test::TaskEnvironment;
using base::test::TestFuture;
using brillo::cryptohome::home::kGuestUserName;
using brillo::cryptohome::home::SanitizeUserName;
using error::CryptohomeCryptoError;
using error::CryptohomeError;
using error::CryptohomeMountError;
using hwsec_foundation::error::testing::IsOk;
using hwsec_foundation::error::testing::NotOk;
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 user_data_auth::AUTH_INTENT_DECRYPT;
using user_data_auth::AUTH_INTENT_VERIFY_ONLY;
using user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_EPHEMERAL_USER;
using AuthenticateCallback = base::OnceCallback<void(
const user_data_auth::AuthenticateAuthSessionReply&)>;
using AddCredentialCallback =
base::OnceCallback<void(const user_data_auth::AddCredentialsReply&)>;
namespace {
constexpr char kUsername[] = "foo@example.com";
constexpr char kPassword[] = "password";
constexpr char kUsername2[] = "foo2@example.com";
constexpr char kPassword2[] = "password2";
constexpr char kUsername3[] = "foo3@example.com";
constexpr char kPassword3[] = "password3";
constexpr char kPasswordLabel[] = "fake-password-label";
constexpr char kPin[] = "1234";
constexpr char kPinLabel[] = "fake-pin-label";
SerializedVaultKeyset CreateFakePasswordVk(const std::string& label) {
SerializedVaultKeyset serialized_vk;
serialized_vk.set_flags(SerializedVaultKeyset::TPM_WRAPPED |
SerializedVaultKeyset::SCRYPT_DERIVED |
SerializedVaultKeyset::PCR_BOUND |
SerializedVaultKeyset::ECC);
serialized_vk.set_password_rounds(1);
serialized_vk.set_tpm_key("tpm-key");
serialized_vk.set_extended_tpm_key("tpm-extended-key");
serialized_vk.set_vkk_iv("iv");
serialized_vk.set_wrapped_reset_seed("wrapped-reset-seed");
serialized_vk.mutable_key_data()->set_type(KeyData::KEY_TYPE_PASSWORD);
serialized_vk.mutable_key_data()->set_label(label);
return serialized_vk;
}
SerializedVaultKeyset CreateFakePinVk(const std::string& label) {
SerializedVaultKeyset serialized_vk;
serialized_vk.set_flags(SerializedVaultKeyset::LE_CREDENTIAL);
serialized_vk.mutable_key_data()->set_type(KeyData::KEY_TYPE_PASSWORD);
serialized_vk.mutable_key_data()->set_label(label);
serialized_vk.mutable_key_data()
->mutable_policy()
->set_low_entropy_credential(true);
serialized_vk.set_salt("salt");
serialized_vk.set_le_chaps_iv("le-chaps-iv");
serialized_vk.set_le_label(0);
serialized_vk.set_le_fek_iv("le-fek-iv");
return serialized_vk;
}
void MockLabelToKeyDataMapLoading(
const std::string& obfuscated_username,
const std::vector<SerializedVaultKeyset>& serialized_vks,
MockKeysetManagement& keyset_management) {
KeyLabelMap key_label_map;
for (const auto& serialized_vk : serialized_vks) {
key_label_map[serialized_vk.key_data().label()] = serialized_vk.key_data();
}
EXPECT_CALL(keyset_management,
GetVaultKeysetLabelsAndData(obfuscated_username, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(key_label_map), Return(true)));
}
void MockKeysetsLoading(
const std::string& obfuscated_username,
const std::vector<SerializedVaultKeyset>& serialized_vks,
MockKeysetManagement& keyset_management) {
std::vector<int> key_indices;
for (size_t index = 0; index < serialized_vks.size(); ++index) {
key_indices.push_back(index);
}
EXPECT_CALL(keyset_management, GetVaultKeysets(obfuscated_username, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(key_indices), Return(true)));
}
void MockKeysetLoadingByIndex(const std::string& obfuscated_username,
int index,
const SerializedVaultKeyset& serialized_vk,
MockKeysetManagement& keyset_management) {
EXPECT_CALL(keyset_management,
LoadVaultKeysetForUser(obfuscated_username, index))
.WillRepeatedly([=](const std::string&, int) {
auto vk = std::make_unique<VaultKeyset>();
vk->InitializeFromSerialized(serialized_vk);
return vk;
});
}
void MockKeysetLoadingByLabel(const std::string& obfuscated_username,
const SerializedVaultKeyset& serialized_vk,
MockKeysetManagement& keyset_management) {
EXPECT_CALL(
keyset_management,
GetVaultKeyset(obfuscated_username, serialized_vk.key_data().label()))
.WillRepeatedly([=](const std::string&, const std::string&) {
auto vk = std::make_unique<VaultKeyset>();
vk->InitializeFromSerialized(serialized_vk);
return vk;
});
}
void MockKeysetDerivation(const std::string& obfuscated_username,
const SerializedVaultKeyset& serialized_vk,
CryptoError derivation_error,
MockAuthBlockUtility& auth_block_utility) {
EXPECT_CALL(auth_block_utility,
GetAuthBlockStateFromVaultKeyset(serialized_vk.key_data().label(),
obfuscated_username, _))
.WillOnce(Return(true));
// Return an arbitrary auth block type from the mock.
EXPECT_CALL(auth_block_utility, GetAuthBlockTypeFromState(_))
.WillOnce(Return(AuthBlockType::kTpmEcc));
const CryptohomeError::ErrorLocationPair fake_error_location =
CryptohomeError::ErrorLocationPair(
static_cast<CryptohomeError::ErrorLocation>(1),
std::string("FakeErrorLocation"));
EXPECT_CALL(auth_block_utility, DeriveKeyBlobsWithAuthBlockAsync(_, _, _, _))
.WillOnce([=](AuthBlockType, const AuthInput&, const AuthBlockState&,
AuthBlock::DeriveCallback derive_callback) {
std::move(derive_callback)
.Run(derivation_error == CryptoError::CE_NONE
? OkStatus<CryptohomeCryptoError>()
: MakeStatus<CryptohomeCryptoError>(
fake_error_location, error::ErrorActionSet(),
derivation_error),
std::make_unique<KeyBlobs>());
return true;
});
}
void MockKeysetCreation(MockAuthBlockUtility& auth_block_utility) {
// Return an arbitrary auth block type from the mock.
EXPECT_CALL(
auth_block_utility,
GetAuthBlockTypeForCreation(_, _, _, AuthFactorStorageType::kVaultKeyset))
.WillOnce(Return(AuthBlockType::kTpmEcc))
.RetiresOnSaturation();
EXPECT_CALL(auth_block_utility, CreateKeyBlobsWithAuthBlockAsync(_, _, _))
.WillOnce([](AuthBlockType, const AuthInput&,
AuthBlock::CreateCallback create_callback) {
std::move(create_callback)
.Run(OkStatus<CryptohomeCryptoError>(),
std::make_unique<KeyBlobs>(),
std::make_unique<AuthBlockState>());
return true;
})
.RetiresOnSaturation();
}
void MockInitialKeysetAdding(const std::string& obfuscated_username,
const SerializedVaultKeyset& serialized_vk,
MockKeysetManagement& keyset_management) {
EXPECT_CALL(keyset_management,
AddInitialKeysetWithKeyBlobs(obfuscated_username, _, _, _, _, _))
.WillOnce([=](const std::string&, const KeyData&,
const std::optional<
SerializedVaultKeyset_SignatureChallengeInfo>&,
const FileSystemKeyset& file_system_keyset, KeyBlobs,
std::unique_ptr<AuthBlockState>) {
// Populate the VK with both public and secret data (like reset seed).
auto vk = std::make_unique<VaultKeyset>();
vk->InitializeFromSerialized(serialized_vk);
vk->CreateFromFileSystemKeyset(file_system_keyset);
return vk;
});
}
void MockKeysetLoadingViaBlobs(const std::string& obfuscated_username,
const SerializedVaultKeyset& serialized_vk,
MockKeysetManagement& keyset_management) {
EXPECT_CALL(keyset_management,
GetValidKeysetWithKeyBlobs(obfuscated_username, _, _))
.WillOnce(
[=](const std::string&, KeyBlobs, const std::optional<std::string>&) {
auto vk = std::make_unique<VaultKeyset>();
vk->InitializeFromSerialized(serialized_vk);
return vk;
});
}
void MockOwnerUser(const std::string& username, MockHomeDirs& homedirs) {
EXPECT_CALL(homedirs, GetPlainOwner(_))
.WillRepeatedly(DoAll(SetArgPointee<0>(username), Return(true)));
}
} // namespace
class AuthSessionInterfaceTestBase : public ::testing::Test {
public:
AuthSessionInterfaceTestBase()
: crypto_(&hwsec_, &pinweaver_, &cryptohome_keys_manager_, nullptr) {
SetUpHWSecExpectations();
MockLECredentialManager* le_cred_manager = new MockLECredentialManager();
crypto_.set_le_manager_for_testing(
std::unique_ptr<cryptohome::LECredentialManager>(le_cred_manager));
crypto_.Init();
userdataauth_.set_platform(&platform_);
userdataauth_.set_homedirs(&homedirs_);
userdataauth_.set_user_session_factory(&user_session_factory_);
userdataauth_.set_keyset_management(&keyset_management_);
userdataauth_.set_auth_factor_manager_for_testing(&auth_factor_manager_);
userdataauth_.set_user_secret_stash_storage_for_testing(
&user_secret_stash_storage_);
userdataauth_.set_user_session_map_for_testing(&user_session_map_);
userdataauth_.set_pkcs11_token_factory(&pkcs11_token_factory_);
userdataauth_.set_user_activity_timestamp_manager(
&user_activity_timestamp_manager_);
userdataauth_.set_install_attrs(&install_attrs_);
userdataauth_.set_mount_task_runner(
task_environment.GetMainThreadTaskRunner());
userdataauth_.set_current_thread_id_for_test(
UserDataAuth::TestThreadId::kMountThread);
}
void SetUpHWSecExpectations() {
EXPECT_CALL(hwsec_, IsEnabled()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(hwsec_, IsReady()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(hwsec_, IsSealingSupported()).WillRepeatedly(ReturnValue(true));
EXPECT_CALL(hwsec_, GetManufacturer())
.WillRepeatedly(ReturnValue(0x43524f53));
EXPECT_CALL(hwsec_, GetAuthValue(_, _))
.WillRepeatedly(ReturnValue(brillo::SecureBlob()));
EXPECT_CALL(hwsec_, SealWithCurrentUser(_, _, _))
.WillRepeatedly(ReturnValue(brillo::Blob()));
EXPECT_CALL(hwsec_, GetPubkeyHash(_))
.WillRepeatedly(ReturnValue(brillo::Blob()));
}
void CreateAuthSessionManager(AuthBlockUtility* auth_block_utility) {
auth_session_manager_ = std::make_unique<AuthSessionManager>(
&crypto_, &platform_, &user_session_map_, &keyset_management_,
auth_block_utility, &auth_factor_manager_, &user_secret_stash_storage_);
userdataauth_.set_auth_session_manager(auth_session_manager_.get());
}
protected:
TaskEnvironment task_environment{
TaskEnvironment::ThreadPoolExecutionMode::QUEUED};
NiceMock<MockPlatform> platform_;
UserSessionMap user_session_map_;
NiceMock<MockHomeDirs> homedirs_;
NiceMock<MockCryptohomeKeysManager> cryptohome_keys_manager_;
NiceMock<hwsec::MockCryptohomeFrontend> hwsec_;
NiceMock<hwsec::MockPinWeaverFrontend> pinweaver_;
Crypto crypto_;
NiceMock<MockUserSessionFactory> user_session_factory_;
AuthFactorManager auth_factor_manager_{&platform_};
UserSecretStashStorage user_secret_stash_storage_{&platform_};
NiceMock<MockKeysetManagement> keyset_management_;
NiceMock<MockPkcs11TokenFactory> pkcs11_token_factory_;
NiceMock<MockUserOldestActivityTimestampManager>
user_activity_timestamp_manager_;
NiceMock<MockInstallAttributes> install_attrs_;
std::unique_ptr<AuthSessionManager> auth_session_manager_;
UserDataAuth userdataauth_;
// Accessors functions to avoid making each test a friend.
CryptohomeStatus PrepareGuestVaultImpl() {
return userdataauth_.PrepareGuestVaultImpl();
}
CryptohomeStatus PrepareEphemeralVaultImpl(
const std::string& auth_session_id) {
return userdataauth_.PrepareEphemeralVaultImpl(auth_session_id);
}
CryptohomeStatus PreparePersistentVaultImpl(
const std::string& auth_session_id,
const CryptohomeVault::Options& vault_options) {
return userdataauth_.PreparePersistentVaultImpl(auth_session_id,
vault_options);
}
CryptohomeStatus CreatePersistentUserImpl(
const std::string& auth_session_id) {
return userdataauth_.CreatePersistentUserImpl(auth_session_id);
}
void AddCredentials(
user_data_auth::AddCredentialsRequest request,
base::OnceCallback<void(const user_data_auth::AddCredentialsReply&)>
on_done) {
userdataauth_.AddCredentials(request, std::move(on_done));
}
void AuthenticateAuthSession(
user_data_auth::AuthenticateAuthSessionRequest request,
base::OnceCallback<
void(const user_data_auth::AuthenticateAuthSessionReply&)> on_done) {
userdataauth_.AuthenticateAuthSession(request, std::move(on_done));
}
void GetAuthSessionStatusImpl(
AuthSession* auth_session,
user_data_auth::GetAuthSessionStatusReply& reply) {
userdataauth_.GetAuthSessionStatusImpl(auth_session, reply);
}
};
class AuthSessionInterfaceTest : public AuthSessionInterfaceTestBase {
protected:
AuthSessionInterfaceTest() {
auth_block_utility_impl_ = std::make_unique<AuthBlockUtilityImpl>(
&keyset_management_, &crypto_, &platform_);
CreateAuthSessionManager(auth_block_utility_impl_.get());
}
void SetAuthSessionAsAuthenticated(AuthSession* auth_session,
base::span<const AuthIntent> intents) {
auth_session->SetAuthSessionAsAuthenticated(intents);
}
AuthorizationRequest CreateAuthorization(const std::string& secret) {
AuthorizationRequest req;
req.mutable_key()->set_secret(secret);
req.mutable_key()->mutable_data()->set_label("test-label");
req.mutable_key()->mutable_data()->set_type(KeyData::KEY_TYPE_PASSWORD);
return req;
}
void ExpectAuth(const std::string& username,
const brillo::SecureBlob& secret) {
auto vk = std::make_unique<VaultKeyset>();
Credentials creds(username, secret);
EXPECT_CALL(keyset_management_, GetValidKeysetWithKeyBlobs(_, _, _))
.WillOnce(Return(ByMove(std::move(vk))));
}
void ExpectVaultKeyset(int num_of_keysets) {
// Assert parameter num_of_calls cannot be negative.
DCHECK_GT(num_of_keysets, 0);
// Setup expectations for GetVaultKeyset to return an initialized
// VaultKeyset Construct the vault keyset with credentials for
// AuthBlockType::kTpmNotBoundToPcrAuthBlockState.
const brillo::SecureBlob blob16(16, 'A');
brillo::SecureBlob passkey(20, 'A');
Credentials credentials("Test User", passkey);
brillo::SecureBlob system_salt_ =
brillo::SecureBlob(*brillo::cryptohome::home::GetSystemSalt());
SerializedVaultKeyset serialized;
serialized.set_flags(SerializedVaultKeyset::LE_CREDENTIAL);
serialized.set_salt(system_salt_.data(), system_salt_.size());
serialized.set_le_chaps_iv(blob16.data(), blob16.size());
serialized.set_le_label(0);
serialized.set_le_fek_iv(blob16.data(), blob16.size());
EXPECT_CALL(keyset_management_, GetVaultKeyset(_, _))
.Times(num_of_keysets)
.WillRepeatedly([=](const std::string& obfuscated_username,
const std::string& key_label) {
auto vk = std::make_unique<VaultKeyset>();
vk->InitializeFromSerialized(serialized);
return vk;
});
}
std::unique_ptr<AuthBlockUtilityImpl> auth_block_utility_impl_;
};
namespace {
TEST_F(AuthSessionInterfaceTest, PrepareGuestVault) {
auto user_session = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session, IsActive()).WillRepeatedly(Return(true));
EXPECT_CALL(*user_session, MountGuest()).WillOnce(Invoke([]() {
return OkStatus<CryptohomeMountError>();
}));
EXPECT_CALL(user_session_factory_, New(_, _, _))
.WillOnce(Return(ByMove(std::move(user_session))));
// Expect auth and existing cryptohome-dir only for non-ephemeral
ExpectAuth(kUsername2, brillo::SecureBlob(kPassword2));
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername2)))
.WillRepeatedly(Return(true));
ASSERT_TRUE(PrepareGuestVaultImpl().ok());
// Trying to prepare another session should fail, whether it is guest, ...
CryptohomeStatus status = PrepareGuestVaultImpl();
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_MOUNT_FATAL);
// ... ephemeral, ...
ExpectVaultKeyset(/*num_of_keysets=*/1);
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, AUTH_SESSION_FLAGS_EPHEMERAL_USER, AuthIntent::kDecrypt);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session->Authenticate(CreateAuthorization(kPassword),
authenticate_future.GetCallback());
EXPECT_THAT(authenticate_future.Get(), IsOk());
status = PrepareEphemeralVaultImpl(auth_session->serialized_token());
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY);
// ... or regular.
auth_session = auth_session_manager_->CreateAuthSession(kUsername2, 0,
AuthIntent::kDecrypt);
TestFuture<CryptohomeStatus> authenticate_regular_future;
auth_session->Authenticate(CreateAuthorization(kPassword2),
authenticate_regular_future.GetCallback());
EXPECT_THAT(authenticate_regular_future.Get(), IsOk());
status = PreparePersistentVaultImpl(auth_session->serialized_token(), {});
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY);
}
TEST_F(AuthSessionInterfaceTest, PrepareEphemeralVault) {
MockOwnerUser("whoever", homedirs_);
// No auth session.
CryptohomeStatus status = PrepareEphemeralVaultImpl("");
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_legacy_error(),
user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN);
// Auth session is initially not authenticated for ephemeral users.
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, AUTH_SESSION_FLAGS_EPHEMERAL_USER, AuthIntent::kDecrypt);
EXPECT_THAT(auth_session->GetStatus(),
AuthStatus::kAuthStatusFurtherFactorRequired);
// User authed and exists.
auto user_session = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session, SetCredentials(An<const Credentials&>()));
EXPECT_CALL(*user_session, GetPkcs11Token()).WillRepeatedly(Return(nullptr));
EXPECT_CALL(*user_session, IsEphemeral()).WillRepeatedly(Return(true));
EXPECT_CALL(*user_session, MountEphemeral(kUsername))
.WillOnce(ReturnError<CryptohomeMountError>());
EXPECT_CALL(user_session_factory_, New(_, _, _))
.WillOnce(Return(ByMove(std::move(user_session))));
ASSERT_TRUE(PrepareEphemeralVaultImpl(auth_session->serialized_token()).ok());
EXPECT_THAT(auth_session->GetStatus(), AuthStatus::kAuthStatusAuthenticated);
// Set up expectation for add credential callback success.
user_data_auth::AddCredentialsRequest request;
request.set_auth_session_id(auth_session->serialized_token());
*request.mutable_authorization() = CreateAuthorization(kPassword);
user_data_auth::AddCredentialsReply reply;
base::MockCallback<AddCredentialCallback> on_done;
EXPECT_CALL(on_done, Run(_)).WillOnce(SaveArg<0>(&reply));
AddCredentials(request, on_done.Get());
// Evaluate error returned by callback.
ASSERT_THAT(reply.error(), Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
// Trying to mount again will yield busy.
status = PrepareEphemeralVaultImpl(auth_session->serialized_token());
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY);
// Guest fails if other sessions present.
status = PrepareGuestVaultImpl();
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_MOUNT_FATAL);
// And so does ephemeral
AuthSession* auth_session2 = auth_session_manager_->CreateAuthSession(
kUsername2, AUTH_SESSION_FLAGS_EPHEMERAL_USER, AuthIntent::kDecrypt);
status = PrepareEphemeralVaultImpl(auth_session2->serialized_token());
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY);
// But a different regular mount succeeds.
auto user_session3 = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session3, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session3, MountVault(kUsername3, _, _))
.WillOnce(ReturnError<CryptohomeMountError>());
EXPECT_CALL(user_session_factory_, New(_, _, _))
.WillOnce(Return(ByMove(std::move(user_session3))));
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername3)))
.WillRepeatedly(Return(true));
ExpectAuth(kUsername3, brillo::SecureBlob(kPassword3));
AuthSession* auth_session3 = auth_session_manager_->CreateAuthSession(
kUsername3, 0, AuthIntent::kDecrypt);
ExpectVaultKeyset(/*num_of_keysets=*/1);
TestFuture<CryptohomeStatus> authenticate_third_future;
auth_session3->Authenticate(CreateAuthorization(kPassword3),
authenticate_third_future.GetCallback());
EXPECT_THAT(authenticate_third_future.Get(), IsOk());
ASSERT_TRUE(
PreparePersistentVaultImpl(auth_session3->serialized_token(), {}).ok());
}
// Test if PreparePersistentVaultImpl can succeed with invalid authSession. It
// should not.
TEST_F(AuthSessionInterfaceTest, PreparePersistentVaultWithInvalidAuthSession) {
// No auth session.
CryptohomeStatus status =
PreparePersistentVaultImpl(/*auth_session_id=*/"", /*vault_options=*/{});
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_legacy_error(),
user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN);
}
// Test for checking if PreparePersistentVaultImpl will proceed with
// unauthenticated auth session.
TEST_F(AuthSessionInterfaceTest,
PreparePersistentVaultWithUnAuthenticatedAuthSession) {
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
CryptohomeStatus status =
PreparePersistentVaultImpl(auth_session->serialized_token(), {});
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
}
// Test to check if PreparePersistentVaultImpl will succeed if user is not
// created.
TEST_F(AuthSessionInterfaceTest, PreparePersistentVaultNoShadowDir) {
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
SetAuthSessionAsAuthenticated(auth_session, kAllAuthIntents);
// If no shadow homedir - we do not have a user.
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(false));
CryptohomeStatus status =
PreparePersistentVaultImpl(auth_session->serialized_token(), {});
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND);
}
// Test to check if PreparePersistentVaultImpl will succeed in happy case and
// calls the required functions.
TEST_F(AuthSessionInterfaceTest, PreparePersistentVaultRegularCase) {
MockOwnerUser("whoever", homedirs_);
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
// Auth and prepare.
auto user_session = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session, IsEphemeral()).WillRepeatedly(Return(false));
EXPECT_CALL(*user_session, GetCredentialVerifier()).WillOnce(Return(nullptr));
EXPECT_CALL(*user_session, SetCredentials(auth_session));
EXPECT_CALL(*user_session, MountVault(kUsername, _, _))
.WillOnce(ReturnError<CryptohomeMountError>());
EXPECT_CALL(user_session_factory_, New(kUsername, _, _))
.WillOnce(Return(ByMove(std::move(user_session))));
ExpectVaultKeyset(/*num_of_keysets=*/1);
ExpectAuth(kUsername, brillo::SecureBlob(kPassword));
// Set up expectation for authenticate callback success.
user_data_auth::AuthenticateAuthSessionRequest request;
request.set_auth_session_id(auth_session->serialized_token());
*request.mutable_authorization() = CreateAuthorization(kPassword);
base::MockCallback<AuthenticateCallback> on_done;
user_data_auth::AuthenticateAuthSessionReply reply;
EXPECT_CALL(on_done, Run(testing::_)).WillOnce(testing::SaveArg<0>(&reply));
AuthenticateAuthSession(request, on_done.Get());
ASSERT_THAT(reply.error(), Eq(MOUNT_ERROR_NONE));
// User authed and exists.
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(true));
ASSERT_TRUE(
PreparePersistentVaultImpl(auth_session->serialized_token(), {}).ok());
}
// Test to check if PreparePersistentVaultImpl will succeed, call required
// functions and not succeed when PreparePersistentVault is called twice.
TEST_F(AuthSessionInterfaceTest, PreparePersistentVaultSecondMountPointBusy) {
MockOwnerUser("whoever", homedirs_);
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
// Auth and prepare.
auto user_session = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session, IsEphemeral()).WillRepeatedly(Return(false));
EXPECT_CALL(*user_session, GetCredentialVerifier()).WillOnce(Return(nullptr));
EXPECT_CALL(*user_session, SetCredentials(auth_session));
EXPECT_CALL(*user_session, MountVault(kUsername, _, _))
.WillOnce(ReturnError<CryptohomeMountError>());
EXPECT_CALL(user_session_factory_, New(kUsername, _, _))
.WillOnce(Return(ByMove(std::move(user_session))));
ExpectVaultKeyset(/*num_of_keysets=*/1);
ExpectAuth(kUsername, brillo::SecureBlob(kPassword));
// Set up expectation for authenticate callback success.
user_data_auth::AuthenticateAuthSessionRequest request;
request.set_auth_session_id(auth_session->serialized_token());
*request.mutable_authorization() = CreateAuthorization(kPassword);
user_data_auth::AuthenticateAuthSessionReply reply;
base::MockCallback<AuthenticateCallback> on_done;
EXPECT_CALL(on_done, Run(_)).WillOnce(SaveArg<0>(&reply));
AuthenticateAuthSession(request, on_done.Get());
ASSERT_THAT(reply.error(), Eq(MOUNT_ERROR_NONE));
// User authed and exists.
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(true));
ASSERT_TRUE(
PreparePersistentVaultImpl(auth_session->serialized_token(), {}).ok());
// Trying to mount again will yield busy.
auto status =
PreparePersistentVaultImpl(auth_session->serialized_token(), {});
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY);
}
TEST_F(AuthSessionInterfaceTest, PreparePersistentVaultAndThenGuestFail) {
// Test to check if PreparePersistentVaultImpl will succeed, call required
// functions and mounting guest would not succeed.
MockOwnerUser("whoever", homedirs_);
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
// Auth and prepare.
auto user_session = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session, MountVault(kUsername, _, _))
.WillOnce(ReturnError<CryptohomeMountError>());
EXPECT_CALL(user_session_factory_, New(kUsername, _, _))
.WillOnce(Return(ByMove(std::move(user_session))));
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(true));
// Set up expectations.
ExpectVaultKeyset(/*num_of_keysets=*/1);
ExpectAuth(kUsername, brillo::SecureBlob(kPassword));
TestFuture<CryptohomeStatus> authenticate_future;
auth_session->Authenticate(CreateAuthorization(kPassword),
authenticate_future.GetCallback());
// Evaluate error returned by callback.
EXPECT_THAT(authenticate_future.Get(), IsOk());
// User authed and exists.
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(true));
ASSERT_TRUE(
PreparePersistentVaultImpl(auth_session->serialized_token(), {}).ok());
// Guest fails if other sessions present.
auto status = PrepareGuestVaultImpl();
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_MOUNT_FATAL);
}
TEST_F(AuthSessionInterfaceTest, PreparePersistentVaultAndEphemeral) {
// Test to check if PreparePersistentVaultImpl will succeed, call required
// functions and mounting ephemeral will succeed as we support multi mount for
// that.
MockOwnerUser("whoever", homedirs_);
// Setup regular user.
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
// Auth and prepare.
auto user_session = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session, IsEphemeral()).WillRepeatedly(Return(false));
EXPECT_CALL(*user_session, GetCredentialVerifier()).WillOnce(Return(nullptr));
EXPECT_CALL(*user_session, SetCredentials(auth_session));
EXPECT_CALL(*user_session, MountVault(kUsername, _, _))
.WillOnce(ReturnError<CryptohomeMountError>());
EXPECT_CALL(user_session_factory_, New(kUsername, _, _))
.WillOnce(Return(ByMove(std::move(user_session))));
ExpectVaultKeyset(/*num_of_keysets=*/1);
ExpectAuth(kUsername, brillo::SecureBlob(kPassword));
// Set up expectation for authenticate callback success.
user_data_auth::AuthenticateAuthSessionRequest request;
request.set_auth_session_id(auth_session->serialized_token());
*request.mutable_authorization() = CreateAuthorization(kPassword);
user_data_auth::AuthenticateAuthSessionReply reply;
base::MockCallback<AuthenticateCallback> on_done;
EXPECT_CALL(on_done, Run(_)).WillOnce(SaveArg<0>(&reply));
AuthenticateAuthSession(request, on_done.Get());
ASSERT_THAT(reply.error(), Eq(MOUNT_ERROR_NONE));
// User authed and exists.
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(true));
ASSERT_TRUE(
PreparePersistentVaultImpl(auth_session->serialized_token(), {}).ok());
// Setup ephemeral user. This should fail.
AuthSession* auth_session2 = auth_session_manager_->CreateAuthSession(
kUsername2, AUTH_SESSION_FLAGS_EPHEMERAL_USER, AuthIntent::kDecrypt);
CryptohomeStatus status =
PrepareEphemeralVaultImpl(auth_session2->serialized_token());
ASSERT_FALSE(status.ok());
ASSERT_EQ(status->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY);
}
// Test to check if PreparePersistentVaultImpl will succeed, call required
// functions and PreparePersistentVault will succeed for another user as we
// support multi mount.
TEST_F(AuthSessionInterfaceTest, PreparePersistentVaultMultiMount) {
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
// Auth and prepare.
auto user_session = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session, IsEphemeral()).WillRepeatedly(Return(false));
EXPECT_CALL(*user_session, GetCredentialVerifier()).WillOnce(Return(nullptr));
EXPECT_CALL(*user_session, SetCredentials(auth_session));
EXPECT_CALL(*user_session, MountVault(kUsername, _, _))
.WillOnce(ReturnError<CryptohomeMountError>());
EXPECT_CALL(user_session_factory_, New(kUsername, _, _))
.WillOnce(Return(ByMove(std::move(user_session))));
ExpectVaultKeyset(/*num_of_keysets=*/1);
ExpectAuth(kUsername, brillo::SecureBlob(kPassword));
// Set up expectation for authenticate callback success.
user_data_auth::AuthenticateAuthSessionRequest request;
request.set_auth_session_id(auth_session->serialized_token());
*request.mutable_authorization() = CreateAuthorization(kPassword);
user_data_auth::AuthenticateAuthSessionReply reply;
base::MockCallback<AuthenticateCallback> on_done;
EXPECT_CALL(on_done, Run(_)).WillOnce(SaveArg<0>(&reply));
AuthenticateAuthSession(request, on_done.Get());
ASSERT_THAT(reply.error(), Eq(MOUNT_ERROR_NONE));
// User authed and exists.
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(true));
ASSERT_TRUE(
PreparePersistentVaultImpl(auth_session->serialized_token(), {}).ok());
// Second mount should also succeed.
AuthSession* auth_session2 = auth_session_manager_->CreateAuthSession(
kUsername2, 0, AuthIntent::kDecrypt);
auto user_session2 = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session2, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session2, IsEphemeral()).WillRepeatedly(Return(false));
EXPECT_CALL(*user_session2, GetCredentialVerifier())
.WillOnce(Return(nullptr));
EXPECT_CALL(*user_session2, SetCredentials(auth_session2));
EXPECT_CALL(*user_session2, MountVault(kUsername2, _, _))
.WillOnce(ReturnError<CryptohomeMountError>());
EXPECT_CALL(user_session_factory_, New(_, _, _))
.WillOnce(Return(ByMove(std::move(user_session2))));
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername2)))
.WillRepeatedly(Return(true));
// Set up expectation for authenticate callback success.
user_data_auth::AuthenticateAuthSessionRequest request2;
user_data_auth::AuthenticateAuthSessionReply reply2;
request2.set_auth_session_id(auth_session2->serialized_token());
AuthorizationRequest auth_req2 = CreateAuthorization(kPassword2);
request2.mutable_authorization()->Swap(&auth_req2);
base::MockCallback<AuthenticateCallback> on_done2;
EXPECT_CALL(on_done2, Run(_)).WillOnce(SaveArg<0>(&reply2));
ExpectVaultKeyset(/*num_of_keysets=*/1);
ExpectAuth(kUsername2, brillo::SecureBlob(kPassword2));
AuthenticateAuthSession(request2, on_done2.Get());
ASSERT_TRUE(
PreparePersistentVaultImpl(auth_session2->serialized_token(), {}).ok());
// Evaluate error returned by callback.
ASSERT_THAT(reply2.error(), Eq(MOUNT_ERROR_NONE));
}
// Test CreatePersistentUserImpl with invalid auth_session.
TEST_F(AuthSessionInterfaceTest, CreatePersistentUserInvalidAuthSession) {
// No auth session.
ASSERT_THAT(CreatePersistentUserImpl("")->local_legacy_error().value(),
Eq(user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN));
}
// Test CreatePersistentUserImpl with valid auth_session but user fails to
// create.
TEST_F(AuthSessionInterfaceTest, CreatePersistentUserFailedCreate) {
EXPECT_CALL(homedirs_, CryptohomeExists(SanitizeUserName(kUsername)))
.WillOnce(ReturnValue(false));
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillOnce(Return(false));
EXPECT_CALL(homedirs_, Create(kUsername)).WillOnce(Return(false));
auto status = CreatePersistentUserImpl(auth_session->serialized_token());
ASSERT_FALSE(status.ok());
ASSERT_THAT(status->local_legacy_error(),
Eq(user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE));
}
// Test CreatePersistentUserImpl when Vault already exists.
TEST_F(AuthSessionInterfaceTest, CreatePersistentUserVaultExists) {
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
EXPECT_CALL(homedirs_, CryptohomeExists(SanitizeUserName(kUsername)))
.WillOnce(ReturnValue(true));
ASSERT_THAT(CreatePersistentUserImpl(auth_session->serialized_token())
->local_legacy_error()
.value(),
Eq(user_data_auth::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY));
}
// Test CreatePersistentUserImpl with regular and expected case.
TEST_F(AuthSessionInterfaceTest, CreatePersistentUserRegular) {
EXPECT_CALL(keyset_management_, UserExists(SanitizeUserName(kUsername)))
.WillOnce(ReturnValue(false));
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
EXPECT_FALSE(auth_session->user_exists());
// User doesn't exist and created.
EXPECT_CALL(homedirs_, CryptohomeExists(SanitizeUserName(kUsername)))
.WillOnce(ReturnValue(false));
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillOnce(Return(false));
EXPECT_CALL(homedirs_, Create(kUsername)).WillOnce(Return(true));
ASSERT_TRUE(CreatePersistentUserImpl(auth_session->serialized_token()).ok());
EXPECT_THAT(auth_session->GetStatus(), AuthStatus::kAuthStatusAuthenticated);
// Set UserSession expectations for upcoming mount.
// Auth and prepare.
auto owned_user_session = std::make_unique<MockUserSession>();
auto* const user_session = owned_user_session.get();
EXPECT_CALL(user_session_factory_, New(kUsername, _, _))
.WillOnce(Return(ByMove(std::move(owned_user_session))));
EXPECT_CALL(*user_session, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session, MountVault(kUsername, _, _))
.WillOnce(ReturnError<CryptohomeMountError>());
// User authed and exists.
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(true));
ASSERT_TRUE(
PreparePersistentVaultImpl(auth_session->serialized_token(), {}).ok());
// Set expectations for credential verifier.
EXPECT_CALL(*user_session, IsEphemeral()).WillRepeatedly(Return(false));
EXPECT_CALL(*user_session, GetCredentialVerifier()).WillOnce(Return(nullptr));
EXPECT_CALL(*user_session, SetCredentials(auth_session));
// Set up expectation for add credential callback success.
user_data_auth::AddCredentialsRequest request;
user_data_auth::AddCredentialsReply reply;
request.set_auth_session_id(auth_session->serialized_token());
*request.mutable_authorization() = CreateAuthorization(kPassword);
base::MockCallback<AddCredentialCallback> on_done;
EXPECT_CALL(on_done, Run(testing::_)).WillOnce(testing::SaveArg<0>(&reply));
EXPECT_CALL(keyset_management_,
AddInitialKeysetWithKeyBlobs(_, _, _, _, _, _))
.WillOnce(Return(ByMove(std::make_unique<VaultKeyset>())));
AddCredentials(request, on_done.Get());
// Evaluate error returned by callback.
ASSERT_THAT(reply.error(), Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
}
TEST_F(AuthSessionInterfaceTest, CreatePersistentUserRepeatCall) {
EXPECT_CALL(keyset_management_, UserExists(SanitizeUserName(kUsername)))
.WillOnce(ReturnValue(false));
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
EXPECT_CALL(homedirs_, CryptohomeExists(SanitizeUserName(kUsername)))
.WillOnce(ReturnValue(false));
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillOnce(Return(false));
EXPECT_CALL(homedirs_, Create(kUsername)).WillOnce(Return(true));
ASSERT_TRUE(CreatePersistentUserImpl(auth_session->serialized_token()).ok());
EXPECT_THAT(auth_session->GetStatus(), AuthStatus::kAuthStatusAuthenticated);
// Called again. User exists, vault should not be created again.
EXPECT_CALL(homedirs_, CryptohomeExists(SanitizeUserName(kUsername)))
.WillOnce(ReturnValue(false));
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillOnce(Return(true));
ASSERT_TRUE(CreatePersistentUserImpl(auth_session->serialized_token()).ok());
}
TEST_F(AuthSessionInterfaceTest, AuthenticateAuthSessionNoLabel) {
// Auth session not authed.
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
// Pass no label in the request.
AuthorizationRequest auth_req;
auth_req.mutable_key()->set_secret(kPassword);
auth_req.mutable_key()->mutable_data()->set_type(KeyData::KEY_TYPE_PASSWORD);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session->Authenticate(auth_req, authenticate_future.GetCallback());
// Evaluate error returned by callback.
ASSERT_THAT(authenticate_future.Get(), NotOk());
EXPECT_EQ(authenticate_future.Get()->local_legacy_error(),
user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
}
TEST_F(AuthSessionInterfaceTest, GetAuthSessionStatus) {
user_data_auth::GetAuthSessionStatusReply reply;
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
// Test 1.
auth_session->SetStatus(AuthStatus::kAuthStatusFurtherFactorRequired);
GetAuthSessionStatusImpl(auth_session, reply);
ASSERT_THAT(reply.status(),
Eq(user_data_auth::AUTH_SESSION_STATUS_FURTHER_FACTOR_REQUIRED));
// Test 2.
auth_session->SetStatus(AuthStatus::kAuthStatusTimedOut);
GetAuthSessionStatusImpl(auth_session, reply);
ASSERT_THAT(reply.status(),
Eq(user_data_auth::AUTH_SESSION_STATUS_INVALID_AUTH_SESSION));
}
TEST_F(AuthSessionInterfaceTest, GetHibernateSecretUnauthenticatedTest) {
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
// Verify an unauthenticated session fails in producing a hibernate secret.
user_data_auth::GetHibernateSecretRequest request;
request.set_auth_session_id(auth_session->serialized_token());
user_data_auth::GetHibernateSecretReply hs_reply =
userdataauth_.GetHibernateSecret(request);
ASSERT_NE(hs_reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
ASSERT_FALSE(hs_reply.hibernate_secret().length());
}
TEST_F(AuthSessionInterfaceTest, GetHibernateSecretTest) {
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, 0, AuthIntent::kDecrypt);
ExpectAuth(kUsername, brillo::SecureBlob(kPassword));
ExpectVaultKeyset(/*num_of_keysets=*/1);
TestFuture<CryptohomeStatus> authenticate_future;
auth_session->Authenticate(CreateAuthorization(kPassword),
authenticate_future.GetCallback());
// Evaluate error returned by callback.
EXPECT_THAT(authenticate_future.Get(), IsOk());
// Verify that a successfully authenticated session produces a hibernate
// secret.
user_data_auth::GetHibernateSecretRequest request;
request.set_auth_session_id(auth_session->serialized_token());
user_data_auth::GetHibernateSecretReply hs_reply =
userdataauth_.GetHibernateSecret(request);
ASSERT_EQ(hs_reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
ASSERT_TRUE(hs_reply.hibernate_secret().length());
}
} // namespace
class AuthSessionInterfaceMockAuthTest : public AuthSessionInterfaceTestBase {
protected:
AuthSessionInterfaceMockAuthTest() {
userdataauth_.set_auth_block_utility(&mock_auth_block_utility_);
CreateAuthSessionManager(&mock_auth_block_utility_);
}
user_data_auth::AddAuthFactorReply AddAuthFactor(
const user_data_auth::AddAuthFactorRequest& request) {
TestFuture<user_data_auth::AddAuthFactorReply> reply_future;
userdataauth_.AddAuthFactor(
request,
reply_future.GetCallback<const user_data_auth::AddAuthFactorReply&>());
return reply_future.Get();
}
user_data_auth::AddAuthFactorReply AddPasswordAuthFactor(
const AuthSession& auth_session,
const std::string& auth_factor_label,
const std::string& password) {
user_data_auth::AddAuthFactorRequest add_request;
add_request.set_auth_session_id(auth_session.serialized_token());
user_data_auth::AuthFactor& request_factor =
*add_request.mutable_auth_factor();
request_factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request_factor.set_label(auth_factor_label);
request_factor.mutable_password_metadata();
add_request.mutable_auth_input()->mutable_password_input()->set_secret(
password);
return AddAuthFactor(add_request);
}
user_data_auth::AuthenticateAuthFactorReply AuthenticateAuthFactor(
const user_data_auth::AuthenticateAuthFactorRequest& request) {
TestFuture<user_data_auth::AuthenticateAuthFactorReply> reply_future;
userdataauth_.AuthenticateAuthFactor(
request,
reply_future
.GetCallback<const user_data_auth::AuthenticateAuthFactorReply&>());
return reply_future.Get();
}
user_data_auth::AuthenticateAuthFactorReply AuthenticatePasswordAuthFactor(
const AuthSession& auth_session,
const std::string& auth_factor_label,
const std::string& password) {
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session.serialized_token());
request.set_auth_factor_label(auth_factor_label);
request.mutable_auth_input()->mutable_password_input()->set_secret(
password);
return AuthenticateAuthFactor(request);
}
// Simulates a new user creation flow by running `CreatePersistentUser` and
// `PreparePersistentVault`. Sets up all necessary mocks. Returns an
// authenticated AuthSession, or null on failure.
AuthSession* CreateAndPrepareUserVault() {
EXPECT_CALL(keyset_management_, UserExists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(false));
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, /*flags=*/0, AuthIntent::kDecrypt);
if (!auth_session)
return nullptr;
// Create the user.
EXPECT_CALL(homedirs_, CryptohomeExists(SanitizeUserName(kUsername)))
.WillOnce(ReturnValue(false));
EXPECT_CALL(homedirs_, Create(kUsername)).WillOnce(Return(true));
EXPECT_THAT(CreatePersistentUserImpl(auth_session->serialized_token()),
IsOk());
// Prepare the user vault. Use the real user session class to exercise
// internal state transitions.
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(true));
auto mount = base::MakeRefCounted<MockMount>();
EXPECT_CALL(*mount, IsMounted())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
auto user_session = std::make_unique<RealUserSession>(
kUsername, &homedirs_, &keyset_management_,
&user_activity_timestamp_manager_, &pkcs11_token_factory_, mount);
EXPECT_CALL(user_session_factory_, New(kUsername, _, _))
.WillOnce(Return(ByMove(std::move(user_session))));
EXPECT_THAT(PreparePersistentVaultImpl(auth_session->serialized_token(),
/*vault_options=*/{}),
IsOk());
return auth_session;
}
AuthSession* PrepareEphemeralUser() {
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, AUTH_SESSION_FLAGS_EPHEMERAL_USER, AuthIntent::kDecrypt);
if (!auth_session)
return nullptr;
// Set up mocks for the user session creation. Use the real user session
// class to exercise internal state transitions.
auto mount = base::MakeRefCounted<MockMount>();
EXPECT_CALL(*mount, IsMounted())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mount, MountEphemeralCryptohome(kUsername))
.WillOnce(ReturnOk<StorageError>());
EXPECT_CALL(*mount, IsEphemeral()).WillRepeatedly(Return(true));
auto user_session = std::make_unique<RealUserSession>(
kUsername, &homedirs_, &keyset_management_,
&user_activity_timestamp_manager_, &pkcs11_token_factory_, mount);
EXPECT_CALL(user_session_factory_, New(kUsername, _, _))
.WillOnce(Return(ByMove(std::move(user_session))));
EXPECT_THAT(PrepareEphemeralVaultImpl(auth_session->serialized_token()),
IsOk());
return auth_session;
}
MockAuthBlockUtility mock_auth_block_utility_;
};
namespace {
// Test that AddAuthFactor succeeds for a freshly created user.
TEST_F(AuthSessionInterfaceMockAuthTest, AddFactorNewUserVk) {
const std::string obfuscated_username = SanitizeUserName(kUsername);
// Arrange.
AuthSession* const auth_session = CreateAndPrepareUserVault();
ASSERT_TRUE(auth_session);
const SerializedVaultKeyset serialized_vk =
CreateFakePasswordVk(kPasswordLabel);
MockKeysetCreation(mock_auth_block_utility_);
MockInitialKeysetAdding(obfuscated_username, serialized_vk,
keyset_management_);
MockKeysetLoadingByLabel(obfuscated_username, serialized_vk,
keyset_management_);
// Act.
user_data_auth::AddAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
user_data_auth::AuthFactor& request_factor = *request.mutable_auth_factor();
request_factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
request_factor.set_label(kPasswordLabel);
request_factor.mutable_password_metadata();
request.mutable_auth_input()->mutable_password_input()->set_secret(kPassword);
user_data_auth::AddAuthFactorReply reply = AddAuthFactor(request);
// Assert.
EXPECT_EQ(reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
UserSession* found_user_session =
userdataauth_.FindUserSessionForTest(kUsername);
ASSERT_TRUE(found_user_session);
EXPECT_TRUE(found_user_session->IsActive());
// Check the user session has a verifier for the given password.
Credentials credentials(kUsername, brillo::SecureBlob(kPassword));
EXPECT_TRUE(found_user_session->VerifyCredentials(credentials));
}
// Test that AddAuthFactor succeeds when adding a second factor for a freshly
// created user, but the credential verifier remains using the first credential.
TEST_F(AuthSessionInterfaceMockAuthTest, AddSecondFactorNewUserVk) {
const std::string obfuscated_username = SanitizeUserName(kUsername);
// Arrange.
AuthSession* const auth_session = CreateAndPrepareUserVault();
ASSERT_TRUE(auth_session);
const SerializedVaultKeyset serialized_vk =
CreateFakePasswordVk(kPasswordLabel);
MockKeysetCreation(mock_auth_block_utility_);
MockInitialKeysetAdding(obfuscated_username, serialized_vk,
keyset_management_);
MockKeysetLoadingByLabel(obfuscated_username, serialized_vk,
keyset_management_);
// Add the initial keyset.
user_data_auth::AddAuthFactorRequest password_request;
password_request.set_auth_session_id(auth_session->serialized_token());
user_data_auth::AuthFactor& password_factor =
*password_request.mutable_auth_factor();
password_factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PASSWORD);
password_factor.set_label(kPasswordLabel);
password_factor.mutable_password_metadata();
password_request.mutable_auth_input()->mutable_password_input()->set_secret(
kPassword);
EXPECT_EQ(AddAuthFactor(password_request).error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Set up mocks for adding the second keyset.
MockKeysetCreation(mock_auth_block_utility_);
MockKeysetLoadingByLabel(obfuscated_username, CreateFakePinVk(kPinLabel),
keyset_management_);
// Act.
user_data_auth::AddAuthFactorRequest pin_request;
pin_request.set_auth_session_id(auth_session->serialized_token());
user_data_auth::AuthFactor& pin_factor = *pin_request.mutable_auth_factor();
pin_factor.set_type(user_data_auth::AUTH_FACTOR_TYPE_PIN);
pin_factor.set_label(kPinLabel);
pin_factor.mutable_pin_metadata();
pin_request.mutable_auth_input()->mutable_pin_input()->set_secret(kPin);
user_data_auth::AddAuthFactorReply reply = AddAuthFactor(pin_request);
// Assert.
EXPECT_EQ(reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
UserSession* found_user_session =
userdataauth_.FindUserSessionForTest(kUsername);
ASSERT_TRUE(found_user_session);
EXPECT_TRUE(found_user_session->IsActive());
// Check the user session has a verifier for the first keyset's password.
Credentials credentials(kUsername, brillo::SecureBlob(kPassword));
EXPECT_TRUE(found_user_session->VerifyCredentials(credentials));
}
// Test that AuthenticateAuthFactor succeeds for an existing user and a
// VautKeyset-based factor when using the correct credential.
TEST_F(AuthSessionInterfaceMockAuthTest, AuthenticateAuthFactorVkSuccess) {
const std::string obfuscated_username = SanitizeUserName(kUsername);
// Arrange.
EXPECT_CALL(keyset_management_, UserExists(obfuscated_username))
.WillRepeatedly(ReturnValue(true));
const SerializedVaultKeyset serialized_vk =
CreateFakePasswordVk(kPasswordLabel);
MockLabelToKeyDataMapLoading(obfuscated_username, {serialized_vk},
keyset_management_);
MockKeysetsLoading(obfuscated_username, {serialized_vk}, keyset_management_);
MockKeysetLoadingByIndex(obfuscated_username, /*index=*/0, serialized_vk,
keyset_management_);
MockKeysetLoadingByLabel(obfuscated_username, serialized_vk,
keyset_management_);
MockKeysetDerivation(obfuscated_username, serialized_vk, CryptoError::CE_NONE,
mock_auth_block_utility_);
MockKeysetLoadingViaBlobs(obfuscated_username, serialized_vk,
keyset_management_);
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, /*flags=*/0, AuthIntent::kDecrypt);
ASSERT_TRUE(auth_session);
// Act.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_label(kPasswordLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kPassword);
const user_data_auth::AuthenticateAuthFactorReply reply =
AuthenticateAuthFactor(request);
// Assert.
EXPECT_EQ(reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_TRUE(reply.authenticated());
EXPECT_THAT(
reply.authorized_for(),
UnorderedElementsAre(AUTH_INTENT_DECRYPT, AUTH_INTENT_VERIFY_ONLY));
EXPECT_EQ(userdataauth_.FindUserSessionForTest(kUsername), nullptr);
}
// Test that AuthenticateAuthFactor fails in case the VaultKeyset decryption
// failed.
TEST_F(AuthSessionInterfaceMockAuthTest,
AuthenticateAuthFactorVkDecryptionError) {
const std::string obfuscated_username = SanitizeUserName(kUsername);
// Arrange. Mock VK decryption to return a failure.
EXPECT_CALL(keyset_management_, UserExists(obfuscated_username))
.WillRepeatedly(ReturnValue(true));
const SerializedVaultKeyset serialized_vk =
CreateFakePasswordVk(kPasswordLabel);
MockLabelToKeyDataMapLoading(obfuscated_username, {serialized_vk},
keyset_management_);
MockKeysetsLoading(obfuscated_username, {serialized_vk}, keyset_management_);
MockKeysetLoadingByIndex(obfuscated_username, /*index=*/0, serialized_vk,
keyset_management_);
MockKeysetLoadingByLabel(obfuscated_username, serialized_vk,
keyset_management_);
MockKeysetDerivation(obfuscated_username, serialized_vk,
CryptoError::CE_OTHER_CRYPTO, mock_auth_block_utility_);
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, /*flags=*/0, AuthIntent::kDecrypt);
ASSERT_TRUE(auth_session);
// Act.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_label(kPasswordLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kPassword);
const user_data_auth::AuthenticateAuthFactorReply reply =
AuthenticateAuthFactor(request);
// Assert.
EXPECT_EQ(reply.error(),
user_data_auth::CRYPTOHOME_ERROR_AUTHORIZATION_KEY_FAILED);
EXPECT_FALSE(reply.authenticated());
EXPECT_THAT(reply.authorized_for(), IsEmpty());
EXPECT_EQ(userdataauth_.FindUserSessionForTest(kUsername), nullptr);
}
// Test that AuthenticateAuthFactor succeeds using credential verifier based
// lightweight authentication when `AuthIntent::kVerifyOnly` is requested.
TEST_F(AuthSessionInterfaceMockAuthTest, AuthenticateAuthFactorLightweight) {
const std::string obfuscated_username = SanitizeUserName(kUsername);
// Arrange. Set up a fake VK without authentication mocks.
EXPECT_CALL(keyset_management_, UserExists(obfuscated_username))
.WillRepeatedly(ReturnValue(true));
const SerializedVaultKeyset serialized_vk =
CreateFakePasswordVk(kPasswordLabel);
MockLabelToKeyDataMapLoading(obfuscated_username, {serialized_vk},
keyset_management_);
MockKeysetsLoading(obfuscated_username, {serialized_vk}, keyset_management_);
MockKeysetLoadingByIndex(obfuscated_username, /*index=*/0, serialized_vk,
keyset_management_);
MockKeysetLoadingByLabel(obfuscated_username, serialized_vk,
keyset_management_);
// Set up a user session with a mocked credential verifier.
auto user_session = std::make_unique<MockUserSession>();
EXPECT_CALL(*user_session, VerifyCredentials(_)).WillOnce(Return(true));
EXPECT_TRUE(user_session_map_.Add(kUsername, std::move(user_session)));
// Create an AuthSession.
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, /*flags=*/0, AuthIntent::kVerifyOnly);
ASSERT_TRUE(auth_session);
// Act.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_label(kPasswordLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kPassword);
const user_data_auth::AuthenticateAuthFactorReply reply =
AuthenticateAuthFactor(request);
// Assert. The legacy `authenticated` field stays false.
EXPECT_EQ(reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_FALSE(reply.authenticated());
EXPECT_THAT(reply.authorized_for(),
UnorderedElementsAre(AUTH_INTENT_VERIFY_ONLY));
}
// Test that AuthenticateAuthFactor fails in case the AuthSession ID is missing.
TEST_F(AuthSessionInterfaceMockAuthTest, AuthenticateAuthFactorNoSessionId) {
const std::string obfuscated_username = SanitizeUserName(kUsername);
// Arrange.
EXPECT_CALL(keyset_management_, UserExists(obfuscated_username))
.WillRepeatedly(ReturnValue(false));
// Act. Omit setting `auth_session_id` in the `request`.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_factor_label(kPasswordLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kPassword);
const user_data_auth::AuthenticateAuthFactorReply reply =
AuthenticateAuthFactor(request);
// Assert.
EXPECT_EQ(reply.error(),
user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN);
EXPECT_FALSE(reply.authenticated());
EXPECT_THAT(reply.authorized_for(), IsEmpty());
EXPECT_EQ(userdataauth_.FindUserSessionForTest(kUsername), nullptr);
}
// Test that AuthenticateAuthFactor fails in case the AuthSession ID is invalid.
TEST_F(AuthSessionInterfaceMockAuthTest, AuthenticateAuthFactorBadSessionId) {
const std::string obfuscated_username = SanitizeUserName(kUsername);
// Arrange.
EXPECT_CALL(keyset_management_, UserExists(obfuscated_username))
.WillRepeatedly(ReturnValue(false));
// Act.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id("bad-session-id");
request.set_auth_factor_label(kPasswordLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kPassword);
const user_data_auth::AuthenticateAuthFactorReply reply =
AuthenticateAuthFactor(request);
// Assert.
EXPECT_EQ(reply.error(),
user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN);
EXPECT_FALSE(reply.authenticated());
EXPECT_THAT(reply.authorized_for(), IsEmpty());
EXPECT_EQ(userdataauth_.FindUserSessionForTest(kUsername), nullptr);
}
// Test that AuthenticateAuthFactor fails in case the AuthSession is expired.
TEST_F(AuthSessionInterfaceMockAuthTest, AuthenticateAuthFactorExpiredSession) {
const std::string obfuscated_username = SanitizeUserName(kUsername);
// Arrange.
EXPECT_CALL(keyset_management_, UserExists(obfuscated_username))
.WillRepeatedly(ReturnValue(false));
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, /*flags=*/0, AuthIntent::kDecrypt);
ASSERT_TRUE(auth_session);
const auto auth_session_id = auth_session->serialized_token();
EXPECT_TRUE(auth_session_manager_->RemoveAuthSession(auth_session_id));
// Act.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session_id);
request.set_auth_factor_label(kPasswordLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kPassword);
const user_data_auth::AuthenticateAuthFactorReply reply =
AuthenticateAuthFactor(request);
// Assert.
EXPECT_EQ(reply.error(),
user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN);
EXPECT_FALSE(reply.authenticated());
EXPECT_THAT(reply.authorized_for(), IsEmpty());
EXPECT_EQ(userdataauth_.FindUserSessionForTest(kUsername), nullptr);
}
// Test that AuthenticateAuthFactor fails in case the user doesn't exist.
TEST_F(AuthSessionInterfaceMockAuthTest, AuthenticateAuthFactorNoUser) {
const std::string obfuscated_username = SanitizeUserName(kUsername);
// Arrange.
EXPECT_CALL(keyset_management_, UserExists(obfuscated_username))
.WillRepeatedly(ReturnValue(false));
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, /*flags=*/0, AuthIntent::kDecrypt);
ASSERT_TRUE(auth_session);
// Act.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_label(kPasswordLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kPassword);
const user_data_auth::AuthenticateAuthFactorReply reply =
AuthenticateAuthFactor(request);
// Assert.
EXPECT_EQ(reply.error(), user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND);
EXPECT_FALSE(reply.authenticated());
EXPECT_THAT(reply.authorized_for(), IsEmpty());
EXPECT_EQ(userdataauth_.FindUserSessionForTest(kUsername), nullptr);
}
// Test that AuthenticateAuthFactor fails in case the user has no keys (because
// the user is just created). The AuthSession, however, stays authenticated.
TEST_F(AuthSessionInterfaceMockAuthTest, AuthenticateAuthFactorNoKeys) {
const std::string obfuscated_username = SanitizeUserName(kUsername);
// Arrange.
EXPECT_CALL(keyset_management_, UserExists(obfuscated_username))
.WillRepeatedly(ReturnValue(false));
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, /*flags=*/0, AuthIntent::kDecrypt);
ASSERT_TRUE(auth_session);
EXPECT_THAT(auth_session->OnUserCreated(), IsOk());
EXPECT_EQ(auth_session->GetStatus(), AuthStatus::kAuthStatusAuthenticated);
EXPECT_THAT(
auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
// Act.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_label(kPasswordLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kPassword);
const user_data_auth::AuthenticateAuthFactorReply reply =
AuthenticateAuthFactor(request);
// Assert.
EXPECT_EQ(reply.error(), user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND);
EXPECT_TRUE(reply.authenticated());
EXPECT_THAT(
reply.authorized_for(),
UnorderedElementsAre(AUTH_INTENT_DECRYPT, AUTH_INTENT_VERIFY_ONLY));
EXPECT_EQ(userdataauth_.FindUserSessionForTest(kUsername), nullptr);
}
// Test that AuthenticateAuthFactor fails when a non-existing key label is
// specified.
TEST_F(AuthSessionInterfaceMockAuthTest, AuthenticateAuthFactorWrongVkLabel) {
constexpr char kConfiguredKeyLabel[] = "fake-configured-label";
constexpr char kRequestedKeyLabel[] = "fake-requested-label";
const std::string obfuscated_username = SanitizeUserName(kUsername);
// Arrange.
EXPECT_CALL(keyset_management_, UserExists(obfuscated_username))
.WillRepeatedly(ReturnValue(true));
const SerializedVaultKeyset serialized_vk =
CreateFakePasswordVk(kConfiguredKeyLabel);
MockLabelToKeyDataMapLoading(obfuscated_username, {serialized_vk},
keyset_management_);
MockKeysetsLoading(obfuscated_username, {serialized_vk}, keyset_management_);
MockKeysetLoadingByIndex(obfuscated_username, /*index=*/0, serialized_vk,
keyset_management_);
MockKeysetLoadingByLabel(obfuscated_username, serialized_vk,
keyset_management_);
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, /*flags=*/0, AuthIntent::kDecrypt);
ASSERT_TRUE(auth_session);
// Act.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_label(kRequestedKeyLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kPassword);
const user_data_auth::AuthenticateAuthFactorReply reply =
AuthenticateAuthFactor(request);
// Assert.
EXPECT_EQ(reply.error(), user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND);
EXPECT_FALSE(reply.authenticated());
EXPECT_THAT(reply.authorized_for(), IsEmpty());
EXPECT_EQ(userdataauth_.FindUserSessionForTest(kUsername), nullptr);
}
// Test that AuthenticateAuthFactor fails when no AuthInput is provided.
TEST_F(AuthSessionInterfaceMockAuthTest, AuthenticateAuthFactorNoInput) {
const std::string obfuscated_username = SanitizeUserName(kUsername);
// Arrange.
EXPECT_CALL(keyset_management_, UserExists(obfuscated_username))
.WillRepeatedly(ReturnValue(true));
const SerializedVaultKeyset serialized_vk =
CreateFakePasswordVk(kPasswordLabel);
MockLabelToKeyDataMapLoading(obfuscated_username, {serialized_vk},
keyset_management_);
MockKeysetsLoading(obfuscated_username, {serialized_vk}, keyset_management_);
MockKeysetLoadingByIndex(obfuscated_username, /*index=*/0, serialized_vk,
keyset_management_);
MockKeysetLoadingByLabel(obfuscated_username, serialized_vk,
keyset_management_);
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, /*flags=*/0, AuthIntent::kDecrypt);
ASSERT_TRUE(auth_session);
// Act. Omit setting `auth_input` in `request`.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_label(kPasswordLabel);
const user_data_auth::AuthenticateAuthFactorReply reply =
AuthenticateAuthFactor(request);
// Assert.
EXPECT_EQ(reply.error(), user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT);
EXPECT_FALSE(reply.authenticated());
EXPECT_THAT(reply.authorized_for(), IsEmpty());
EXPECT_EQ(userdataauth_.FindUserSessionForTest(kUsername), nullptr);
}
// Test the PreparePersistentVault, when called after a successful
// AuthenticateAuthFactor, mounts the home dir and sets up the user session.
TEST_F(AuthSessionInterfaceMockAuthTest, PrepareVaultAfterFactorAuthVk) {
const std::string obfuscated_username = SanitizeUserName(kUsername);
// Arrange.
EXPECT_CALL(keyset_management_, UserExists(obfuscated_username))
.WillRepeatedly(Return(true));
// Mock successful authentication via a VaultKeyset.
const SerializedVaultKeyset serialized_vk =
CreateFakePasswordVk(kPasswordLabel);
MockLabelToKeyDataMapLoading(obfuscated_username, {serialized_vk},
keyset_management_);
MockKeysetsLoading(obfuscated_username, {serialized_vk}, keyset_management_);
MockKeysetLoadingByIndex(obfuscated_username, /*index=*/0, serialized_vk,
keyset_management_);
MockKeysetLoadingByLabel(obfuscated_username, serialized_vk,
keyset_management_);
MockKeysetDerivation(obfuscated_username, serialized_vk, CryptoError::CE_NONE,
mock_auth_block_utility_);
MockKeysetLoadingViaBlobs(obfuscated_username, serialized_vk,
keyset_management_);
// Prepare an AuthSession.
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, /*flags=*/0, AuthIntent::kDecrypt);
ASSERT_TRUE(auth_session);
// Authenticate the AuthSession.
user_data_auth::AuthenticateAuthFactorRequest request;
request.set_auth_session_id(auth_session->serialized_token());
request.set_auth_factor_label(kPasswordLabel);
request.mutable_auth_input()->mutable_password_input()->set_secret(kPassword);
EXPECT_EQ(AuthenticateAuthFactor(request).error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Mock user vault mounting. Use the real user session class in order to check
// session state transitions.
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(true));
auto mount = base::MakeRefCounted<MockMount>();
EXPECT_CALL(*mount, IsMounted())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
auto user_session = std::make_unique<RealUserSession>(
kUsername, &homedirs_, &keyset_management_,
&user_activity_timestamp_manager_, &pkcs11_token_factory_, mount);
EXPECT_CALL(user_session_factory_, New(kUsername, _, _))
.WillOnce(Return(ByMove(std::move(user_session))));
// Act.
CryptohomeStatus prepare_status = PreparePersistentVaultImpl(
auth_session->serialized_token(), /*vault_options=*/{});
// Assert.
EXPECT_THAT(prepare_status, IsOk());
UserSession* found_user_session =
userdataauth_.FindUserSessionForTest(kUsername);
ASSERT_TRUE(found_user_session);
EXPECT_TRUE(found_user_session->IsActive());
// Check the user session has a verifier for the given password.
Credentials credentials(kUsername, brillo::SecureBlob(kPassword));
EXPECT_TRUE(found_user_session->VerifyCredentials(credentials));
}
// That that AddAuthFactor succeeds for a freshly prepared ephemeral user. The
// credential is stored in the user session as a verifier.
TEST_F(AuthSessionInterfaceMockAuthTest,
AddPasswordFactorAfterPrepareEphemeral) {
// Arrange.
// Pretend to have a different owner user, because otherwise the ephemeral
// login is disallowed.
MockOwnerUser("whoever", homedirs_);
// Prepare the ephemeral vault, which should also create the session.
AuthSession* const auth_session = PrepareEphemeralUser();
ASSERT_TRUE(auth_session);
UserSession* found_user_session =
userdataauth_.FindUserSessionForTest(kUsername);
ASSERT_TRUE(found_user_session);
EXPECT_TRUE(found_user_session->IsActive());
EXPECT_THAT(found_user_session->GetCredentialVerifier(), IsNull());
// Act.
user_data_auth::AddAuthFactorReply reply =
AddPasswordAuthFactor(*auth_session, kPasswordLabel, kPassword);
// Assert.
EXPECT_EQ(reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Check the user session has a verifier for the given password.
EXPECT_THAT(found_user_session->GetCredentialVerifier(), NotNull());
Credentials credentials(kUsername, brillo::SecureBlob(kPassword));
EXPECT_TRUE(found_user_session->VerifyCredentials(credentials));
EXPECT_THAT(
auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kDecrypt, AuthIntent::kVerifyOnly));
}
// Test that AuthenticateAuthFactor succeeds for a freshly prepared ephemeral
// user who has a password added.
TEST_F(AuthSessionInterfaceMockAuthTest,
AuthenticatePasswordFactorForEphemeral) {
// Arrange.
// Pretend to have a different owner user, because otherwise the ephemeral
// login is disallowed.
MockOwnerUser("whoever", homedirs_);
AuthSession* const first_auth_session = PrepareEphemeralUser();
ASSERT_TRUE(first_auth_session);
EXPECT_EQ(
AddPasswordAuthFactor(*first_auth_session, kPasswordLabel, kPassword)
.error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Act.
AuthSession* const second_auth_session =
auth_session_manager_->CreateAuthSession(
kUsername, AUTH_SESSION_FLAGS_EPHEMERAL_USER,
AuthIntent::kVerifyOnly);
ASSERT_TRUE(second_auth_session);
user_data_auth::AuthenticateAuthFactorReply reply =
AuthenticatePasswordAuthFactor(*second_auth_session, kPasswordLabel,
kPassword);
// Assert.
EXPECT_EQ(reply.error(), user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
EXPECT_THAT(second_auth_session->authorized_intents(),
UnorderedElementsAre(AuthIntent::kVerifyOnly));
}
// Test that AuthenticateAuthFactor fails for a freshly prepared ephemeral user
// if a wrong password is provided.
TEST_F(AuthSessionInterfaceMockAuthTest,
AuthenticatePasswordFactorForEphemeralWrongPassword) {
// Arrange.
// Pretend to have a different owner user, because otherwise the ephemeral
// login is disallowed.
MockOwnerUser("whoever", homedirs_);
// Prepare the ephemeral user with a password configured.
AuthSession* const first_auth_session = PrepareEphemeralUser();
ASSERT_TRUE(first_auth_session);
EXPECT_EQ(
AddPasswordAuthFactor(*first_auth_session, kPasswordLabel, kPassword)
.error(),
user_data_auth::CRYPTOHOME_ERROR_NOT_SET);
// Act.
AuthSession* const second_auth_session =
auth_session_manager_->CreateAuthSession(
kUsername, AUTH_SESSION_FLAGS_EPHEMERAL_USER,
AuthIntent::kVerifyOnly);
ASSERT_TRUE(second_auth_session);
user_data_auth::AuthenticateAuthFactorReply reply =
AuthenticatePasswordAuthFactor(*second_auth_session, kPasswordLabel,
kPassword2);
// Assert. The error code is such because AuthSession falls back to checking
// persistent auth factors.
EXPECT_EQ(reply.error(), user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND);
EXPECT_THAT(second_auth_session->authorized_intents(), IsEmpty());
}
// Test that AuthenticateAuthFactor fails for a freshly prepared ephemeral user
// if no password was configured.
TEST_F(AuthSessionInterfaceMockAuthTest,
AuthenticatePasswordFactorForEphemeralNoPassword) {
// Arrange.
// Pretend to have a different owner user, because otherwise the ephemeral
// login is disallowed.
MockOwnerUser("whoever", homedirs_);
// Prepare the ephemeral user without any factor configured.
EXPECT_TRUE(PrepareEphemeralUser());
// Act.
AuthSession* const auth_session = auth_session_manager_->CreateAuthSession(
kUsername, AUTH_SESSION_FLAGS_EPHEMERAL_USER, AuthIntent::kVerifyOnly);
ASSERT_TRUE(auth_session);
user_data_auth::AuthenticateAuthFactorReply reply =
AuthenticatePasswordAuthFactor(*auth_session, kPasswordLabel, kPassword);
// Assert. The error code is such because AuthSession falls back to checking
// persistent auth factors.
EXPECT_EQ(reply.error(), user_data_auth::CRYPTOHOME_ERROR_KEY_NOT_FOUND);
EXPECT_THAT(auth_session->authorized_intents(), IsEmpty());
}
} // namespace
} // namespace cryptohome