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