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