xref: /openbmc/estoraged/src/estoraged.cpp (revision 7f2ab643)
1 
2 #include "estoraged.hpp"
3 
4 #include "cryptsetupInterface.hpp"
5 #include "pattern.hpp"
6 #include "verifyDriveGeometry.hpp"
7 
8 #include <libcryptsetup.h>
9 #include <openssl/rand.h>
10 #include <stdlib.h>
11 
12 #include <phosphor-logging/lg2.hpp>
13 #include <xyz/openbmc_project/Common/error.hpp>
14 
15 #include <filesystem>
16 #include <iostream>
17 #include <string_view>
18 #include <vector>
19 
20 namespace estoraged
21 {
22 
23 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
24 using sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound;
25 using sdbusplus::xyz::openbmc_project::Common::Error::UnsupportedRequest;
26 
27 void eStoraged::formatLuks(std::vector<uint8_t> password, FilesystemType type)
28 {
29     std::string msg = "OpenBMC.0.1.DriveFormat";
30     lg2::info("Starting format", "REDFISH_MESSAGE_ID", msg);
31 
32     if (type != FilesystemType::ext4)
33     {
34         lg2::error("Only ext4 filesystems are supported currently",
35                    "REDFISH_MESSAGE_ID", std::string("OpenBMC.0.1.FormatFail"));
36         throw UnsupportedRequest();
37     }
38 
39     CryptHandle cryptHandle(devPath.c_str());
40     if (cryptHandle.get() == nullptr)
41     {
42         lg2::error("Failed to initialize crypt device", "REDFISH_MESSAGE_ID",
43                    std::string("OpenBMC.0.1.FormatFail"));
44         throw ResourceNotFound();
45     }
46 
47     formatLuksDev(cryptHandle.get(), password);
48     activateLuksDev(cryptHandle.get(), password);
49 
50     createFilesystem();
51     mountFilesystem();
52 }
53 
54 void eStoraged::erase(EraseMethod inEraseMethod)
55 {
56     std::cerr << "Erasing encrypted eMMC" << std::endl;
57     lg2::info("Starting erase", "REDFISH_MESSAGE_ID",
58               std::string("OpenBMC.0.1.DriveErase"));
59     switch (inEraseMethod)
60     {
61         case EraseMethod::CryptoErase:
62         {
63             break;
64         }
65         case EraseMethod::VerifyGeometry:
66         {
67             VerifyDriveGeometry myVerifyGeometry(devPath);
68             uint64_t size = myVerifyGeometry.findSizeOfBlockDevice();
69             myVerifyGeometry.geometryOkay(size);
70             break;
71         }
72         case EraseMethod::LogicalOverWrite:
73         {
74             Pattern myErasePattern(devPath);
75             ManagedFd drivefd =
76                 stdplus::fd::open(devPath, stdplus::fd::OpenAccess::WriteOnly);
77             myErasePattern.writePattern(myErasePattern.findSizeOfBlockDevice(),
78                                         drivefd);
79             break;
80         }
81         case EraseMethod::LogicalVerify:
82         {
83             Pattern myErasePattern(devPath);
84             ManagedFd drivefd =
85                 stdplus::fd::open(devPath, stdplus::fd::OpenAccess::ReadOnly);
86             myErasePattern.verifyPattern(myErasePattern.findSizeOfBlockDevice(),
87                                          drivefd);
88             break;
89         }
90         case EraseMethod::VendorSanitize:
91         {
92             break;
93         }
94         case EraseMethod::ZeroOverWrite:
95         {
96             break;
97         }
98         case EraseMethod::ZeroVerify:
99         {
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     /* Create directory for the filesystem. */
254     bool success = fsIface->createDirectory(std::filesystem::path(mountPoint));
255     if (!success)
256     {
257         lg2::error("Failed to create mount point: {DIR}", "DIR", mountPoint,
258                    "REDFISH_MESSAGE_ID",
259                    std::string("OpenBMC.0.1.MountFilesystemFail"));
260         throw InternalFailure();
261     }
262 
263     /* Run the command to mount the filesystem. */
264     std::string luksContainer("/dev/mapper/" + containerName);
265     int retval = fsIface->doMount(luksContainer.c_str(), mountPoint.c_str(),
266                                   "ext4", 0, nullptr);
267     if (retval)
268     {
269         lg2::error("Failed to mount filesystem: {RETVAL}", "RETVAL", retval,
270                    "REDFISH_MESSAGE_ID",
271                    std::string("OpenBMC.0.1.MountFilesystemFail"));
272         bool removeSuccess =
273             fsIface->removeDirectory(std::filesystem::path(mountPoint));
274         if (!removeSuccess)
275         {
276             lg2::error("Failed to remove mount point: {DIR}", "DIR", mountPoint,
277                        "REDFISH_MESSAGE_ID",
278                        std::string("OpenBMC.0.1.MountFilesystemFail"));
279         }
280         throw InternalFailure();
281     }
282 
283     lg2::info("Successfully mounted filesystem at {DIR}", "DIR", mountPoint,
284               "REDFISH_MESSAGE_ID",
285               std::string("OpenBMC.0.1.MountFilesystemSuccess"));
286 }
287 
288 void eStoraged::unmountFilesystem()
289 {
290     int retval = fsIface->doUnmount(mountPoint.c_str());
291     if (retval)
292     {
293         lg2::error("Failed to unmount filesystem: {RETVAL}", "RETVAL", retval,
294                    "REDFISH_MESSAGE_ID",
295                    std::string("OpenBMC.0.1.UnmountFilesystemFail"));
296         throw InternalFailure();
297     }
298 
299     /* Remove the mount point. */
300     bool success = fsIface->removeDirectory(std::filesystem::path(mountPoint));
301     if (!success)
302     {
303         lg2::error("Failed to remove mount point {DIR}", "DIR", mountPoint,
304                    "REDFISH_MESSAGE_ID",
305                    std::string("OpenBMC.0.1.UnmountFilesystemFail"));
306         throw InternalFailure();
307     }
308 
309     lg2::info("Successfully unmounted filesystem at {DIR}", "DIR", mountPoint,
310               "REDFISH_MESSAGE_ID",
311               std::string("OpenBMC.0.1.MountFilesystemSuccess"));
312 }
313 
314 void eStoraged::deactivateLuksDev()
315 {
316     lg2::info("Deactivating LUKS device {DEV}", "DEV", devPath,
317               "REDFISH_MESSAGE_ID",
318               std::string("OpenBMC.0.1.DeactivateLuksDev"));
319 
320     int retval = cryptIface->cryptDeactivate(nullptr, containerName.c_str());
321     if (retval < 0)
322     {
323         lg2::error("Failed to deactivate crypt device: {RETVAL}", "RETVAL",
324                    retval, "REDFISH_MESSAGE_ID",
325                    std::string("OpenBMC.0.1.DeactivateLuksDevFail"));
326         throw InternalFailure();
327     }
328 
329     /* Device is now locked. */
330     locked(true);
331 
332     lg2::info("Successfully deactivated LUKS device {DEV}", "DEV", devPath,
333               "REDFISH_MESSAGE_ID",
334               std::string("OpenBMC.0.1.DeactivateLuksDevSuccess"));
335 }
336 
337 } // namespace estoraged
338