blob: f293ffc7bbcb6d3d63256e6b35be09a5396fd5d8 [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>
10#include <xyz/openbmc_project/eStoraged/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
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
75using sdbusplus::xyz::openbmc_project::eStoraged::Error::EncryptionError;
76using sdbusplus::xyz::openbmc_project::eStoraged::Error::FilesystemError;
77using std::filesystem::path;
78using ::testing::_;
79using ::testing::ContainsRegex;
80using ::testing::IsNull;
81using ::testing::Return;
82using ::testing::StrEq;
83
84/*
85 * This sdbus mock object gets used in the destructor of one of the parent
86 * classes for the MockeStoraged object, so this can't be part of the
87 * eStoragedTest class.
88 */
89sdbusplus::SdBusMock sdbusMock;
90
91class eStoragedTest : public testing::Test
92{
93 public:
94 static constexpr char testFileName[] = "testfile";
95 static constexpr char testLuksDevName[] = "testfile_luksDev";
96 std::ofstream testFile;
97 std::unique_ptr<estoraged::eStoraged> esObject;
98 static constexpr auto TEST_PATH = "/test/openbmc_project/storage/test_dev";
99 static constexpr auto ESTORAGED_INTERFACE = "xyz.openbmc_project.eStoraged";
100 sdbusplus::bus::bus bus;
101 std::string passwordString;
102 std::vector<uint8_t> password;
103 MockCryptsetupInterface* mockCryptIface;
104 MockFilesystemInterface* mockFsIface;
105
106 eStoragedTest() :
107 bus(sdbusplus::get_mocked_new(&sdbusMock)), passwordString("password"),
108 password(passwordString.begin(), passwordString.end())
109 {}
110
111 void SetUp() override
112 {
113 /* Create an empty file that we'll pretend is a 'storage device'. */
114 testFile.open(testFileName,
115 std::ios::out | std::ios::binary | std::ios::trunc);
116 testFile.close();
117 if (testFile.fail())
118 {
119 throw std::runtime_error("Failed to open test file");
120 }
121
122 EXPECT_CALL(sdbusMock,
123 sd_bus_add_object_vtable(IsNull(), _, StrEq(TEST_PATH),
124 StrEq(ESTORAGED_INTERFACE), _, _))
125 .WillRepeatedly(Return(0));
126
127 EXPECT_CALL(sdbusMock,
128 sd_bus_emit_object_added(IsNull(), StrEq(TEST_PATH)))
129 .WillRepeatedly(Return(0));
130
131 EXPECT_CALL(sdbusMock,
132 sd_bus_emit_object_removed(IsNull(), StrEq(TEST_PATH)))
133 .WillRepeatedly(Return(0));
134
135 std::unique_ptr<MockCryptsetupInterface> cryptIface =
136 std::make_unique<MockCryptsetupInterface>();
137 mockCryptIface = cryptIface.get();
138 std::unique_ptr<MockFilesystemInterface> fsIface =
139 std::make_unique<MockFilesystemInterface>();
140 mockFsIface = fsIface.get();
141
142 esObject = std::make_unique<estoraged::eStoraged>(
143 bus, TEST_PATH, std::string(testFileName),
144 std::string(testLuksDevName), std::move(cryptIface),
145 std::move(fsIface));
146 }
147
148 void TearDown() override
149 {
150 EXPECT_EQ(0, unlink(testFileName));
151 }
152};
153
154/* Test case to format and then lock the LUKS device. */
155TEST_F(eStoragedTest, FormatPass)
156{
157 EXPECT_CALL(sdbusMock,
158 sd_bus_emit_properties_changed_strv(
159 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _))
160 .WillRepeatedly(Return(0));
161
162 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
163
164 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
165 .Times(1);
166
167 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
168
169 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
170 .Times(1);
171
172 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0));
173
174 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint())))
175 .WillOnce(Return(true));
176
177 EXPECT_CALL(*mockFsIface,
178 doMount(ContainsRegex("/dev/mapper/"),
179 StrEq(esObject->getMountPoint()), _, _, _))
180 .WillOnce(Return(0));
181
182 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint())))
183 .WillOnce(Return(0));
184
185 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint())))
186 .WillOnce(Return(true));
187
188 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)).Times(1);
189
190 /* Format the encrypted device. */
191 esObject->format(password);
192 EXPECT_FALSE(esObject->isLocked());
193
194 esObject->lock(password);
195 EXPECT_TRUE(esObject->isLocked());
196}
197
198/* Test case where the device/file doesn't exist. */
199TEST_F(eStoragedTest, FormatNoDeviceFail)
200{
201 /* Delete the test file. */
202 EXPECT_EQ(0, unlink(testFileName));
203
204 EXPECT_THROW(esObject->format(password), EncryptionError);
205 EXPECT_FALSE(esObject->isLocked());
206
207 /* Create the test file again, so that the TearDown function works. */
208 testFile.open(testFileName,
209 std::ios::out | std::ios::binary | std::ios::trunc);
210 testFile.close();
211}
212
213/* Test case where we fail to format the LUKS device. */
214TEST_F(eStoragedTest, FormatFail)
215{
216 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _))
217 .WillOnce(Return(-1));
218
219 EXPECT_THROW(esObject->format(password), EncryptionError);
220 EXPECT_FALSE(esObject->isLocked());
221}
222
223/* Test case where we fail to set the password for the LUKS device. */
224TEST_F(eStoragedTest, AddKeyslotFail)
225{
226 EXPECT_CALL(sdbusMock,
227 sd_bus_emit_properties_changed_strv(
228 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _))
229 .WillRepeatedly(Return(0));
230
231 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
232
233 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
234 .WillOnce(Return(-1));
235
236 EXPECT_THROW(esObject->format(password), EncryptionError);
237 EXPECT_TRUE(esObject->isLocked());
238}
239
240/* Test case where we fail to load the LUKS header. */
241TEST_F(eStoragedTest, LoadLuksHeaderFail)
242{
243 EXPECT_CALL(sdbusMock,
244 sd_bus_emit_properties_changed_strv(
245 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _))
246 .WillRepeatedly(Return(0));
247
248 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
249
250 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
251 .Times(1);
252
253 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).WillOnce(Return(-1));
254
255 EXPECT_THROW(esObject->format(password), EncryptionError);
256 EXPECT_TRUE(esObject->isLocked());
257}
258
259/* Test case where we fail to activate the LUKS device. */
260TEST_F(eStoragedTest, ActivateFail)
261{
262 EXPECT_CALL(sdbusMock,
263 sd_bus_emit_properties_changed_strv(
264 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _))
265 .WillRepeatedly(Return(0));
266
267 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
268
269 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
270 .Times(1);
271
272 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
273
274 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
275 .WillOnce(Return(-1));
276
277 EXPECT_THROW(esObject->format(password), EncryptionError);
278 EXPECT_TRUE(esObject->isLocked());
279}
280
281/* Test case where we fail to create the filesystem. */
282TEST_F(eStoragedTest, CreateFilesystemFail)
283{
284 EXPECT_CALL(sdbusMock,
285 sd_bus_emit_properties_changed_strv(
286 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _))
287 .WillRepeatedly(Return(0));
288
289 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
290
291 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
292 .Times(1);
293
294 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
295
296 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
297 .Times(1);
298
299 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(-1));
300
301 EXPECT_THROW(esObject->format(password), FilesystemError);
302 EXPECT_FALSE(esObject->isLocked());
303}
304
305/* Test case where we fail to create the mount point. */
306TEST_F(eStoragedTest, CreateMountPointFail)
307{
308 EXPECT_CALL(sdbusMock,
309 sd_bus_emit_properties_changed_strv(
310 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _))
311 .WillRepeatedly(Return(0));
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 .Times(1);
322
323 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0));
324
325 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint())))
326 .WillOnce(Return(false));
327
328 EXPECT_THROW(esObject->format(password), FilesystemError);
329 EXPECT_FALSE(esObject->isLocked());
330}
331
332/* Test case where we fail to mount the filesystem. */
333TEST_F(eStoragedTest, MountFail)
334{
335 EXPECT_CALL(sdbusMock,
336 sd_bus_emit_properties_changed_strv(
337 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _))
338 .WillRepeatedly(Return(0));
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, createDirectory(path(esObject->getMountPoint())))
353 .WillOnce(Return(true));
354
355 EXPECT_CALL(*mockFsIface,
356 doMount(ContainsRegex("/dev/mapper/"),
357 StrEq(esObject->getMountPoint()), _, _, _))
358 .WillOnce(Return(-1));
359
360 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint())))
361 .WillOnce(Return(true));
362
363 EXPECT_THROW(esObject->format(password), FilesystemError);
364 EXPECT_FALSE(esObject->isLocked());
365}
366
367/* Test case where we fail to unmount the filesystem. */
368TEST_F(eStoragedTest, UnmountFail)
369{
370 EXPECT_CALL(sdbusMock,
371 sd_bus_emit_properties_changed_strv(
372 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _))
373 .WillRepeatedly(Return(0));
374
375 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
376
377 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
378 .Times(1);
379
380 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
381
382 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
383 .Times(1);
384
385 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0));
386
387 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint())))
388 .WillOnce(Return(true));
389
390 EXPECT_CALL(*mockFsIface,
391 doMount(ContainsRegex("/dev/mapper/"),
392 StrEq(esObject->getMountPoint()), _, _, _))
393 .WillOnce(Return(0));
394
395 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint())))
396 .WillOnce(Return(-1));
397
398 esObject->format(password);
399 EXPECT_FALSE(esObject->isLocked());
400
401 EXPECT_THROW(esObject->lock(password), FilesystemError);
402 EXPECT_FALSE(esObject->isLocked());
403}
404
405/* Test case where we fail to remove the mount point. */
406TEST_F(eStoragedTest, RemoveMountPointFail)
407{
408 EXPECT_CALL(sdbusMock,
409 sd_bus_emit_properties_changed_strv(
410 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _))
411 .WillRepeatedly(Return(0));
412
413 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
414
415 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
416 .Times(1);
417
418 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
419
420 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
421 .Times(1);
422
423 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0));
424
425 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint())))
426 .WillOnce(Return(true));
427
428 EXPECT_CALL(*mockFsIface,
429 doMount(ContainsRegex("/dev/mapper/"),
430 StrEq(esObject->getMountPoint()), _, _, _))
431 .WillOnce(Return(0));
432
433 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint())))
434 .WillOnce(Return(0));
435
436 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint())))
437 .WillOnce(Return(false));
438
439 esObject->format(password);
440 EXPECT_FALSE(esObject->isLocked());
441
442 /* This will fail to remove the mount point. */
443 EXPECT_THROW(esObject->lock(password), FilesystemError);
444 EXPECT_FALSE(esObject->isLocked());
445}
446
447/* Test case where we fail to deactivate the LUKS device. */
448TEST_F(eStoragedTest, DeactivateFail)
449{
450 EXPECT_CALL(sdbusMock,
451 sd_bus_emit_properties_changed_strv(
452 IsNull(), StrEq(TEST_PATH), StrEq(ESTORAGED_INTERFACE), _))
453 .WillRepeatedly(Return(0));
454
455 EXPECT_CALL(*mockCryptIface, cryptFormat(_, _, _, _, _, _, _, _)).Times(1);
456
457 EXPECT_CALL(*mockCryptIface, cryptKeyslotAddByVolumeKey(_, _, _, _, _, _))
458 .Times(1);
459
460 EXPECT_CALL(*mockCryptIface, cryptLoad(_, _, _)).Times(1);
461
462 EXPECT_CALL(*mockCryptIface, cryptActivateByPassphrase(_, _, _, _, _, _))
463 .Times(1);
464
465 EXPECT_CALL(*mockFsIface, runMkfs(testLuksDevName)).WillOnce(Return(0));
466
467 EXPECT_CALL(*mockFsIface, createDirectory(path(esObject->getMountPoint())))
468 .WillOnce(Return(true));
469
470 EXPECT_CALL(*mockFsIface,
471 doMount(ContainsRegex("/dev/mapper/"),
472 StrEq(esObject->getMountPoint()), _, _, _))
473 .WillOnce(Return(0));
474
475 EXPECT_CALL(*mockFsIface, doUnmount(StrEq(esObject->getMountPoint())))
476 .WillOnce(Return(0));
477
478 EXPECT_CALL(*mockFsIface, removeDirectory(path(esObject->getMountPoint())))
479 .WillOnce(Return(true));
480
481 EXPECT_CALL(*mockCryptIface, cryptDeactivate(_, _)).WillOnce(Return(-1));
482
483 /* Format the encrypted device. */
484 esObject->format(password);
485 EXPECT_FALSE(esObject->isLocked());
486
487 EXPECT_THROW(esObject->lock(password), EncryptionError);
488 EXPECT_FALSE(esObject->isLocked());
489}
490
491} // namespace estoraged_test