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