blob: e3ff1f459e51ab6cef2831a4888785f86a57c0ad [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_session_manager.h"
#include "cryptohome/cleanup/mock_user_oldest_activity_timestamp_manager.h"
#include "cryptohome/credentials.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/storage/mock_mount.h"
#include "cryptohome/storage/mock_mount_factory.h"
#include "cryptohome/vault_keyset.h"
namespace cryptohome {
using ::testing::_;
using ::testing::ByMove;
using ::testing::DoAll;
using ::testing::Eq;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SetArgPointee;
using base::test::TaskEnvironment;
using brillo::cryptohome::home::kGuestUserName;
using brillo::cryptohome::home::SanitizeUserName;
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";
MATCHER_P(CredentialsMatcher, creds, "") {
if (creds.username() != arg.username()) {
return false;
}
if (creds.passkey() != arg.passkey()) {
return false;
}
return true;
}
} // 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>(&keyset_management_);
brillo::SecureBlob system_salt;
InitializeFilesystemLayout(&platform_, &crypto_, &system_salt);
platform_.GetFake()->SetSystemSaltForLibbrillo(system_salt);
userdataauth_.set_platform(&platform_);
userdataauth_.set_homedirs(&homedirs_);
userdataauth_.set_mount_factory(&mount_factory_);
userdataauth_.set_keyset_management(&keyset_management_);
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);
}
void TearDown() override {
platform_.GetFake()->RemoveSystemSaltForLibbrillo();
}
protected:
TaskEnvironment task_environment{
TaskEnvironment::ThreadPoolExecutionMode::QUEUED};
NiceMock<MockPlatform> platform_;
Crypto crypto_;
NiceMock<MockHomeDirs> homedirs_;
NiceMock<MockMountFactory> mount_factory_;
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);
}
user_data_auth::CryptohomeErrorCode CreatePersistentUserImpl(
const std::string& auth_session_id) {
return userdataauth_.CreatePersistentUserImpl(auth_session_id);
}
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) {
// Mount factory returns a raw pointer, which caller wraps into unique_ptr.
MockMount* mount = new MockMount();
EXPECT_CALL(mount_factory_, New(_, _)).WillOnce(Return(mount));
EXPECT_CALL(*mount, Init(_)).WillOnce(Return(true));
EXPECT_CALL(*mount, IsMounted()).WillRepeatedly(Return(true));
EXPECT_CALL(*mount, MountEphemeralCryptohome(kGuestUserName))
.WillOnce(Return(MOUNT_ERROR_NONE));
// 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_THAT(auth_session->Authenticate(CreateAuthorization(kPassword)),
Eq(MOUNT_ERROR_NONE));
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_THAT(auth_session->Authenticate(CreateAuthorization(kPassword2)),
Eq(MOUNT_ERROR_NONE));
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 not authed.
AuthSession* auth_session = auth_session_manager_->CreateAuthSession(
kUsername, AUTH_SESSION_FLAGS_EPHEMERAL_USER);
ASSERT_THAT(PrepareEphemeralVaultImpl(auth_session->serialized_token()),
Eq(user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT));
// User authed and exists.
// Mount factory returns a raw pointer, which caller wraps into unique_ptr.
MockMount* mount = new MockMount();
EXPECT_CALL(mount_factory_, New(_, _)).WillOnce(Return(mount));
EXPECT_CALL(*mount, Init(_)).WillOnce(Return(true));
EXPECT_CALL(*mount, IsMounted())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mount, MountEphemeralCryptohome(kUsername))
.WillOnce(Return(MOUNT_ERROR_NONE));
ASSERT_THAT(auth_session->Authenticate(CreateAuthorization(kPassword)),
Eq(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 ...
// Mount factory returns a raw pointer, which caller wraps into unique_ptr.
MockMount* mount2 = new MockMount();
EXPECT_CALL(mount_factory_, New(_, _)).WillOnce(Return(mount2));
EXPECT_CALL(*mount2, Init(_)).WillOnce(Return(true));
EXPECT_CALL(*mount2, IsMounted())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mount2, MountEphemeralCryptohome(kUsername2))
.WillOnce(Return(MOUNT_ERROR_NONE));
AuthSession* auth_session2 = auth_session_manager_->CreateAuthSession(
kUsername2, AUTH_SESSION_FLAGS_EPHEMERAL_USER);
ASSERT_THAT(auth_session2->Authenticate(CreateAuthorization(kPassword2)),
Eq(MOUNT_ERROR_NONE));
ASSERT_THAT(PrepareEphemeralVaultImpl(auth_session2->serialized_token()),
Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
// ... and so regular.
// Mount factory returns a raw pointer, which caller wraps into unique_ptr.
MockMount* mount3 = new MockMount();
EXPECT_CALL(mount_factory_, New(_, _)).WillOnce(Return(mount3));
EXPECT_CALL(*mount3, Init(_)).WillOnce(Return(true));
EXPECT_CALL(*mount3, IsMounted())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mount3, MountCryptohome(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_THAT(auth_session3->Authenticate(CreateAuthorization(kPassword3)),
Eq(MOUNT_ERROR_NONE));
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.
// Mount factory returns a raw pointer, which caller wraps into unique_ptr.
MockMount* mount = new MockMount();
EXPECT_CALL(mount_factory_, New(_, _)).WillOnce(Return(mount));
EXPECT_CALL(*mount, Init(_)).WillOnce(Return(true));
EXPECT_CALL(*mount, IsMounted())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mount, MountCryptohome(kUsername, _, _))
.WillOnce(Return(MOUNT_ERROR_NONE));
ExpectAuth(kUsername, brillo::SecureBlob(kPassword));
ASSERT_THAT(auth_session->Authenticate(CreateAuthorization(kPassword)),
Eq(MOUNT_ERROR_NONE));
// 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 ...
// Mount factory returns a raw pointer, which caller wraps into unique_ptr.
MockMount* mount2 = new MockMount();
EXPECT_CALL(mount_factory_, New(_, _)).WillOnce(Return(mount2));
EXPECT_CALL(*mount2, Init(_)).WillOnce(Return(true));
EXPECT_CALL(*mount2, IsMounted())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mount2, MountEphemeralCryptohome(kUsername2))
.WillOnce(Return(MOUNT_ERROR_NONE));
AuthSession* auth_session2 = auth_session_manager_->CreateAuthSession(
kUsername2, AUTH_SESSION_FLAGS_EPHEMERAL_USER);
ASSERT_THAT(auth_session2->Authenticate(CreateAuthorization(kPassword2)),
Eq(MOUNT_ERROR_NONE));
ASSERT_THAT(PrepareEphemeralVaultImpl(auth_session2->serialized_token()),
Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
// ... and so regular.
MockMount* mount3 = new MockMount();
// Mount factory returns a raw pointer, which caller wraps into unique_ptr.
EXPECT_CALL(mount_factory_, New(_, _)).WillOnce(Return(mount3));
EXPECT_CALL(*mount3, Init(_)).WillOnce(Return(true));
EXPECT_CALL(*mount3, IsMounted())
.WillOnce(Return(false))
.WillRepeatedly(Return(true));
EXPECT_CALL(*mount3, MountCryptohome(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_THAT(auth_session3->Authenticate(CreateAuthorization(kPassword3)),
Eq(MOUNT_ERROR_NONE));
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(""),
Eq(user_data_auth::CRYPTOHOME_INVALID_AUTH_SESSION_TOKEN));
// Auth session not authed.
AuthSession* auth_session =
auth_session_manager_->CreateAuthSession(kUsername, 0);
ASSERT_THAT(CreatePersistentUserImpl(auth_session->serialized_token()),
Eq(user_data_auth::CRYPTOHOME_ERROR_INVALID_ARGUMENT));
ExpectAuth(kUsername, brillo::SecureBlob(kPassword));
ASSERT_THAT(auth_session->Authenticate(CreateAuthorization(kPassword)),
Eq(MOUNT_ERROR_NONE));
// Vault already exists.
EXPECT_CALL(homedirs_, CryptohomeExists(SanitizeUserName(kUsername), _))
.WillOnce(Return(true));
ASSERT_THAT(CreatePersistentUserImpl(auth_session->serialized_token()),
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(SanitizeUserName(kUsername)))
.WillOnce(Return(false));
ASSERT_THAT(CreatePersistentUserImpl(auth_session->serialized_token()),
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(SanitizeUserName(kUsername)))
.WillOnce(Return(true));
ASSERT_THAT(CreatePersistentUserImpl(auth_session->serialized_token()),
Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
// 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_THAT(CreatePersistentUserImpl(auth_session->serialized_token()),
Eq(user_data_auth::CRYPTOHOME_ERROR_NOT_SET));
}
} // namespace
} // namespace cryptohome