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