#include "estoraged_test.hpp" #include "estoraged.hpp" #include "estoraged_conf.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace estoraged_test { using sdbusplus::server::xyz::openbmc_project::inventory::item::Volume; using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; using sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound; using std::filesystem::path; using ::testing::_; using ::testing::Return; using ::testing::StrEq; class EStoragedTest : public testing::Test { public: const char* testFileName = "testfile"; const char* testLuksDevName = "testfile_luksDev"; const char* testCryptDir = "/tmp"; const std::string testConfigPath = "/xyz/openbmc_project/inventory/system/board/test_board/test_emmc"; const uint64_t testSize = 24; const uint8_t testLifeTime = 25; const std::string testPartNumber = "12345678"; const std::string testSerialNumber = "ABCDEF1234"; const std::string testLocationCode = "U102020"; const std::string testDriveType = "SSD"; const std::string testDriveProtocol = "eMMC"; std::ofstream testFile; std::string passwordString; std::vector password; MockCryptsetupInterface* mockCryptIface{}; MockFilesystemInterface* mockFsIface{}; boost::asio::io_context io; std::shared_ptr conn; std::unique_ptr objectServer; std::unique_ptr esObject; EStoragedTest() : passwordString("password"), password(passwordString.begin(), passwordString.end()) {} void SetUp() override { /* Create an empty file that we'll pretend is a 'storage device'. */ testFile.open(testFileName, std::ios::out | std::ios::binary | std::ios::trunc); testFile.close(); if (testFile.fail()) { throw std::runtime_error("Failed to open test file"); } std::unique_ptr cryptIface = std::make_unique(); mockCryptIface = cryptIface.get(); std::unique_ptr fsIface = std::make_unique(); mockFsIface = fsIface.get(); /* Set up location of dummy mapped crypt file. */ EXPECT_CALL(*cryptIface, cryptGetDir).WillOnce(Return(testCryptDir)); conn = std::make_shared(io); // request D-Bus server name. conn->request_name("xyz.openbmc_project.eStoraged.test"); objectServer = std::make_unique(conn); esObject = std::make_unique( *objectServer, testConfigPath, testFileName, testLuksDevName, testSize, testLifeTime, testPartNumber, testSerialNumber, testLocationCode, ERASE_MAX_GEOMETRY, ERASE_MIN_GEOMETRY, testDriveType, testDriveProtocol, std::move(cryptIface), std::move(fsIface)); } void TearDown() override { EXPECT_EQ(0, unlink(testFileName)); } }; const char* mappedDevicePath = "/tmp/testfile_luksDev"; std::ofstream mappedDevice; int createMappedDev() { mappedDevice.open(mappedDevicePath, std::ios::out | std::ios::binary | std::ios::trunc); mappedDevice.close(); if (mappedDevice.fail()) { throw std::runtime_error("Failed to open test mapped device"); } return 0; } int removeMappedDev() { EXPECT_EQ(0, unlink(mappedDevicePath)); return 0; } /* Test case to format and then lock the LUKS device. */ TEST_F(EStoragedTest, FormatPass) { EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) .Times(1); EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) .WillOnce(&createMappedDev); EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) .WillOnce(Return(false)); EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) .WillOnce(Return(true)); EXPECT_CALL(*mockFsIface, doMount(StrEq(esObject->getCryptDevicePath()), StrEq(esObject->getMountPoint()), _, _, _)) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) .WillOnce(Return(true)); EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)) .WillOnce(&removeMappedDev); /* Format the encrypted device. */ esObject->formatLuks(password, Volume::FilesystemType::ext4); EXPECT_FALSE(esObject->isLocked()); esObject->lock(); EXPECT_TRUE(esObject->isLocked()); } /* * Test case where the mount point directory already exists, so it shouldn't * try to create it. */ TEST_F(EStoragedTest, MountPointExistsPass) { EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) .Times(1); EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) .WillOnce(&createMappedDev); EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) .WillOnce(Return(true)); EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) .Times(0); EXPECT_CALL(*mockFsIface, doMount(StrEq(esObject->getCryptDevicePath()), StrEq(esObject->getMountPoint()), _, _, _)) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) .WillOnce(Return(true)); EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)) .WillOnce(&removeMappedDev); /* Format the encrypted device. */ esObject->formatLuks(password, Volume::FilesystemType::ext4); EXPECT_FALSE(esObject->isLocked()); esObject->lock(); EXPECT_TRUE(esObject->isLocked()); } /* Test case where the device/file doesn't exist. */ TEST_F(EStoragedTest, FormatNoDeviceFail) { /* Delete the test file. */ EXPECT_EQ(0, unlink(testFileName)); EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), ResourceNotFound); EXPECT_TRUE(esObject->isLocked()); /* Create the test file again, so that the TearDown function works. */ testFile.open(testFileName, std::ios::out | std::ios::binary | std::ios::trunc); testFile.close(); } /* Test case where we fail to format the LUKS device. */ TEST_F(EStoragedTest, FormatFail) { EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)) .WillOnce(Return(-1)); EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), InternalFailure); EXPECT_TRUE(esObject->isLocked()); } /* Test case where we fail to set the password for the LUKS device. */ TEST_F(EStoragedTest, AddKeyslotFail) { EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) .WillOnce(Return(-1)); EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), InternalFailure); EXPECT_TRUE(esObject->isLocked()); } /* Test case where we fail to load the LUKS header. */ TEST_F(EStoragedTest, LoadLuksHeaderFail) { EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) .Times(1); EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).WillOnce(Return(-1)); EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), InternalFailure); EXPECT_TRUE(esObject->isLocked()); } /* Test case where we fail to activate the LUKS device. */ TEST_F(EStoragedTest, ActivateFail) { EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) .Times(1); EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) .WillOnce(Return(-1)); EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), InternalFailure); EXPECT_TRUE(esObject->isLocked()); } /* Test case where we fail to create the filesystem. */ TEST_F(EStoragedTest, CreateFilesystemFail) { EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) .Times(1); EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) .WillOnce(&createMappedDev); EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) .WillOnce(Return(-1)); EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), InternalFailure); EXPECT_FALSE(esObject->isLocked()); EXPECT_EQ(0, removeMappedDev()); } /* Test case where we fail to create the mount point. */ TEST_F(EStoragedTest, CreateMountPointFail) { EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) .Times(1); EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) .WillOnce(&createMappedDev); EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) .WillOnce(Return(false)); EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) .WillOnce(Return(false)); EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), InternalFailure); EXPECT_FALSE(esObject->isLocked()); EXPECT_EQ(0, removeMappedDev()); } /* Test case where we fail to mount the filesystem. */ TEST_F(EStoragedTest, MountFail) { EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) .Times(1); EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) .WillOnce(&createMappedDev); EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) .WillOnce(Return(false)); EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) .WillOnce(Return(true)); EXPECT_CALL(*mockFsIface, doMount(StrEq(esObject->getCryptDevicePath()), StrEq(esObject->getMountPoint()), _, _, _)) .WillOnce(Return(-1)); EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) .WillOnce(Return(true)); EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), InternalFailure); EXPECT_FALSE(esObject->isLocked()); EXPECT_EQ(0, removeMappedDev()); } /* Test case where we fail to unmount the filesystem. */ TEST_F(EStoragedTest, UnmountFail) { EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) .Times(1); EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) .WillOnce(&createMappedDev); EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) .WillOnce(Return(false)); EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) .WillOnce(Return(true)); EXPECT_CALL(*mockFsIface, doMount(StrEq(esObject->getCryptDevicePath()), StrEq(esObject->getMountPoint()), _, _, _)) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) .WillOnce(Return(-1)); esObject->formatLuks(password, Volume::FilesystemType::ext4); EXPECT_FALSE(esObject->isLocked()); EXPECT_THROW(esObject->lock(), InternalFailure); EXPECT_FALSE(esObject->isLocked()); EXPECT_EQ(0, removeMappedDev()); } /* Test case where we fail to remove the mount point. */ TEST_F(EStoragedTest, RemoveMountPointFail) { EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) .Times(1); EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) .WillOnce(&createMappedDev); EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) .WillOnce(Return(false)); EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) .WillOnce(Return(true)); EXPECT_CALL(*mockFsIface, doMount(StrEq(esObject->getCryptDevicePath()), StrEq(esObject->getMountPoint()), _, _, _)) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) .WillOnce(Return(false)); esObject->formatLuks(password, Volume::FilesystemType::ext4); EXPECT_FALSE(esObject->isLocked()); /* This will fail to remove the mount point. */ EXPECT_THROW(esObject->lock(), InternalFailure); EXPECT_FALSE(esObject->isLocked()); EXPECT_EQ(0, removeMappedDev()); } /* Test case where we fail to deactivate the LUKS device. */ TEST_F(EStoragedTest, DeactivateFail) { EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) .Times(1); EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) .WillOnce(&createMappedDev); EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) .WillOnce(Return(false)); EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) .WillOnce(Return(true)); EXPECT_CALL(*mockFsIface, doMount(StrEq(esObject->getCryptDevicePath()), StrEq(esObject->getMountPoint()), _, _, _)) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) .WillOnce(Return(0)); EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) .WillOnce(Return(true)); EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)).WillOnce(Return(-1)); /* Format the encrypted device. */ esObject->formatLuks(password, Volume::FilesystemType::ext4); EXPECT_FALSE(esObject->isLocked()); EXPECT_THROW(esObject->lock(), InternalFailure); EXPECT_FALSE(esObject->isLocked()); EXPECT_EQ(0, removeMappedDev()); } /* Test case where we successfully change the password. */ TEST_F(EStoragedTest, ChangePasswordSuccess) { std::string newPasswordString("newPassword"); std::vector newPassword(newPasswordString.begin(), newPasswordString.end()); EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptKeyslotChangeByPassphrase( _, _, _, reinterpret_cast(password.data()), password.size(), reinterpret_cast(newPassword.data()), newPassword.size())) .WillOnce(Return(0)); /* Change the password for the LUKS-encrypted device. */ esObject->changePassword(password, newPassword); } /* Test case where we fail to change the password. */ TEST_F(EStoragedTest, ChangePasswordFail) { std::string newPasswordString("newPassword"); std::vector newPassword(newPasswordString.begin(), newPasswordString.end()); EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); EXPECT_CALL(*mockCryptIface, cryptKeyslotChangeByPassphrase( _, _, _, reinterpret_cast(password.data()), password.size(), reinterpret_cast(newPassword.data()), newPassword.size())) .WillOnce(Return(-1)); EXPECT_THROW(esObject->changePassword(password, newPassword), InternalFailure); } } // namespace estoraged_test