| // 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, 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, 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, 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, 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, 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, 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, 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 |