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