1 #include "estoraged_test.hpp" 2 3 #include "estoraged.hpp" 4 #include "estoraged_conf.hpp" 5 6 #include <unistd.h> 7 8 #include <boost/asio/io_context.hpp> 9 #include <sdbusplus/asio/connection.hpp> 10 #include <sdbusplus/asio/object_server.hpp> 11 #include <xyz/openbmc_project/Common/error.hpp> 12 #include <xyz/openbmc_project/Inventory/Item/Volume/server.hpp> 13 14 #include <exception> 15 #include <filesystem> 16 #include <fstream> 17 #include <iterator> 18 #include <memory> 19 #include <string> 20 #include <vector> 21 22 #include <gmock/gmock.h> 23 #include <gtest/gtest.h> 24 25 namespace estoraged_test 26 { 27 28 using sdbusplus::server::xyz::openbmc_project::inventory::item::Volume; 29 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 30 using sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound; 31 using std::filesystem::path; 32 using ::testing::_; 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 char* testCryptDir = "/tmp"; 42 const std::string testConfigPath = 43 "/xyz/openbmc_project/inventory/system/board/test_board/test_emmc"; 44 const uint64_t testSize = 24; 45 const uint8_t testLifeTime = 25; 46 const std::string testPartNumber = "12345678"; 47 const std::string testSerialNumber = "ABCDEF1234"; 48 const std::string testLocationCode = "U102020"; 49 const std::string testDriveType = "SSD"; 50 std::ofstream testFile; 51 std::string passwordString; 52 std::vector<uint8_t> password; 53 MockCryptsetupInterface* mockCryptIface{}; 54 MockFilesystemInterface* mockFsIface{}; 55 boost::asio::io_context io; 56 std::shared_ptr<sdbusplus::asio::connection> conn; 57 std::unique_ptr<sdbusplus::asio::object_server> objectServer; 58 std::unique_ptr<estoraged::EStoraged> esObject; 59 60 EStoragedTest() : 61 passwordString("password"), 62 password(passwordString.begin(), passwordString.end()) 63 {} 64 65 void SetUp() override 66 { 67 /* Create an empty file that we'll pretend is a 'storage device'. */ 68 testFile.open(testFileName, 69 std::ios::out | std::ios::binary | std::ios::trunc); 70 testFile.close(); 71 if (testFile.fail()) 72 { 73 throw std::runtime_error("Failed to open test file"); 74 } 75 76 std::unique_ptr<MockCryptsetupInterface> cryptIface = 77 std::make_unique<MockCryptsetupInterface>(); 78 mockCryptIface = cryptIface.get(); 79 std::unique_ptr<MockFilesystemInterface> fsIface = 80 std::make_unique<MockFilesystemInterface>(); 81 mockFsIface = fsIface.get(); 82 83 /* Set up location of dummy mapped crypt file. */ 84 EXPECT_CALL(*cryptIface, cryptGetDir).WillOnce(Return(testCryptDir)); 85 86 conn = std::make_shared<sdbusplus::asio::connection>(io); 87 // request D-Bus server name. 88 conn->request_name("xyz.openbmc_project.eStoraged.test"); 89 objectServer = std::make_unique<sdbusplus::asio::object_server>(conn); 90 91 esObject = std::make_unique<estoraged::EStoraged>( 92 *objectServer, testConfigPath, testFileName, testLuksDevName, 93 testSize, testLifeTime, testPartNumber, testSerialNumber, 94 testLocationCode, ERASE_MAX_GEOMETRY, ERASE_MIN_GEOMETRY, 95 testDriveType, std::move(cryptIface), std::move(fsIface)); 96 } 97 98 void TearDown() override 99 { 100 EXPECT_EQ(0, unlink(testFileName)); 101 } 102 }; 103 104 const char* mappedDevicePath = "/tmp/testfile_luksDev"; 105 std::ofstream mappedDevice; 106 107 int createMappedDev() 108 { 109 mappedDevice.open(mappedDevicePath, 110 std::ios::out | std::ios::binary | std::ios::trunc); 111 mappedDevice.close(); 112 if (mappedDevice.fail()) 113 { 114 throw std::runtime_error("Failed to open test mapped device"); 115 } 116 117 return 0; 118 } 119 120 int removeMappedDev() 121 { 122 EXPECT_EQ(0, unlink(mappedDevicePath)); 123 124 return 0; 125 } 126 127 /* Test case to format and then lock the LUKS device. */ 128 TEST_F(EStoragedTest, FormatPass) 129 { 130 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 131 132 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 133 .Times(1); 134 135 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 136 137 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 138 .WillOnce(&createMappedDev); 139 140 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) 141 .WillOnce(Return(0)); 142 143 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 144 .WillOnce(Return(false)); 145 146 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 147 .WillOnce(Return(true)); 148 149 EXPECT_CALL(*mockFsIface, 150 doMount(StrEq(esObject->getCryptDevicePath()), 151 StrEq(esObject->getMountPoint()), _, _, _)) 152 .WillOnce(Return(0)); 153 154 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 155 .WillOnce(Return(0)); 156 157 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 158 .WillOnce(Return(true)); 159 160 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)) 161 .WillOnce(&removeMappedDev); 162 163 /* Format the encrypted device. */ 164 esObject->formatLuks(password, Volume::FilesystemType::ext4); 165 EXPECT_FALSE(esObject->isLocked()); 166 167 esObject->lock(); 168 EXPECT_TRUE(esObject->isLocked()); 169 } 170 171 /* 172 * Test case where the mount point directory already exists, so it shouldn't 173 * try to create it. 174 */ 175 TEST_F(EStoragedTest, MountPointExistsPass) 176 { 177 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 178 179 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 180 .Times(1); 181 182 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 183 184 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 185 .WillOnce(&createMappedDev); 186 187 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) 188 .WillOnce(Return(0)); 189 190 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 191 .WillOnce(Return(true)); 192 193 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 194 .Times(0); 195 196 EXPECT_CALL(*mockFsIface, 197 doMount(StrEq(esObject->getCryptDevicePath()), 198 StrEq(esObject->getMountPoint()), _, _, _)) 199 .WillOnce(Return(0)); 200 201 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 202 .WillOnce(Return(0)); 203 204 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 205 .WillOnce(Return(true)); 206 207 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)) 208 .WillOnce(&removeMappedDev); 209 210 /* Format the encrypted device. */ 211 esObject->formatLuks(password, Volume::FilesystemType::ext4); 212 EXPECT_FALSE(esObject->isLocked()); 213 214 esObject->lock(); 215 EXPECT_TRUE(esObject->isLocked()); 216 } 217 218 /* Test case where the device/file doesn't exist. */ 219 TEST_F(EStoragedTest, FormatNoDeviceFail) 220 { 221 /* Delete the test file. */ 222 EXPECT_EQ(0, unlink(testFileName)); 223 224 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 225 ResourceNotFound); 226 EXPECT_TRUE(esObject->isLocked()); 227 228 /* Create the test file again, so that the TearDown function works. */ 229 testFile.open(testFileName, 230 std::ios::out | std::ios::binary | std::ios::trunc); 231 testFile.close(); 232 } 233 234 /* Test case where we fail to format the LUKS device. */ 235 TEST_F(EStoragedTest, FormatFail) 236 { 237 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)) 238 .WillOnce(Return(-1)); 239 240 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 241 InternalFailure); 242 EXPECT_TRUE(esObject->isLocked()); 243 } 244 245 /* Test case where we fail to set the password for the LUKS device. */ 246 TEST_F(EStoragedTest, AddKeyslotFail) 247 { 248 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 249 250 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 251 .WillOnce(Return(-1)); 252 253 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 254 InternalFailure); 255 EXPECT_TRUE(esObject->isLocked()); 256 } 257 258 /* Test case where we fail to load the LUKS header. */ 259 TEST_F(EStoragedTest, LoadLuksHeaderFail) 260 { 261 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 262 263 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 264 .Times(1); 265 266 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).WillOnce(Return(-1)); 267 268 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 269 InternalFailure); 270 EXPECT_TRUE(esObject->isLocked()); 271 } 272 273 /* Test case where we fail to activate the LUKS device. */ 274 TEST_F(EStoragedTest, ActivateFail) 275 { 276 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 277 278 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 279 .Times(1); 280 281 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 282 283 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 284 .WillOnce(Return(-1)); 285 286 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 287 InternalFailure); 288 EXPECT_TRUE(esObject->isLocked()); 289 } 290 291 /* Test case where we fail to create the filesystem. */ 292 TEST_F(EStoragedTest, CreateFilesystemFail) 293 { 294 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 295 296 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 297 .Times(1); 298 299 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 300 301 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 302 .WillOnce(&createMappedDev); 303 304 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) 305 .WillOnce(Return(-1)); 306 307 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 308 InternalFailure); 309 EXPECT_FALSE(esObject->isLocked()); 310 311 EXPECT_EQ(0, removeMappedDev()); 312 } 313 314 /* Test case where we fail to create the mount point. */ 315 TEST_F(EStoragedTest, CreateMountPointFail) 316 { 317 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 318 319 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 320 .Times(1); 321 322 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 323 324 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 325 .WillOnce(&createMappedDev); 326 327 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) 328 .WillOnce(Return(0)); 329 330 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 331 .WillOnce(Return(false)); 332 333 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 334 .WillOnce(Return(false)); 335 336 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 337 InternalFailure); 338 EXPECT_FALSE(esObject->isLocked()); 339 340 EXPECT_EQ(0, removeMappedDev()); 341 } 342 343 /* Test case where we fail to mount the filesystem. */ 344 TEST_F(EStoragedTest, MountFail) 345 { 346 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 347 348 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 349 .Times(1); 350 351 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 352 353 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 354 .WillOnce(&createMappedDev); 355 356 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) 357 .WillOnce(Return(0)); 358 359 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 360 .WillOnce(Return(false)); 361 362 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 363 .WillOnce(Return(true)); 364 365 EXPECT_CALL(*mockFsIface, 366 doMount(StrEq(esObject->getCryptDevicePath()), 367 StrEq(esObject->getMountPoint()), _, _, _)) 368 .WillOnce(Return(-1)); 369 370 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 371 .WillOnce(Return(true)); 372 373 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 374 InternalFailure); 375 EXPECT_FALSE(esObject->isLocked()); 376 377 EXPECT_EQ(0, removeMappedDev()); 378 } 379 380 /* Test case where we fail to unmount the filesystem. */ 381 TEST_F(EStoragedTest, UnmountFail) 382 { 383 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 384 385 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 386 .Times(1); 387 388 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 389 390 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 391 .WillOnce(&createMappedDev); 392 393 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) 394 .WillOnce(Return(0)); 395 396 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 397 .WillOnce(Return(false)); 398 399 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 400 .WillOnce(Return(true)); 401 402 EXPECT_CALL(*mockFsIface, 403 doMount(StrEq(esObject->getCryptDevicePath()), 404 StrEq(esObject->getMountPoint()), _, _, _)) 405 .WillOnce(Return(0)); 406 407 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 408 .WillOnce(Return(-1)); 409 410 esObject->formatLuks(password, Volume::FilesystemType::ext4); 411 EXPECT_FALSE(esObject->isLocked()); 412 413 EXPECT_THROW(esObject->lock(), InternalFailure); 414 EXPECT_FALSE(esObject->isLocked()); 415 416 EXPECT_EQ(0, removeMappedDev()); 417 } 418 419 /* Test case where we fail to remove the mount point. */ 420 TEST_F(EStoragedTest, RemoveMountPointFail) 421 { 422 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 423 424 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 425 .Times(1); 426 427 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 428 429 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 430 .WillOnce(&createMappedDev); 431 432 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) 433 .WillOnce(Return(0)); 434 435 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 436 .WillOnce(Return(false)); 437 438 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 439 .WillOnce(Return(true)); 440 441 EXPECT_CALL(*mockFsIface, 442 doMount(StrEq(esObject->getCryptDevicePath()), 443 StrEq(esObject->getMountPoint()), _, _, _)) 444 .WillOnce(Return(0)); 445 446 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 447 .WillOnce(Return(0)); 448 449 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 450 .WillOnce(Return(false)); 451 452 esObject->formatLuks(password, Volume::FilesystemType::ext4); 453 EXPECT_FALSE(esObject->isLocked()); 454 455 /* This will fail to remove the mount point. */ 456 EXPECT_THROW(esObject->lock(), InternalFailure); 457 EXPECT_FALSE(esObject->isLocked()); 458 459 EXPECT_EQ(0, removeMappedDev()); 460 } 461 462 /* Test case where we fail to deactivate the LUKS device. */ 463 TEST_F(EStoragedTest, DeactivateFail) 464 { 465 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 466 467 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 468 .Times(1); 469 470 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 471 472 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 473 .WillOnce(&createMappedDev); 474 475 EXPECT_CALL(*mockFsIface, runMkfs(StrEq(esObject->getCryptDevicePath()))) 476 .WillOnce(Return(0)); 477 478 EXPECT_CALL(*mockFsIface, directoryExists(path(esObject->getMountPoint()))) 479 .WillOnce(Return(false)); 480 481 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 482 .WillOnce(Return(true)); 483 484 EXPECT_CALL(*mockFsIface, 485 doMount(StrEq(esObject->getCryptDevicePath()), 486 StrEq(esObject->getMountPoint()), _, _, _)) 487 .WillOnce(Return(0)); 488 489 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 490 .WillOnce(Return(0)); 491 492 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 493 .WillOnce(Return(true)); 494 495 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)).WillOnce(Return(-1)); 496 497 /* Format the encrypted device. */ 498 esObject->formatLuks(password, Volume::FilesystemType::ext4); 499 EXPECT_FALSE(esObject->isLocked()); 500 501 EXPECT_THROW(esObject->lock(), InternalFailure); 502 EXPECT_FALSE(esObject->isLocked()); 503 504 EXPECT_EQ(0, removeMappedDev()); 505 } 506 507 /* Test case where we successfully change the password. */ 508 TEST_F(EStoragedTest, ChangePasswordSuccess) 509 { 510 std::string newPasswordString("newPassword"); 511 std::vector<uint8_t> newPassword(newPasswordString.begin(), 512 newPasswordString.end()); 513 514 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 515 516 EXPECT_CALL(*mockCryptIface, 517 cryptKeyslotChangeByPassphrase( 518 _, _, _, reinterpret_cast<const char*>(password.data()), 519 password.size(), 520 reinterpret_cast<const char*>(newPassword.data()), 521 newPassword.size())) 522 .WillOnce(Return(0)); 523 524 /* Change the password for the LUKS-encrypted device. */ 525 esObject->changePassword(password, newPassword); 526 } 527 528 /* Test case where we fail to change the password. */ 529 TEST_F(EStoragedTest, ChangePasswordFail) 530 { 531 std::string newPasswordString("newPassword"); 532 std::vector<uint8_t> newPassword(newPasswordString.begin(), 533 newPasswordString.end()); 534 535 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 536 537 EXPECT_CALL(*mockCryptIface, 538 cryptKeyslotChangeByPassphrase( 539 _, _, _, reinterpret_cast<const char*>(password.data()), 540 password.size(), 541 reinterpret_cast<const char*>(newPassword.data()), 542 newPassword.size())) 543 .WillOnce(Return(-1)); 544 545 EXPECT_THROW(esObject->changePassword(password, newPassword), 546 InternalFailure); 547 } 548 549 } // namespace estoraged_test 550