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