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