1 #include "estoraged_test.hpp" 2 3 #include "estoraged.hpp" 4 5 #include <unistd.h> 6 7 #include <boost/asio/io_context.hpp> 8 #include <sdbusplus/asio/connection.hpp> 9 #include <sdbusplus/asio/object_server.hpp> 10 #include <xyz/openbmc_project/Common/error.hpp> 11 #include <xyz/openbmc_project/Inventory/Item/Volume/client.hpp> 12 13 #include <exception> 14 #include <filesystem> 15 #include <fstream> 16 #include <iterator> 17 #include <memory> 18 #include <string> 19 #include <vector> 20 21 #include <gmock/gmock.h> 22 #include <gtest/gtest.h> 23 24 namespace estoraged_test 25 { 26 27 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 28 using sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound; 29 using sdbusplus::xyz::openbmc_project::Inventory::Item::server::Volume; 30 using std::filesystem::path; 31 using ::testing::_; 32 using ::testing::ContainsRegex; 33 using ::testing::Return; 34 using ::testing::StrEq; 35 36 class EStoragedTest : public testing::Test 37 { 38 public: 39 const char* testFileName = "testfile"; 40 const char* testLuksDevName = "testfile_luksDev"; 41 const uint64_t testSize = 24; 42 const uint8_t testLifeTime = 25; 43 std::ofstream testFile; 44 const char* testPath = "/test/openbmc_project/storage/test_dev"; 45 const char* estoragedInterface = 46 "xyz.openbmc_project.Inventory.Item.Volume"; 47 const char* driveInterface = "xyz.openbmc_project.Inventory.Item.Drive"; 48 std::string passwordString; 49 std::vector<uint8_t> password; 50 MockCryptsetupInterface* mockCryptIface{}; 51 MockFilesystemInterface* mockFsIface{}; 52 boost::asio::io_context io; 53 std::shared_ptr<sdbusplus::asio::connection> conn; 54 std::unique_ptr<sdbusplus::asio::object_server> objectServer; 55 std::unique_ptr<estoraged::EStoraged> esObject; 56 57 EStoragedTest() : 58 passwordString("password"), 59 password(passwordString.begin(), passwordString.end()) 60 {} 61 62 void SetUp() override 63 { 64 /* Create an empty file that we'll pretend is a 'storage device'. */ 65 testFile.open(testFileName, 66 std::ios::out | std::ios::binary | std::ios::trunc); 67 testFile.close(); 68 if (testFile.fail()) 69 { 70 throw std::runtime_error("Failed to open test file"); 71 } 72 73 std::unique_ptr<MockCryptsetupInterface> cryptIface = 74 std::make_unique<MockCryptsetupInterface>(); 75 mockCryptIface = cryptIface.get(); 76 std::unique_ptr<MockFilesystemInterface> fsIface = 77 std::make_unique<MockFilesystemInterface>(); 78 mockFsIface = fsIface.get(); 79 80 conn = std::make_shared<sdbusplus::asio::connection>(io); 81 // request D-Bus server name. 82 conn->request_name("xyz.openbmc_project.eStoraged.test"); 83 objectServer = std::make_unique<sdbusplus::asio::object_server>(conn); 84 85 esObject = std::make_unique<estoraged::EStoraged>( 86 *objectServer, testFileName, testLuksDevName, testSize, 87 testLifeTime, std::move(cryptIface), std::move(fsIface)); 88 } 89 90 void TearDown() override 91 { 92 EXPECT_EQ(0, unlink(testFileName)); 93 } 94 }; 95 96 /* Test case to format and then lock the LUKS device. */ 97 TEST_F(EStoragedTest, FormatPass) 98 { 99 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 100 101 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 102 .Times(1); 103 104 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 105 106 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 107 .Times(1); 108 109 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 110 111 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 112 .WillOnce(Return(false)); 113 114 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 115 .WillOnce(Return(true)); 116 117 EXPECT_CALL(*mockFsIface, 118 doMount(ContainsRegex("/dev/mapper/"), 119 StrEq(esObject->getMountPoint()), _, _, _)) 120 .WillOnce(Return(0)); 121 122 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 123 .WillOnce(Return(0)); 124 125 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 126 .WillOnce(Return(true)); 127 128 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)).Times(1); 129 130 /* Format the encrypted device. */ 131 esObject->formatLuks(password, Volume::FilesystemType::ext4); 132 EXPECT_FALSE(esObject->isLocked()); 133 134 esObject->lock(); 135 EXPECT_TRUE(esObject->isLocked()); 136 } 137 138 /* 139 * Test case where the mount point directory already exists, so it shouldn't 140 * try to create it. 141 */ 142 TEST_F(EStoragedTest, MountPointExistsPass) 143 { 144 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 145 146 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 147 .Times(1); 148 149 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 150 151 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 152 .Times(1); 153 154 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 155 156 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 157 .WillOnce(Return(true)); 158 159 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 160 .Times(0); 161 162 EXPECT_CALL(*mockFsIface, 163 doMount(ContainsRegex("/dev/mapper/"), 164 StrEq(esObject->getMountPoint()), _, _, _)) 165 .WillOnce(Return(0)); 166 167 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 168 .WillOnce(Return(0)); 169 170 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 171 .WillOnce(Return(true)); 172 173 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)).Times(1); 174 175 /* Format the encrypted device. */ 176 esObject->formatLuks(password, Volume::FilesystemType::ext4); 177 EXPECT_FALSE(esObject->isLocked()); 178 179 esObject->lock(); 180 EXPECT_TRUE(esObject->isLocked()); 181 } 182 183 /* Test case where the device/file doesn't exist. */ 184 TEST_F(EStoragedTest, FormatNoDeviceFail) 185 { 186 /* Delete the test file. */ 187 EXPECT_EQ(0, unlink(testFileName)); 188 189 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 190 ResourceNotFound); 191 EXPECT_FALSE(esObject->isLocked()); 192 193 /* Create the test file again, so that the TearDown function works. */ 194 testFile.open(testFileName, 195 std::ios::out | std::ios::binary | std::ios::trunc); 196 testFile.close(); 197 } 198 199 /* Test case where we fail to format the LUKS device. */ 200 TEST_F(EStoragedTest, FormatFail) 201 { 202 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)) 203 .WillOnce(Return(-1)); 204 205 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 206 InternalFailure); 207 EXPECT_FALSE(esObject->isLocked()); 208 } 209 210 /* Test case where we fail to set the password for the LUKS device. */ 211 TEST_F(EStoragedTest, AddKeyslotFail) 212 { 213 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 214 215 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 216 .WillOnce(Return(-1)); 217 218 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 219 InternalFailure); 220 EXPECT_TRUE(esObject->isLocked()); 221 } 222 223 /* Test case where we fail to load the LUKS header. */ 224 TEST_F(EStoragedTest, LoadLuksHeaderFail) 225 { 226 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 227 228 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 229 .Times(1); 230 231 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).WillOnce(Return(-1)); 232 233 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 234 InternalFailure); 235 EXPECT_TRUE(esObject->isLocked()); 236 } 237 238 /* Test case where we fail to activate the LUKS device. */ 239 TEST_F(EStoragedTest, ActivateFail) 240 { 241 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 242 243 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 244 .Times(1); 245 246 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 247 248 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 249 .WillOnce(Return(-1)); 250 251 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 252 InternalFailure); 253 EXPECT_TRUE(esObject->isLocked()); 254 } 255 256 /* Test case where we fail to create the filesystem. */ 257 TEST_F(EStoragedTest, CreateFilesystemFail) 258 { 259 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 260 261 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 262 .Times(1); 263 264 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 265 266 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 267 .Times(1); 268 269 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(-1)); 270 271 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 272 InternalFailure); 273 EXPECT_FALSE(esObject->isLocked()); 274 } 275 276 /* Test case where we fail to create the mount point. */ 277 TEST_F(EStoragedTest, CreateMountPointFail) 278 { 279 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 280 281 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 282 .Times(1); 283 284 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 285 286 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 287 .Times(1); 288 289 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 290 291 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 292 .WillOnce(Return(false)); 293 294 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 295 .WillOnce(Return(false)); 296 297 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 298 InternalFailure); 299 EXPECT_FALSE(esObject->isLocked()); 300 } 301 302 /* Test case where we fail to mount the filesystem. */ 303 TEST_F(EStoragedTest, MountFail) 304 { 305 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 306 307 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 308 .Times(1); 309 310 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 311 312 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 313 .Times(1); 314 315 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 316 317 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 318 .WillOnce(Return(false)); 319 320 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 321 .WillOnce(Return(true)); 322 323 EXPECT_CALL(*mockFsIface, 324 doMount(ContainsRegex("/dev/mapper/"), 325 StrEq(esObject->getMountPoint()), _, _, _)) 326 .WillOnce(Return(-1)); 327 328 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 329 .WillOnce(Return(true)); 330 331 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 332 InternalFailure); 333 EXPECT_FALSE(esObject->isLocked()); 334 } 335 336 /* Test case where we fail to unmount the filesystem. */ 337 TEST_F(EStoragedTest, UnmountFail) 338 { 339 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 340 341 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 342 .Times(1); 343 344 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 345 346 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 347 .Times(1); 348 349 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 350 351 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 352 .WillOnce(Return(false)); 353 354 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 355 .WillOnce(Return(true)); 356 357 EXPECT_CALL(*mockFsIface, 358 doMount(ContainsRegex("/dev/mapper/"), 359 StrEq(esObject->getMountPoint()), _, _, _)) 360 .WillOnce(Return(0)); 361 362 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 363 .WillOnce(Return(-1)); 364 365 esObject->formatLuks(password, Volume::FilesystemType::ext4); 366 EXPECT_FALSE(esObject->isLocked()); 367 368 EXPECT_THROW(esObject->lock(), InternalFailure); 369 EXPECT_FALSE(esObject->isLocked()); 370 } 371 372 /* Test case where we fail to remove the mount point. */ 373 TEST_F(EStoragedTest, RemoveMountPointFail) 374 { 375 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 376 377 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 378 .Times(1); 379 380 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 381 382 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 383 .Times(1); 384 385 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 386 387 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 388 .WillOnce(Return(false)); 389 390 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 391 .WillOnce(Return(true)); 392 393 EXPECT_CALL(*mockFsIface, 394 doMount(ContainsRegex("/dev/mapper/"), 395 StrEq(esObject->getMountPoint()), _, _, _)) 396 .WillOnce(Return(0)); 397 398 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 399 .WillOnce(Return(0)); 400 401 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 402 .WillOnce(Return(false)); 403 404 esObject->formatLuks(password, Volume::FilesystemType::ext4); 405 EXPECT_FALSE(esObject->isLocked()); 406 407 /* This will fail to remove the mount point. */ 408 EXPECT_THROW(esObject->lock(), InternalFailure); 409 EXPECT_FALSE(esObject->isLocked()); 410 } 411 412 /* Test case where we fail to deactivate the LUKS device. */ 413 TEST_F(EStoragedTest, DeactivateFail) 414 { 415 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 416 417 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 418 .Times(1); 419 420 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 421 422 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 423 .Times(1); 424 425 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 426 427 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 428 .WillOnce(Return(false)); 429 430 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 431 .WillOnce(Return(true)); 432 433 EXPECT_CALL(*mockFsIface, 434 doMount(ContainsRegex("/dev/mapper/"), 435 StrEq(esObject->getMountPoint()), _, _, _)) 436 .WillOnce(Return(0)); 437 438 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 439 .WillOnce(Return(0)); 440 441 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 442 .WillOnce(Return(true)); 443 444 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)).WillOnce(Return(-1)); 445 446 /* Format the encrypted device. */ 447 esObject->formatLuks(password, Volume::FilesystemType::ext4); 448 EXPECT_FALSE(esObject->isLocked()); 449 450 EXPECT_THROW(esObject->lock(), InternalFailure); 451 EXPECT_FALSE(esObject->isLocked()); 452 } 453 454 } // namespace estoraged_test 455