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