blob: e8cc8fac5415ece2f197cbab9c89bc0ced0643e8 [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "cryptohome/userdataauth.h"
#include <memory>
#include <utility>
#include <base/test/task_environment.h>
#include <brillo/cryptohome.h>
#include <brillo/secure_blob.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "cryptohome/auth_blocks/mock_auth_block_utility.h"
#include "cryptohome/auth_factor/auth_factor_manager.h"
#include "cryptohome/auth_session_manager.h"
#include "cryptohome/cleanup/mock_user_oldest_activity_timestamp_manager.h"
#include "cryptohome/credentials.h"
#include "cryptohome/credentials_test_util.h"
#include "cryptohome/crypto.h"
#include "cryptohome/filesystem_layout.h"
#include "cryptohome/mock_install_attributes.h"
#include "cryptohome/mock_keyset_management.h"
#include "cryptohome/mock_pkcs11_init.h"
#include "cryptohome/mock_platform.h"
#include "cryptohome/pkcs11/mock_pkcs11_token_factory.h"
#include "cryptohome/storage/mock_homedirs.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/vault_keyset.h"
namespace cryptohome {
using ::testing::_;
using ::testing::ByMove;
using ::testing::DoAll;
using ::testing::Eq;
using ::testing::Invoke;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SetArgPointee;
using base::test::TaskEnvironment;
using brillo::cryptohome::home::kGuestUserName;
using brillo::cryptohome::home::SanitizeUserName;
using error::CryptohomeMountError;
using hwsec_foundation::status::OkStatus;
using user_data_auth::AuthSessionFlags::AUTH_SESSION_FLAGS_EPHEMERAL_USER;
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";
} // namespace
class AuthSessionInterfaceTest : public ::testing::Test {
public:
AuthSessionInterfaceTest() : crypto_(&platform_) {}
~AuthSessionInterfaceTest() override = default;
AuthSessionInterfaceTest(const AuthSessionInterfaceTest&) = delete;
AuthSessionInterfaceTest& operator=(const AuthSessionInterfaceTest&) = delete;
void SetUp() override {
auth_session_manager_ = std::make_unique<AuthSessionManager>(
&crypto_, &keyset_management_, &auth_block_utility_,
&auth_factor_manager_, &user_secret_stash_storage_);
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_auth_session_manager(auth_session_manager_.get());
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);
}
protected:
TaskEnvironment task_environment{
TaskEnvironment::ThreadPoolExecutionMode::QUEUED};
NiceMock<MockPlatform> platform_;
Crypto crypto_;
NiceMock<MockHomeDirs> homedirs_;
NiceMock<MockUserSessionFactory> user_session_factory_;
NiceMock<MockAuthBlockUtility> auth_block_utility_;
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.
user_data_auth::CryptohomeErrorCode PrepareGuestVaultImpl() {
return userdataauth_.PrepareGuestVaultImpl();
}
user_data_auth::CryptohomeErrorCode PrepareEphemeralVaultImpl(
const std::string& auth_session_id) {
return userdataauth_.PrepareEphemeralVaultImpl(auth_session_id);
}
user_data_auth::CryptohomeErrorCode 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);
}
user_data_auth::CryptohomeErrorCode HandleAddCredentialForEphemeralVault(
AuthorizationRequest request, const AuthSession* auth_session) {
return userdataauth_.HandleAddCredentialForEphemeralVault(request,
auth_session);
}
void GetAuthSessionStatusImpl(
AuthSession* auth_session,
user_data_auth::GetAuthSessionStatusReply& reply) {
userdataauth_.GetAuthSessionStatusImpl(auth_session, reply);
}
AuthorizationRequest CreateAuthorization(const std::string& secret) {
AuthorizationRequest req;
req.mutable_key()->set_secret(secret);
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_,
GetValidKeyset(CredentialsMatcher(creds), _))
.WillOnce(DoAll(SetArgPointee<1>(MOUNT_ERROR_NONE),
Return(ByMove(std::move(vk)))));
}
};
namespace {
TEST_F(AuthSessionInterfaceTest, PrepareGuestVault) {
scoped_refptr<MockUserSession> user_session =
base::MakeRefCounted<MockUserSession>();
EXPECT_CALL(user_session_factory_, New(_, _)).WillOnce(Return(user_session));
EXPECT_CALL(*user_session, IsActive()).WillRepeatedly(Return(true));
EXPECT_CALL(*user_session, MountGuest()).WillOnce(Invoke([]() {
return OkStatus<CryptohomeMountError>();
}));
// 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_THAT(PrepareGuestVaultImpl(),
Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
// Trying to prepare another session should fail, whether it is guest, ...
ASSERT_THAT(PrepareGuestVaultImpl(),
Eq(user_data_auth::CRYPTOHOME_ERROR_MOUNT_FATAL));
// ... ephemeral, ...
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, AUTH_SESSION_FLAGS_EPHEMERAL_USER);
ASSERT_TRUE(auth_session->Authenticate(CreateAuthorization(kPassword)).ok());
ASSERT_THAT(PrepareEphemeralVaultImpl(auth_session->serialized_token()),
Eq(user_data_auth::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY));
// ... or regular.
auth_session = auth_session_manager_->CreateAuthSession(kUsername2, 0);
ASSERT_TRUE(auth_session->Authenticate(CreateAuthorization(kPassword2)).ok());
ASSERT_THAT(PreparePersistentVaultImpl(auth_session->serialized_token(), {}),
Eq(user_data_auth::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY));
}
TEST_F(AuthSessionInterfaceTest, PrepareEphemeralVault) {
EXPECT_CALL(homedirs_, GetPlainOwner(_))
.WillRepeatedly(
DoAll(SetArgPointee<0>(std::string("whoever")), Return(true)));
// No auth session.
ASSERT_THAT(PrepareEphemeralVaultImpl(""),
Eq(user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN));
// Auth session is authed for ephemeral users.
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, AUTH_SESSION_FLAGS_EPHEMERAL_USER);
// User authed and exists.
scoped_refptr<MockUserSession> user_session =
base::MakeRefCounted<MockUserSession>();
EXPECT_CALL(user_session_factory_, New(_, _)).WillOnce(Return(user_session));
EXPECT_CALL(*user_session, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session, MountEphemeral(kUsername))
.WillOnce(Return(MOUNT_ERROR_NONE));
ASSERT_THAT(PrepareEphemeralVaultImpl(auth_session->serialized_token()),
Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
// Trying to mount again will yield busy.
ASSERT_THAT(PrepareEphemeralVaultImpl(auth_session->serialized_token()),
Eq(user_data_auth::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY));
// Guest fails if other sessions present.
ASSERT_THAT(PrepareGuestVaultImpl(),
Eq(user_data_auth::CRYPTOHOME_ERROR_MOUNT_FATAL));
// But ephemeral succeeds ...
scoped_refptr<MockUserSession> user_session2 =
base::MakeRefCounted<MockUserSession>();
EXPECT_CALL(user_session_factory_, New(_, _)).WillOnce(Return(user_session2));
EXPECT_CALL(*user_session2, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session2, IsEphemeral()).WillRepeatedly(Return(true));
EXPECT_CALL(*user_session2, MountEphemeral(kUsername2))
.WillOnce(Return(MOUNT_ERROR_NONE));
AuthSession* auth_session2 = auth_session_manager_->CreateAuthSession(
kUsername2, AUTH_SESSION_FLAGS_EPHEMERAL_USER);
ASSERT_THAT(PrepareEphemeralVaultImpl(auth_session2->serialized_token()),
Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
ASSERT_THAT(HandleAddCredentialForEphemeralVault(
CreateAuthorization(kPassword3), auth_session2),
Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
// ... and so regular.
scoped_refptr<MockUserSession> user_session3 =
base::MakeRefCounted<MockUserSession>();
EXPECT_CALL(user_session_factory_, New(_, _)).WillOnce(Return(user_session3));
EXPECT_CALL(*user_session3, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session3, MountVault(kUsername3, _, _))
.WillOnce(Return(MOUNT_ERROR_NONE));
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername3)))
.WillRepeatedly(Return(true));
ExpectAuth(kUsername3, brillo::SecureBlob(kPassword3));
AuthSession* auth_session3 =
auth_session_manager_->CreateAuthSession(kUsername3, 0);
ASSERT_TRUE(
auth_session3->Authenticate(CreateAuthorization(kPassword3)).ok());
ASSERT_THAT(PreparePersistentVaultImpl(auth_session3->serialized_token(), {}),
Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
}
TEST_F(AuthSessionInterfaceTest, PreparePersistentVault) {
EXPECT_CALL(homedirs_, GetPlainOwner(_))
.WillRepeatedly(
DoAll(SetArgPointee<0>(std::string("whoever")), Return(true)));
// No auth session.
ASSERT_THAT(PreparePersistentVaultImpl("", {}),
Eq(user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN));
// Auth session not authed.
AuthSession* auth_session =
auth_session_manager_->CreateAuthSession(kUsername, 0);
ASSERT_THAT(PreparePersistentVaultImpl(auth_session->serialized_token(), {}),
Eq(user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT));
// Auth and prepare.
scoped_refptr<MockUserSession> user_session =
base::MakeRefCounted<MockUserSession>();
EXPECT_CALL(user_session_factory_, New(_, _)).WillOnce(Return(user_session));
EXPECT_CALL(*user_session, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session, MountVault(kUsername, _, _))
.WillOnce(Return(MOUNT_ERROR_NONE));
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(true));
ExpectAuth(kUsername, brillo::SecureBlob(kPassword));
ASSERT_TRUE(auth_session->Authenticate(CreateAuthorization(kPassword)).ok());
// If no shadow homedir - we do not have a user.
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(false));
ASSERT_THAT(PreparePersistentVaultImpl(auth_session->serialized_token(), {}),
Eq(user_data_auth::CRYPTOHOME_ERROR_ACCOUNT_NOT_FOUND));
// User authed and exists.
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillRepeatedly(Return(true));
ASSERT_THAT(PreparePersistentVaultImpl(auth_session->serialized_token(), {}),
Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
// Trying to mount again will yield busy.
ASSERT_THAT(PreparePersistentVaultImpl(auth_session->serialized_token(), {}),
Eq(user_data_auth::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY));
// Guest fails if other sessions present.
ASSERT_THAT(PrepareGuestVaultImpl(),
Eq(user_data_auth::CRYPTOHOME_ERROR_MOUNT_FATAL));
// But ephemeral succeeds ...
scoped_refptr<MockUserSession> user_session2 =
base::MakeRefCounted<MockUserSession>();
EXPECT_CALL(user_session_factory_, New(_, _)).WillOnce(Return(user_session2));
EXPECT_CALL(*user_session2, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session2, MountEphemeral(kUsername2))
.WillOnce(Return(MOUNT_ERROR_NONE));
AuthSession* auth_session2 = auth_session_manager_->CreateAuthSession(
kUsername2, AUTH_SESSION_FLAGS_EPHEMERAL_USER);
ASSERT_TRUE(
auth_session2->Authenticate(CreateAuthorization(kPassword2)).ok());
ASSERT_THAT(PrepareEphemeralVaultImpl(auth_session2->serialized_token()),
Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
// ... and so regular.
scoped_refptr<MockUserSession> user_session3 =
base::MakeRefCounted<MockUserSession>();
EXPECT_CALL(user_session_factory_, New(_, _)).WillOnce(Return(user_session3));
EXPECT_CALL(*user_session3, IsActive())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*user_session3, MountVault(kUsername3, _, _))
.WillOnce(Return(MOUNT_ERROR_NONE));
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername3)))
.WillRepeatedly(Return(true));
ExpectAuth(kUsername3, brillo::SecureBlob(kPassword3));
AuthSession* auth_session3 =
auth_session_manager_->CreateAuthSession(kUsername3, 0);
ASSERT_TRUE(
auth_session3->Authenticate(CreateAuthorization(kPassword3)).ok());
ASSERT_THAT(PreparePersistentVaultImpl(auth_session3->serialized_token(), {}),
Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
}
TEST_F(AuthSessionInterfaceTest, CreatePersistentUser) {
// No auth session.
ASSERT_THAT(CreatePersistentUserImpl("")->local_legacy_error().value(),
Eq(user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN));
// Auth session not authed.
AuthSession* auth_session =
auth_session_manager_->CreateAuthSession(kUsername, 0);
ExpectAuth(kUsername, brillo::SecureBlob(kPassword));
ASSERT_TRUE(auth_session->Authenticate(CreateAuthorization(kPassword)).ok());
// Vault already exists.
EXPECT_CALL(homedirs_, CryptohomeExists(SanitizeUserName(kUsername), _))
.WillOnce(Return(true));
ASSERT_THAT(CreatePersistentUserImpl(auth_session->serialized_token())
->local_legacy_error()
.value(),
Eq(user_data_auth::CRYPTOHOME_ERROR_MOUNT_MOUNT_POINT_BUSY));
// User doesn't exist and failed to create.
EXPECT_CALL(homedirs_, CryptohomeExists(SanitizeUserName(kUsername), _))
.WillOnce(Return(false));
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillOnce(Return(false));
EXPECT_CALL(homedirs_, Create(kUsername)).WillOnce(Return(false));
ASSERT_THAT(CreatePersistentUserImpl(auth_session->serialized_token())
->local_legacy_error()
.value(),
Eq(user_data_auth::CRYPTOHOME_ERROR_BACKING_STORE_FAILURE));
// User doesn't exist and created.
EXPECT_CALL(homedirs_, CryptohomeExists(SanitizeUserName(kUsername), _))
.WillOnce(Return(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());
// User exists but vault doesn't.
EXPECT_CALL(homedirs_, CryptohomeExists(SanitizeUserName(kUsername), _))
.WillOnce(Return(false));
EXPECT_CALL(homedirs_, Exists(SanitizeUserName(kUsername)))
.WillOnce(Return(true));
ASSERT_TRUE(CreatePersistentUserImpl(auth_session->serialized_token()).ok());
}
TEST_F(AuthSessionInterfaceTest, GetAuthSessionStatus) {
user_data_auth::GetAuthSessionStatusReply reply;
AuthSession* auth_session =
auth_session_manager_->CreateAuthSession(kUsername, 0);
// 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));
}
} // namespace
} // namespace cryptohome