xref: /openbmc/estoraged/src/estoraged.cpp (revision 59dffa63af63ac1021f372e4505fb8c1b9084906)
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 #include <stdlib.h>
13 
14 #include <phosphor-logging/lg2.hpp>
15 #include <xyz/openbmc_project/Common/error.hpp>
16 
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             break;
108         }
109     }
110 }
111 
112 void eStoraged::lock()
113 {
114     std::string msg = "OpenBMC.0.1.DriveLock";
115     lg2::info("Starting lock", "REDFISH_MESSAGE_ID", msg);
116 
117     unmountFilesystem();
118     deactivateLuksDev();
119 }
120 
121 void eStoraged::unlock(std::vector<uint8_t> password)
122 {
123     std::string msg = "OpenBMC.0.1.DriveUnlock";
124     lg2::info("Starting unlock", "REDFISH_MESSAGE_ID", msg);
125 
126     CryptHandle cryptHandle(devPath.c_str());
127     if (cryptHandle.get() == nullptr)
128     {
129         lg2::error("Failed to initialize crypt device", "REDFISH_MESSAGE_ID",
130                    std::string("OpenBMC.0.1.UnlockFail"));
131         throw ResourceNotFound();
132     }
133 
134     activateLuksDev(cryptHandle.get(), password);
135     mountFilesystem();
136 }
137 
138 void eStoraged::changePassword(std::vector<uint8_t>, std::vector<uint8_t>)
139 {
140     std::cerr << "Changing password for encrypted eMMC" << std::endl;
141     lg2::info("Starting change password", "REDFISH_MESSAGE_ID",
142               std::string("OpenBMC.0.1.DrivePasswordChanged"));
143 }
144 
145 bool eStoraged::isLocked() const
146 {
147     return locked();
148 }
149 
150 std::string_view eStoraged::getMountPoint() const
151 {
152     return mountPoint;
153 }
154 
155 void eStoraged::formatLuksDev(struct crypt_device* cd,
156                               std::vector<uint8_t> password)
157 {
158     lg2::info("Formatting device {DEV}", "DEV", devPath, "REDFISH_MESSAGE_ID",
159               std::string("OpenBMC.0.1.FormatLuksDev"));
160 
161     /* Generate the volume key. */
162     const std::size_t keySize = 64;
163     std::vector<uint8_t> volumeKey(keySize);
164     if (RAND_bytes(volumeKey.data(), keySize) != 1)
165     {
166         lg2::error("Failed to create volume key", "REDFISH_MESSAGE_ID",
167                    std::string("OpenBMC.0.1.FormatLuksDevFail"));
168         throw InternalFailure();
169     }
170     /* Format the LUKS encrypted device. */
171     int retval =
172         cryptIface->cryptFormat(cd, CRYPT_LUKS2, "aes", "xts-plain64", nullptr,
173                                 reinterpret_cast<const char*>(volumeKey.data()),
174                                 volumeKey.size(), nullptr);
175     if (retval < 0)
176     {
177         lg2::error("Failed to format encrypted device: {RETVAL}", "RETVAL",
178                    retval, "REDFISH_MESSAGE_ID",
179                    std::string("OpenBMC.0.1.FormatLuksDevFail"));
180         throw InternalFailure();
181     }
182 
183     /* Device is now encrypted. */
184     locked(true);
185 
186     /* Set the password. */
187     retval = cryptIface->cryptKeyslotAddByVolumeKey(
188         cd, CRYPT_ANY_SLOT, nullptr, 0,
189         reinterpret_cast<const char*>(password.data()), password.size());
190 
191     if (retval < 0)
192     {
193         lg2::error("Failed to set encryption password", "REDFISH_MESSAGE_ID",
194                    std::string("OpenBMC.0.1.FormatLuksDevFail"));
195         throw InternalFailure();
196     }
197 
198     lg2::info("Encrypted device {DEV} successfully formatted", "DEV", devPath,
199               "REDFISH_MESSAGE_ID",
200               std::string("OpenBMC.0.1.FormatLuksDevSuccess"));
201 }
202 
203 void eStoraged::activateLuksDev(struct crypt_device* cd,
204                                 std::vector<uint8_t> password)
205 {
206     lg2::info("Activating LUKS dev {DEV}", "DEV", devPath, "REDFISH_MESSAGE_ID",
207               std::string("OpenBMC.0.1.ActivateLuksDev"));
208 
209     int retval = cryptIface->cryptLoad(cd, CRYPT_LUKS2, nullptr);
210     if (retval < 0)
211     {
212         lg2::error("Failed to load LUKS header: {RETVAL}", "RETVAL", retval,
213                    "REDFISH_MESSAGE_ID",
214                    std::string("OpenBMC.0.1.ActivateLuksDevFail"));
215         throw InternalFailure();
216     }
217 
218     retval = cryptIface->cryptActivateByPassphrase(
219         cd, containerName.c_str(), CRYPT_ANY_SLOT,
220         reinterpret_cast<const char*>(password.data()), password.size(), 0);
221 
222     if (retval < 0)
223     {
224         lg2::error("Failed to activate LUKS dev: {RETVAL}", "RETVAL", retval,
225                    "REDFISH_MESSAGE_ID",
226                    std::string("OpenBMC.0.1.ActivateLuksDevFail"));
227         throw InternalFailure();
228     }
229 
230     /* Device is now unlocked. */
231     locked(false);
232 
233     lg2::info("Successfully activated LUKS dev {DEV}", "DEV", devPath,
234               "REDFISH_MESSAGE_ID",
235               std::string("OpenBMC.0.1.ActivateLuksDevSuccess"));
236 }
237 
238 void eStoraged::createFilesystem()
239 {
240     /* Run the command to create the filesystem. */
241     int retval = fsIface->runMkfs(containerName);
242     if (retval)
243     {
244         lg2::error("Failed to create filesystem: {RETVAL}", "RETVAL", retval,
245                    "REDFISH_MESSAGE_ID",
246                    std::string("OpenBMC.0.1.CreateFilesystemFail"));
247         throw InternalFailure();
248     }
249     lg2::info("Successfully created filesystem for /dev/mapper/{CONTAINER}",
250               "CONTAINER", containerName, "REDFISH_MESSAGE_ID",
251               std::string("OpenBMC.0.1.CreateFilesystemSuccess"));
252 }
253 
254 void eStoraged::mountFilesystem()
255 {
256     /*
257      * Create directory for the filesystem, if it's not already present. It
258      * might already exist if, for example, the BMC reboots after creating the
259      * directory.
260      */
261     if (!fsIface->directoryExists(std::filesystem::path(mountPoint)))
262     {
263         bool success =
264             fsIface->createDirectory(std::filesystem::path(mountPoint));
265         if (!success)
266         {
267             lg2::error("Failed to create mount point: {DIR}", "DIR", mountPoint,
268                        "REDFISH_MESSAGE_ID",
269                        std::string("OpenBMC.0.1.MountFilesystemFail"));
270             throw InternalFailure();
271         }
272     }
273 
274     /* Run the command to mount the filesystem. */
275     std::string luksContainer("/dev/mapper/" + containerName);
276     int retval = fsIface->doMount(luksContainer.c_str(), mountPoint.c_str(),
277                                   "ext4", 0, nullptr);
278     if (retval)
279     {
280         lg2::error("Failed to mount filesystem: {RETVAL}", "RETVAL", retval,
281                    "REDFISH_MESSAGE_ID",
282                    std::string("OpenBMC.0.1.MountFilesystemFail"));
283         bool removeSuccess =
284             fsIface->removeDirectory(std::filesystem::path(mountPoint));
285         if (!removeSuccess)
286         {
287             lg2::error("Failed to remove mount point: {DIR}", "DIR", mountPoint,
288                        "REDFISH_MESSAGE_ID",
289                        std::string("OpenBMC.0.1.MountFilesystemFail"));
290         }
291         throw InternalFailure();
292     }
293 
294     lg2::info("Successfully mounted filesystem at {DIR}", "DIR", mountPoint,
295               "REDFISH_MESSAGE_ID",
296               std::string("OpenBMC.0.1.MountFilesystemSuccess"));
297 }
298 
299 void eStoraged::unmountFilesystem()
300 {
301     int retval = fsIface->doUnmount(mountPoint.c_str());
302     if (retval)
303     {
304         lg2::error("Failed to unmount filesystem: {RETVAL}", "RETVAL", retval,
305                    "REDFISH_MESSAGE_ID",
306                    std::string("OpenBMC.0.1.UnmountFilesystemFail"));
307         throw InternalFailure();
308     }
309 
310     /* Remove the mount point. */
311     bool success = fsIface->removeDirectory(std::filesystem::path(mountPoint));
312     if (!success)
313     {
314         lg2::error("Failed to remove mount point {DIR}", "DIR", mountPoint,
315                    "REDFISH_MESSAGE_ID",
316                    std::string("OpenBMC.0.1.UnmountFilesystemFail"));
317         throw InternalFailure();
318     }
319 
320     lg2::info("Successfully unmounted filesystem at {DIR}", "DIR", mountPoint,
321               "REDFISH_MESSAGE_ID",
322               std::string("OpenBMC.0.1.MountFilesystemSuccess"));
323 }
324 
325 void eStoraged::deactivateLuksDev()
326 {
327     lg2::info("Deactivating LUKS device {DEV}", "DEV", devPath,
328               "REDFISH_MESSAGE_ID",
329               std::string("OpenBMC.0.1.DeactivateLuksDev"));
330 
331     int retval = cryptIface->cryptDeactivate(nullptr, containerName.c_str());
332     if (retval < 0)
333     {
334         lg2::error("Failed to deactivate crypt device: {RETVAL}", "RETVAL",
335                    retval, "REDFISH_MESSAGE_ID",
336                    std::string("OpenBMC.0.1.DeactivateLuksDevFail"));
337         throw InternalFailure();
338     }
339 
340     /* Device is now locked. */
341     locked(true);
342 
343     lg2::info("Successfully deactivated LUKS device {DEV}", "DEV", devPath,
344               "REDFISH_MESSAGE_ID",
345               std::string("OpenBMC.0.1.DeactivateLuksDevSuccess"));
346 }
347 
348 } // namespace estoraged
349