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