1 2 #include "cryptsetupInterface.hpp" 3 #include "estoraged.hpp" 4 #include "filesystemInterface.hpp" 5 6 #include <unistd.h> 7 8 #include <sdbusplus/bus.hpp> 9 #include <sdbusplus/test/sdbus_mock.hpp> 10 #include <xyz/openbmc_project/Common/error.hpp> 11 12 #include <exception> 13 #include <filesystem> 14 #include <fstream> 15 #include <iterator> 16 #include <string> 17 #include <vector> 18 19 #include <gmock/gmock.h> 20 #include <gtest/gtest.h> 21 22 namespace estoraged_test 23 { 24 25 class MockFilesystemInterface : public estoraged::FilesystemInterface 26 { 27 public: 28 MOCK_METHOD(int, runMkfs, (const std::string& logicalVolume), (override)); 29 30 MOCK_METHOD(int, doMount, 31 (const char* source, const char* target, 32 const char* filesystemtype, unsigned long mountflags, 33 const void* data), 34 (override)); 35 36 MOCK_METHOD(int, doUnmount, (const char* target), (override)); 37 38 MOCK_METHOD(bool, createDirectory, (const std::filesystem::path& p), 39 (override)); 40 41 MOCK_METHOD(bool, removeDirectory, (const std::filesystem::path& p), 42 (override)); 43 }; 44 45 class MockCryptsetupInterface : public estoraged::CryptsetupInterface 46 { 47 public: 48 MOCK_METHOD(int, cryptFormat, 49 (struct crypt_device * cd, const char* type, const char* cipher, 50 const char* cipher_mode, const char* uuid, 51 const char* volume_key, size_t volume_key_size, void* params), 52 (override)); 53 54 MOCK_METHOD(int, cryptKeyslotAddByVolumeKey, 55 (struct crypt_device * cd, int keyslot, const char* volume_key, 56 size_t volume_key_size, const char* passphrase, 57 size_t passphrase_size), 58 (override)); 59 60 MOCK_METHOD(int, cryptLoad, 61 (struct crypt_device * cd, const char* requested_type, 62 void* params), 63 (override)); 64 65 MOCK_METHOD(int, cryptActivateByPassphrase, 66 (struct crypt_device * cd, const char* name, int keyslot, 67 const char* passphrase, size_t passphrase_size, 68 uint32_t flags), 69 (override)); 70 71 MOCK_METHOD(int, cryptDeactivate, 72 (struct crypt_device * cd, const char* name), (override)); 73 }; 74 75 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 76 using sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound; 77 using sdbusplus::xyz::openbmc_project::Inventory::Item::server::Volume; 78 using std::filesystem::path; 79 using ::testing::_; 80 using ::testing::ContainsRegex; 81 using ::testing::IsNull; 82 using ::testing::Return; 83 using ::testing::StrEq; 84 85 /* 86 * This sdbus mock object gets used in the destructor of one of the parent 87 * classes for the MockeStoraged object, so this can't be part of the 88 * eStoragedTest class. 89 */ 90 sdbusplus::SdBusMock sdbusMock; 91 92 class eStoragedTest : public testing::Test 93 { 94 public: 95 static constexpr char testFileName[] = "testfile"; 96 static constexpr char testLuksDevName[] = "testfile_luksDev"; 97 std::ofstream testFile; 98 std::unique_ptr<estoraged::eStoraged> esObject; 99 static constexpr auto TEST_PATH = "/test/openbmc_project/storage/test_dev"; 100 static constexpr auto ESTORAGED_INTERFACE = 101 "xyz.openbmc_project.Inventory.Item.Volume"; 102 sdbusplus::bus::bus bus; 103 std::string passwordString; 104 std::vector<uint8_t> password; 105 MockCryptsetupInterface* mockCryptIface; 106 MockFilesystemInterface* mockFsIface; 107 108 eStoragedTest() : 109 bus(sdbusplus::get_mocked_new(&sdbusMock)), passwordString("password"), 110 password(passwordString.begin(), passwordString.end()) 111 {} 112 113 void SetUp() override 114 { 115 /* Create an empty file that we'll pretend is a 'storage device'. */ 116 testFile.open(testFileName, 117 std::ios::out | std::ios::binary | std::ios::trunc); 118 testFile.close(); 119 if (testFile.fail()) 120 { 121 throw std::runtime_error("Failed to open test file"); 122 } 123 124 EXPECT_CALL(sdbusMock, 125 sd_bus_add_object_vtable(IsNull(), _, StrEq(TEST_PATH), 126 StrEq(ESTORAGED_INTERFACE), _, _)) 127 .WillRepeatedly(Return(0)); 128 129 EXPECT_CALL(sdbusMock, 130 sd_bus_emit_object_added(IsNull(), StrEq(TEST_PATH))) 131 .WillRepeatedly(Return(0)); 132 133 EXPECT_CALL(sdbusMock, 134 sd_bus_emit_object_removed(IsNull(), StrEq(TEST_PATH))) 135 .WillRepeatedly(Return(0)); 136 137 std::unique_ptr<MockCryptsetupInterface> cryptIface = 138 std::make_unique<MockCryptsetupInterface>(); 139 mockCryptIface = cryptIface.get(); 140 std::unique_ptr<MockFilesystemInterface> fsIface = 141 std::make_unique<MockFilesystemInterface>(); 142 mockFsIface = fsIface.get(); 143 144 esObject = std::make_unique<estoraged::eStoraged>( 145 bus, TEST_PATH, std::string(testFileName), 146 std::string(testLuksDevName), std::move(cryptIface), 147 std::move(fsIface)); 148 } 149 150 void TearDown() override 151 { 152 EXPECT_EQ(0, unlink(testFileName)); 153 } 154 }; 155 156 /* Test case to format and then lock the LUKS device. */ 157 TEST_F(eStoragedTest, FormatPass) 158 { 159 EXPECT_CALL(sdbusMock, 160 sd_bus_emit_properties_changed_strv( 161 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _)) 162 .WillRepeatedly(Return(0)); 163 164 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 165 166 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 167 .Times(1); 168 169 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 170 171 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 172 .Times(1); 173 174 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 175 176 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 177 .WillOnce(Return(true)); 178 179 EXPECT_CALL(*mockFsIface, 180 doMount(ContainsRegex("/dev/mapper/"), 181 StrEq(esObject->getMountPoint()), _, _, _)) 182 .WillOnce(Return(0)); 183 184 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 185 .WillOnce(Return(0)); 186 187 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint()))) 188 .WillOnce(Return(true)); 189 190 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)).Times(1); 191 192 /* Format the encrypted device. */ 193 esObject->formatLuks(password, Volume::FilesystemType::ext4); 194 EXPECT_FALSE(esObject->isLocked()); 195 196 esObject->lock(); 197 EXPECT_TRUE(esObject->isLocked()); 198 } 199 200 /* Test case where the device/file doesn't exist. */ 201 TEST_F(eStoragedTest, FormatNoDeviceFail) 202 { 203 /* Delete the test file. */ 204 EXPECT_EQ(0, unlink(testFileName)); 205 206 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 207 ResourceNotFound); 208 EXPECT_FALSE(esObject->isLocked()); 209 210 /* Create the test file again, so that the TearDown function works. */ 211 testFile.open(testFileName, 212 std::ios::out | std::ios::binary | std::ios::trunc); 213 testFile.close(); 214 } 215 216 /* Test case where we fail to format the LUKS device. */ 217 TEST_F(eStoragedTest, FormatFail) 218 { 219 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)) 220 .WillOnce(Return(-1)); 221 222 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 223 InternalFailure); 224 EXPECT_FALSE(esObject->isLocked()); 225 } 226 227 /* Test case where we fail to set the password for the LUKS device. */ 228 TEST_F(eStoragedTest, AddKeyslotFail) 229 { 230 EXPECT_CALL(sdbusMock, 231 sd_bus_emit_properties_changed_strv( 232 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _)) 233 .WillRepeatedly(Return(0)); 234 235 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 236 237 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 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 load the LUKS header. */ 246 TEST_F(eStoragedTest, LoadLuksHeaderFail) 247 { 248 EXPECT_CALL(sdbusMock, 249 sd_bus_emit_properties_changed_strv( 250 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _)) 251 .WillRepeatedly(Return(0)); 252 253 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 254 255 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 256 .Times(1); 257 258 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).WillOnce(Return(-1)); 259 260 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 261 InternalFailure); 262 EXPECT_TRUE(esObject->isLocked()); 263 } 264 265 /* Test case where we fail to activate the LUKS device. */ 266 TEST_F(eStoragedTest, ActivateFail) 267 { 268 EXPECT_CALL(sdbusMock, 269 sd_bus_emit_properties_changed_strv( 270 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _)) 271 .WillRepeatedly(Return(0)); 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(sdbusMock, 292 sd_bus_emit_properties_changed_strv( 293 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _)) 294 .WillRepeatedly(Return(0)); 295 296 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 297 298 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 299 .Times(1); 300 301 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 302 303 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 304 .Times(1); 305 306 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(-1)); 307 308 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4), 309 InternalFailure); 310 EXPECT_FALSE(esObject->isLocked()); 311 } 312 313 /* Test case where we fail to create the mount point. */ 314 TEST_F(eStoragedTest, CreateMountPointFail) 315 { 316 EXPECT_CALL(sdbusMock, 317 sd_bus_emit_properties_changed_strv( 318 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _)) 319 .WillRepeatedly(Return(0)); 320 321 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 322 323 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 324 .Times(1); 325 326 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 327 328 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 329 .Times(1); 330 331 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 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 341 /* Test case where we fail to mount the filesystem. */ 342 TEST_F(eStoragedTest, MountFail) 343 { 344 EXPECT_CALL(sdbusMock, 345 sd_bus_emit_properties_changed_strv( 346 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _)) 347 .WillRepeatedly(Return(0)); 348 349 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 350 351 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 352 .Times(1); 353 354 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 355 356 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 357 .Times(1); 358 359 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 360 361 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 362 .WillOnce(Return(true)); 363 364 EXPECT_CALL(*mockFsIface, 365 doMount(ContainsRegex("/dev/mapper/"), 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 377 /* Test case where we fail to unmount the filesystem. */ 378 TEST_F(eStoragedTest, UnmountFail) 379 { 380 EXPECT_CALL(sdbusMock, 381 sd_bus_emit_properties_changed_strv( 382 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _)) 383 .WillRepeatedly(Return(0)); 384 385 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 386 387 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 388 .Times(1); 389 390 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 391 392 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 393 .Times(1); 394 395 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 396 397 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 398 .WillOnce(Return(true)); 399 400 EXPECT_CALL(*mockFsIface, 401 doMount(ContainsRegex("/dev/mapper/"), 402 StrEq(esObject->getMountPoint()), _, _, _)) 403 .WillOnce(Return(0)); 404 405 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint()))) 406 .WillOnce(Return(-1)); 407 408 esObject->formatLuks(password, Volume::FilesystemType::ext4); 409 EXPECT_FALSE(esObject->isLocked()); 410 411 EXPECT_THROW(esObject->lock(), InternalFailure); 412 EXPECT_FALSE(esObject->isLocked()); 413 } 414 415 /* Test case where we fail to remove the mount point. */ 416 TEST_F(eStoragedTest, RemoveMountPointFail) 417 { 418 EXPECT_CALL(sdbusMock, 419 sd_bus_emit_properties_changed_strv( 420 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _)) 421 .WillRepeatedly(Return(0)); 422 423 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1); 424 425 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _)) 426 .Times(1); 427 428 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1); 429 430 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _)) 431 .Times(1); 432 433 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 434 435 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 436 .WillOnce(Return(true)); 437 438 EXPECT_CALL(*mockFsIface, 439 doMount(ContainsRegex("/dev/mapper/"), 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 457 /* Test case where we fail to deactivate the LUKS device. */ 458 TEST_F(eStoragedTest, DeactivateFail) 459 { 460 EXPECT_CALL(sdbusMock, 461 sd_bus_emit_properties_changed_strv( 462 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _)) 463 .WillRepeatedly(Return(0)); 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 .Times(1); 474 475 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0)); 476 477 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint()))) 478 .WillOnce(Return(true)); 479 480 EXPECT_CALL(*mockFsIface, 481 doMount(ContainsRegex("/dev/mapper/"), 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 501 } // namespace estoraged_test 502