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