xref: /openbmc/estoraged/src/estoraged.cpp (revision 82897c35761f505c2b881f72c61f726f7d562692)
1 
2 #include "estoraged.hpp"
3 
4 #include "cryptErase.hpp"
5 #include "cryptsetupInterface.hpp"
6 #include "pattern.hpp"
7 #include "verifyDriveGeometry.hpp"
8 #include "zero.hpp"
9 
10 #include <libcryptsetup.h>
11 #include <openssl/rand.h>
12 
13 #include <phosphor-logging/lg2.hpp>
14 #include <xyz/openbmc_project/Common/error.hpp>
15 
16 #include <cstdlib>
17 #include <filesystem>
18 #include <iostream>
19 #include <string_view>
20 #include <vector>
21 
22 namespace estoraged
23 {
24 
25 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
26 using sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound;
27 using sdbusplus::xyz::openbmc_project::Common::Error::UnsupportedRequest;
28 
29 void EStoraged::formatLuks(std::vector<uint8_t> password, FilesystemType type)
30 {
31     std::string msg = "OpenBMC.0.1.DriveFormat";
32     lg2::info("Starting format", "REDFISH_MESSAGE_ID", msg);
33 
34     if (type != FilesystemType::ext4)
35     {
36         lg2::error("Only ext4 filesystems are supported currently",
37                    "REDFISH_MESSAGE_ID", std::string("OpenBMC.0.1.FormatFail"));
38         throw UnsupportedRequest();
39     }
40 
41     CryptHandle cryptHandle(devPath.c_str());
42     if (cryptHandle.get() == nullptr)
43     {
44         lg2::error("Failed to initialize crypt device", "REDFISH_MESSAGE_ID",
45                    std::string("OpenBMC.0.1.FormatFail"));
46         throw ResourceNotFound();
47     }
48 
49     formatLuksDev(cryptHandle.get(), password);
50     activateLuksDev(cryptHandle.get(), password);
51 
52     createFilesystem();
53     mountFilesystem();
54 }
55 
56 void EStoraged::erase(EraseMethod inEraseMethod)
57 {
58     std::cerr << "Erasing encrypted eMMC" << std::endl;
59     lg2::info("Starting erase", "REDFISH_MESSAGE_ID",
60               std::string("OpenBMC.0.1.DriveErase"));
61     switch (inEraseMethod)
62     {
63         case EraseMethod::CryptoErase:
64         {
65             CryptErase myCryptErase(devPath);
66             myCryptErase.doErase();
67             break;
68         }
69         case EraseMethod::VerifyGeometry:
70         {
71             VerifyDriveGeometry myVerifyGeometry(devPath);
72             uint64_t size = myVerifyGeometry.findSizeOfBlockDevice();
73             myVerifyGeometry.geometryOkay(size);
74             break;
75         }
76         case EraseMethod::LogicalOverWrite:
77         {
78             Pattern myErasePattern(devPath);
79             myErasePattern.writePattern(myErasePattern.findSizeOfBlockDevice());
80             break;
81         }
82         case EraseMethod::LogicalVerify:
83         {
84             Pattern myErasePattern(devPath);
85             myErasePattern.verifyPattern(
86                 myErasePattern.findSizeOfBlockDevice());
87             break;
88         }
89         case EraseMethod::VendorSanitize:
90         {
91             break;
92         }
93         case EraseMethod::ZeroOverWrite:
94         {
95             Zero myZero(devPath);
96             myZero.writeZero(myZero.findSizeOfBlockDevice());
97             break;
98         }
99         case EraseMethod::ZeroVerify:
100         {
101             Zero myZero(devPath);
102             myZero.verifyZero(myZero.findSizeOfBlockDevice());
103             break;
104         }
105         case EraseMethod::SecuredLocked:
106         {
107             if (isLocked())
108             {
109                 lock();
110             }
111             // TODO: implement hardware locking
112             // Until that is done, we can lock using eStoraged::lock()
113             break;
114         }
115     }
116 }
117 
118 void EStoraged::lock()
119 {
120     std::string msg = "OpenBMC.0.1.DriveLock";
121     lg2::info("Starting lock", "REDFISH_MESSAGE_ID", msg);
122 
123     unmountFilesystem();
124     deactivateLuksDev();
125 }
126 
127 void EStoraged::unlock(std::vector<uint8_t> password)
128 {
129     std::string msg = "OpenBMC.0.1.DriveUnlock";
130     lg2::info("Starting unlock", "REDFISH_MESSAGE_ID", msg);
131 
132     CryptHandle cryptHandle(devPath.c_str());
133     if (cryptHandle.get() == nullptr)
134     {
135         lg2::error("Failed to initialize crypt device", "REDFISH_MESSAGE_ID",
136                    std::string("OpenBMC.0.1.UnlockFail"));
137         throw ResourceNotFound();
138     }
139 
140     activateLuksDev(cryptHandle.get(), password);
141     mountFilesystem();
142 }
143 
144 void EStoraged::changePassword(std::vector<uint8_t> /*oldPassword*/,
145                                std::vector<uint8_t> /*newPassword*/)
146 {
147     std::cerr << "Changing password for encrypted eMMC" << std::endl;
148     lg2::info("Starting change password", "REDFISH_MESSAGE_ID",
149               std::string("OpenBMC.0.1.DrivePasswordChanged"));
150 }
151 
152 bool EStoraged::isLocked() const
153 {
154     return locked();
155 }
156 
157 std::string_view EStoraged::getMountPoint() const
158 {
159     return mountPoint;
160 }
161 
162 void EStoraged::formatLuksDev(struct crypt_device* cd,
163                               std::vector<uint8_t> password)
164 {
165     lg2::info("Formatting device {DEV}", "DEV", devPath, "REDFISH_MESSAGE_ID",
166               std::string("OpenBMC.0.1.FormatLuksDev"));
167 
168     /* Generate the volume key. */
169     const std::size_t keySize = 64;
170     std::vector<uint8_t> volumeKey(keySize);
171     if (RAND_bytes(volumeKey.data(), keySize) != 1)
172     {
173         lg2::error("Failed to create volume key", "REDFISH_MESSAGE_ID",
174                    std::string("OpenBMC.0.1.FormatLuksDevFail"));
175         throw InternalFailure();
176     }
177     /* Format the LUKS encrypted device. */
178     int retval =
179         cryptIface->cryptFormat(cd, CRYPT_LUKS2, "aes", "xts-plain64", nullptr,
180                                 reinterpret_cast<const char*>(volumeKey.data()),
181                                 volumeKey.size(), nullptr);
182     if (retval < 0)
183     {
184         lg2::error("Failed to format encrypted device: {RETVAL}", "RETVAL",
185                    retval, "REDFISH_MESSAGE_ID",
186                    std::string("OpenBMC.0.1.FormatLuksDevFail"));
187         throw InternalFailure();
188     }
189 
190     /* Device is now encrypted. */
191     locked(true);
192 
193     /* Set the password. */
194     retval = cryptIface->cryptKeyslotAddByVolumeKey(
195         cd, CRYPT_ANY_SLOT, nullptr, 0,
196         reinterpret_cast<const char*>(password.data()), password.size());
197 
198     if (retval < 0)
199     {
200         lg2::error("Failed to set encryption password", "REDFISH_MESSAGE_ID",
201                    std::string("OpenBMC.0.1.FormatLuksDevFail"));
202         throw InternalFailure();
203     }
204 
205     lg2::info("Encrypted device {DEV} successfully formatted", "DEV", devPath,
206               "REDFISH_MESSAGE_ID",
207               std::string("OpenBMC.0.1.FormatLuksDevSuccess"));
208 }
209 
210 void EStoraged::activateLuksDev(struct crypt_device* cd,
211                                 std::vector<uint8_t> password)
212 {
213     lg2::info("Activating LUKS dev {DEV}", "DEV", devPath, "REDFISH_MESSAGE_ID",
214               std::string("OpenBMC.0.1.ActivateLuksDev"));
215 
216     int retval = cryptIface->cryptLoad(cd, CRYPT_LUKS2, nullptr);
217     if (retval < 0)
218     {
219         lg2::error("Failed to load LUKS header: {RETVAL}", "RETVAL", retval,
220                    "REDFISH_MESSAGE_ID",
221                    std::string("OpenBMC.0.1.ActivateLuksDevFail"));
222         throw InternalFailure();
223     }
224 
225     retval = cryptIface->cryptActivateByPassphrase(
226         cd, containerName.c_str(), CRYPT_ANY_SLOT,
227         reinterpret_cast<const char*>(password.data()), password.size(), 0);
228 
229     if (retval < 0)
230     {
231         lg2::error("Failed to activate LUKS dev: {RETVAL}", "RETVAL", retval,
232                    "REDFISH_MESSAGE_ID",
233                    std::string("OpenBMC.0.1.ActivateLuksDevFail"));
234         throw InternalFailure();
235     }
236 
237     /* Device is now unlocked. */
238     locked(false);
239 
240     lg2::info("Successfully activated LUKS dev {DEV}", "DEV", devPath,
241               "REDFISH_MESSAGE_ID",
242               std::string("OpenBMC.0.1.ActivateLuksDevSuccess"));
243 }
244 
245 void EStoraged::createFilesystem()
246 {
247     /* Run the command to create the filesystem. */
248     int retval = fsIface->runMkfs(containerName);
249     if (retval != 0)
250     {
251         lg2::error("Failed to create filesystem: {RETVAL}", "RETVAL", retval,
252                    "REDFISH_MESSAGE_ID",
253                    std::string("OpenBMC.0.1.CreateFilesystemFail"));
254         throw InternalFailure();
255     }
256     lg2::info("Successfully created filesystem for /dev/mapper/{CONTAINER}",
257               "CONTAINER", containerName, "REDFISH_MESSAGE_ID",
258               std::string("OpenBMC.0.1.CreateFilesystemSuccess"));
259 }
260 
261 void EStoraged::mountFilesystem()
262 {
263     /*
264      * Create directory for the filesystem, if it's not already present. It
265      * might already exist if, for example, the BMC reboots after creating the
266      * directory.
267      */
268     if (!fsIface->directoryExists(std::filesystem::path(mountPoint)))
269     {
270         bool success =
271             fsIface->createDirectory(std::filesystem::path(mountPoint));
272         if (!success)
273         {
274             lg2::error("Failed to create mount point: {DIR}", "DIR", mountPoint,
275                        "REDFISH_MESSAGE_ID",
276                        std::string("OpenBMC.0.1.MountFilesystemFail"));
277             throw InternalFailure();
278         }
279     }
280 
281     /* Run the command to mount the filesystem. */
282     std::string luksContainer("/dev/mapper/" + containerName);
283     int retval = fsIface->doMount(luksContainer.c_str(), mountPoint.c_str(),
284                                   "ext4", 0, nullptr);
285     if (retval != 0)
286     {
287         lg2::error("Failed to mount filesystem: {RETVAL}", "RETVAL", retval,
288                    "REDFISH_MESSAGE_ID",
289                    std::string("OpenBMC.0.1.MountFilesystemFail"));
290         bool removeSuccess =
291             fsIface->removeDirectory(std::filesystem::path(mountPoint));
292         if (!removeSuccess)
293         {
294             lg2::error("Failed to remove mount point: {DIR}", "DIR", mountPoint,
295                        "REDFISH_MESSAGE_ID",
296                        std::string("OpenBMC.0.1.MountFilesystemFail"));
297         }
298         throw InternalFailure();
299     }
300 
301     lg2::info("Successfully mounted filesystem at {DIR}", "DIR", mountPoint,
302               "REDFISH_MESSAGE_ID",
303               std::string("OpenBMC.0.1.MountFilesystemSuccess"));
304 }
305 
306 void EStoraged::unmountFilesystem()
307 {
308     int retval = fsIface->doUnmount(mountPoint.c_str());
309     if (retval != 0)
310     {
311         lg2::error("Failed to unmount filesystem: {RETVAL}", "RETVAL", retval,
312                    "REDFISH_MESSAGE_ID",
313                    std::string("OpenBMC.0.1.UnmountFilesystemFail"));
314         throw InternalFailure();
315     }
316 
317     /* Remove the mount point. */
318     bool success = fsIface->removeDirectory(std::filesystem::path(mountPoint));
319     if (!success)
320     {
321         lg2::error("Failed to remove mount point {DIR}", "DIR", mountPoint,
322                    "REDFISH_MESSAGE_ID",
323                    std::string("OpenBMC.0.1.UnmountFilesystemFail"));
324         throw InternalFailure();
325     }
326 
327     lg2::info("Successfully unmounted filesystem at {DIR}", "DIR", mountPoint,
328               "REDFISH_MESSAGE_ID",
329               std::string("OpenBMC.0.1.MountFilesystemSuccess"));
330 }
331 
332 void EStoraged::deactivateLuksDev()
333 {
334     lg2::info("Deactivating LUKS device {DEV}", "DEV", devPath,
335               "REDFISH_MESSAGE_ID",
336               std::string("OpenBMC.0.1.DeactivateLuksDev"));
337 
338     int retval = cryptIface->cryptDeactivate(nullptr, containerName.c_str());
339     if (retval < 0)
340     {
341         lg2::error("Failed to deactivate crypt device: {RETVAL}", "RETVAL",
342                    retval, "REDFISH_MESSAGE_ID",
343                    std::string("OpenBMC.0.1.DeactivateLuksDevFail"));
344         throw InternalFailure();
345     }
346 
347     /* Device is now locked. */
348     locked(true);
349 
350     lg2::info("Successfully deactivated LUKS device {DEV}", "DEV", devPath,
351               "REDFISH_MESSAGE_ID",
352               std::string("OpenBMC.0.1.DeactivateLuksDevSuccess"));
353 }
354 
355 } // namespace estoraged
356