xref: /openbmc/estoraged/src/estoraged.cpp (revision 4bc8a10caff8d7154d9ad789b9d0fc01499fa02e)
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             ManagedFd drivefd =
77                 stdplus::fd::open(devPath, stdplus::fd::OpenAccess::WriteOnly);
78             myErasePattern.writePattern(myErasePattern.findSizeOfBlockDevice(),
79                                         drivefd);
80             break;
81         }
82         case EraseMethod::LogicalVerify:
83         {
84             Pattern myErasePattern(devPath);
85             ManagedFd drivefd =
86                 stdplus::fd::open(devPath, stdplus::fd::OpenAccess::ReadOnly);
87             myErasePattern.verifyPattern(myErasePattern.findSizeOfBlockDevice(),
88                                          drivefd);
89             break;
90         }
91         case EraseMethod::VendorSanitize:
92         {
93             break;
94         }
95         case EraseMethod::ZeroOverWrite:
96         {
97             Zero myZero(devPath);
98             ManagedFd drivefd =
99                 stdplus::fd::open(devPath, stdplus::fd::OpenAccess::WriteOnly);
100             myZero.writeZero(myZero.findSizeOfBlockDevice(), drivefd);
101             break;
102         }
103         case EraseMethod::ZeroVerify:
104         {
105             Zero myZero(devPath);
106             ManagedFd drivefd =
107                 stdplus::fd::open(devPath, stdplus::fd::OpenAccess::ReadOnly);
108             myZero.verifyZero(myZero.findSizeOfBlockDevice(), drivefd);
109             break;
110         }
111         case EraseMethod::SecuredLocked:
112         {
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