blob: 6b93a88319da51e8acb2f07a06b137b7dbb2c681 [file] [log] [blame]
John Wedigb810c922021-11-17 16:38:03 -08001
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>
John Wedig972c3fa2021-12-29 17:30:41 -080010#include <xyz/openbmc_project/Common/error.hpp>
John Wedigb810c922021-11-17 16:38:03 -080011
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
22namespace estoraged_test
23{
24
25class 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
45class 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
John Wedig972c3fa2021-12-29 17:30:41 -080075using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
76using sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound;
77using sdbusplus::xyz::openbmc_project::Inventory::Item::server::Volume;
John Wedigb810c922021-11-17 16:38:03 -080078using std::filesystem::path;
79using ::testing::_;
80using ::testing::ContainsRegex;
81using ::testing::IsNull;
82using ::testing::Return;
83using ::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 */
90sdbusplus::SdBusMock sdbusMock;
91
92class 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";
John Wedig972c3fa2021-12-29 17:30:41 -0800100 static constexpr auto ESTORAGED_INTERFACE =
101 "xyz.openbmc_project.Inventory.Item.Volume";
John Wedigb810c922021-11-17 16:38:03 -0800102 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. */
157TEST_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. */
John Wedig972c3fa2021-12-29 17:30:41 -0800193 esObject->formatLuks(password, Volume::FilesystemType::ext4);
John Wedigb810c922021-11-17 16:38:03 -0800194 EXPECT_FALSE(esObject->isLocked());
195
John Wedig972c3fa2021-12-29 17:30:41 -0800196 esObject->lock();
John Wedigb810c922021-11-17 16:38:03 -0800197 EXPECT_TRUE(esObject->isLocked());
198}
199
200/* Test case where the device/file doesn't exist. */
201TEST_F(eStoragedTest, FormatNoDeviceFail)
202{
203 /* Delete the test file. */
204 EXPECT_EQ(0, unlink(testFileName));
205
John Wedig972c3fa2021-12-29 17:30:41 -0800206 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
207 ResourceNotFound);
John Wedigb810c922021-11-17 16:38:03 -0800208 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. */
217TEST_F(eStoragedTest, FormatFail)
218{
219 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _))
220 .WillOnce(Return(-1));
221
John Wedig972c3fa2021-12-29 17:30:41 -0800222 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
223 InternalFailure);
John Wedigb810c922021-11-17 16:38:03 -0800224 EXPECT_FALSE(esObject->isLocked());
225}
226
227/* Test case where we fail to set the password for the LUKS device. */
228TEST_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
John Wedig972c3fa2021-12-29 17:30:41 -0800240 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
241 InternalFailure);
John Wedigb810c922021-11-17 16:38:03 -0800242 EXPECT_TRUE(esObject->isLocked());
243}
244
245/* Test case where we fail to load the LUKS header. */
246TEST_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
John Wedig972c3fa2021-12-29 17:30:41 -0800260 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
261 InternalFailure);
John Wedigb810c922021-11-17 16:38:03 -0800262 EXPECT_TRUE(esObject->isLocked());
263}
264
265/* Test case where we fail to activate the LUKS device. */
266TEST_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
John Wedig972c3fa2021-12-29 17:30:41 -0800283 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
284 InternalFailure);
John Wedigb810c922021-11-17 16:38:03 -0800285 EXPECT_TRUE(esObject->isLocked());
286}
287
288/* Test case where we fail to create the filesystem. */
289TEST_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
John Wedig972c3fa2021-12-29 17:30:41 -0800308 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
309 InternalFailure);
John Wedigb810c922021-11-17 16:38:03 -0800310 EXPECT_FALSE(esObject->isLocked());
311}
312
313/* Test case where we fail to create the mount point. */
314TEST_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
John Wedig972c3fa2021-12-29 17:30:41 -0800336 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
337 InternalFailure);
John Wedigb810c922021-11-17 16:38:03 -0800338 EXPECT_FALSE(esObject->isLocked());
339}
340
341/* Test case where we fail to mount the filesystem. */
342TEST_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
John Wedig972c3fa2021-12-29 17:30:41 -0800372 EXPECT_THROW(esObject->formatLuks(password, Volume::FilesystemType::ext4),
373 InternalFailure);
John Wedigb810c922021-11-17 16:38:03 -0800374 EXPECT_FALSE(esObject->isLocked());
375}
376
377/* Test case where we fail to unmount the filesystem. */
378TEST_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
John Wedig972c3fa2021-12-29 17:30:41 -0800408 esObject->formatLuks(password, Volume::FilesystemType::ext4);
John Wedigb810c922021-11-17 16:38:03 -0800409 EXPECT_FALSE(esObject->isLocked());
410
John Wedig972c3fa2021-12-29 17:30:41 -0800411 EXPECT_THROW(esObject->lock(), InternalFailure);
John Wedigb810c922021-11-17 16:38:03 -0800412 EXPECT_FALSE(esObject->isLocked());
413}
414
415/* Test case where we fail to remove the mount point. */
416TEST_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
John Wedig972c3fa2021-12-29 17:30:41 -0800449 esObject->formatLuks(password, Volume::FilesystemType::ext4);
John Wedigb810c922021-11-17 16:38:03 -0800450 EXPECT_FALSE(esObject->isLocked());
451
452 /* This will fail to remove the mount point. */
John Wedig972c3fa2021-12-29 17:30:41 -0800453 EXPECT_THROW(esObject->lock(), InternalFailure);
John Wedigb810c922021-11-17 16:38:03 -0800454 EXPECT_FALSE(esObject->isLocked());
455}
456
457/* Test case where we fail to deactivate the LUKS device. */
458TEST_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. */
John Wedig972c3fa2021-12-29 17:30:41 -0800494 esObject->formatLuks(password, Volume::FilesystemType::ext4);
John Wedigb810c922021-11-17 16:38:03 -0800495 EXPECT_FALSE(esObject->isLocked());
496
John Wedig972c3fa2021-12-29 17:30:41 -0800497 EXPECT_THROW(esObject->lock(), InternalFailure);
John Wedigb810c922021-11-17 16:38:03 -0800498 EXPECT_FALSE(esObject->isLocked());
499}
500
501} // namespace estoraged_test