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 <sdbusplus/asio/object_server.hpp> 16 #include <xyz/openbmc_project/Common/error.hpp> 17 18 #include <cstdlib> 19 #include <filesystem> 20 #include <iostream> 21 #include <string> 22 #include <string_view> 23 #include <utility> 24 #include <vector> 25 26 namespace estoraged 27 { 28 29 using sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 30 using sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound; 31 using sdbusplus::xyz::openbmc_project::Common::Error::UnsupportedRequest; 32 using sdbusplus::xyz::openbmc_project::Inventory::Item::server::Volume; 33 34 EStoraged::EStoraged(sdbusplus::asio::object_server& server, 35 const std::string& devPath, const std::string& luksName, 36 uint64_t size, 37 std::unique_ptr<CryptsetupInterface> cryptInterface, 38 std::unique_ptr<FilesystemInterface> fsInterface) : 39 devPath(devPath), 40 containerName(luksName), mountPoint("/mnt/" + luksName + "_fs"), 41 lockedProperty(false), cryptIface(std::move(cryptInterface)), 42 fsIface(std::move(fsInterface)), objectServer(server) 43 { 44 /* Get the filename of the device (without "/dev/"). */ 45 std::string deviceName = std::filesystem::path(devPath).filename().string(); 46 /* DBus object path */ 47 std::string path = "/xyz/openbmc_project/inventory/storage/" + deviceName; 48 49 /* Add Volume interface. */ 50 volumeInterface = objectServer.add_interface( 51 path, "xyz.openbmc_project.Inventory.Item.Volume"); 52 volumeInterface->register_method( 53 "FormatLuks", [this](const std::vector<uint8_t>& password, 54 Volume::FilesystemType type) { 55 this->formatLuks(password, type); 56 }); 57 volumeInterface->register_method( 58 "Erase", 59 [this](Volume::EraseMethod eraseType) { this->erase(eraseType); }); 60 volumeInterface->register_method("Lock", [this]() { this->lock(); }); 61 volumeInterface->register_method( 62 "Unlock", 63 [this](std::vector<uint8_t>& password) { this->unlock(password); }); 64 volumeInterface->register_method( 65 "ChangePassword", [this](const std::vector<uint8_t>& oldPassword, 66 const std::vector<uint8_t>& newPassword) { 67 this->changePassword(oldPassword, newPassword); 68 }); 69 volumeInterface->register_property_r( 70 "Locked", lockedProperty, sdbusplus::vtable::property_::emits_change, 71 [this](bool& value) { 72 value = this->isLocked(); 73 return value; 74 }); 75 76 /* Add Drive interface. */ 77 driveInterface = objectServer.add_interface( 78 path, "xyz.openbmc_project.Inventory.Item.Drive"); 79 driveInterface->register_property("Capacity", size); 80 81 volumeInterface->initialize(); 82 driveInterface->initialize(); 83 } 84 85 EStoraged::~EStoraged() 86 { 87 objectServer.remove_interface(volumeInterface); 88 objectServer.remove_interface(driveInterface); 89 } 90 91 void EStoraged::formatLuks(const std::vector<uint8_t>& password, 92 Volume::FilesystemType type) 93 { 94 std::string msg = "OpenBMC.0.1.DriveFormat"; 95 lg2::info("Starting format", "REDFISH_MESSAGE_ID", msg); 96 97 if (type != Volume::FilesystemType::ext4) 98 { 99 lg2::error("Only ext4 filesystems are supported currently", 100 "REDFISH_MESSAGE_ID", std::string("OpenBMC.0.1.FormatFail")); 101 throw UnsupportedRequest(); 102 } 103 104 CryptHandle cryptHandle(devPath.c_str()); 105 if (cryptHandle.get() == nullptr) 106 { 107 lg2::error("Failed to initialize crypt device", "REDFISH_MESSAGE_ID", 108 std::string("OpenBMC.0.1.FormatFail")); 109 throw ResourceNotFound(); 110 } 111 112 formatLuksDev(cryptHandle.get(), password); 113 activateLuksDev(cryptHandle.get(), password); 114 115 createFilesystem(); 116 mountFilesystem(); 117 } 118 119 void EStoraged::erase(Volume::EraseMethod inEraseMethod) 120 { 121 std::cerr << "Erasing encrypted eMMC" << std::endl; 122 lg2::info("Starting erase", "REDFISH_MESSAGE_ID", 123 std::string("OpenBMC.0.1.DriveErase")); 124 switch (inEraseMethod) 125 { 126 case Volume::EraseMethod::CryptoErase: 127 { 128 CryptErase myCryptErase(devPath); 129 myCryptErase.doErase(); 130 break; 131 } 132 case Volume::EraseMethod::VerifyGeometry: 133 { 134 VerifyDriveGeometry myVerifyGeometry(devPath); 135 myVerifyGeometry.geometryOkay(); 136 break; 137 } 138 case Volume::EraseMethod::LogicalOverWrite: 139 { 140 Pattern myErasePattern(devPath); 141 myErasePattern.writePattern(); 142 break; 143 } 144 case Volume::EraseMethod::LogicalVerify: 145 { 146 Pattern myErasePattern(devPath); 147 myErasePattern.verifyPattern(); 148 break; 149 } 150 case Volume::EraseMethod::VendorSanitize: 151 { 152 Sanitize mySanitize(devPath); 153 mySanitize.doSanitize(); 154 break; 155 } 156 case Volume::EraseMethod::ZeroOverWrite: 157 { 158 Zero myZero(devPath); 159 myZero.writeZero(); 160 break; 161 } 162 case Volume::EraseMethod::ZeroVerify: 163 { 164 Zero myZero(devPath); 165 myZero.verifyZero(); 166 break; 167 } 168 case Volume::EraseMethod::SecuredLocked: 169 { 170 if (isLocked()) 171 { 172 lock(); 173 } 174 // TODO: implement hardware locking 175 // Until that is done, we can lock using eStoraged::lock() 176 break; 177 } 178 } 179 } 180 181 void EStoraged::lock() 182 { 183 std::string msg = "OpenBMC.0.1.DriveLock"; 184 lg2::info("Starting lock", "REDFISH_MESSAGE_ID", msg); 185 186 unmountFilesystem(); 187 deactivateLuksDev(); 188 } 189 190 void EStoraged::unlock(std::vector<uint8_t> password) 191 { 192 std::string msg = "OpenBMC.0.1.DriveUnlock"; 193 lg2::info("Starting unlock", "REDFISH_MESSAGE_ID", msg); 194 195 CryptHandle cryptHandle(devPath.c_str()); 196 if (cryptHandle.get() == nullptr) 197 { 198 lg2::error("Failed to initialize crypt device", "REDFISH_MESSAGE_ID", 199 std::string("OpenBMC.0.1.UnlockFail")); 200 throw ResourceNotFound(); 201 } 202 203 activateLuksDev(cryptHandle.get(), std::move(password)); 204 mountFilesystem(); 205 } 206 207 void EStoraged::changePassword(const std::vector<uint8_t>& /*oldPassword*/, 208 const std::vector<uint8_t>& /*newPassword*/) 209 { 210 std::cerr << "Changing password for encrypted eMMC" << std::endl; 211 lg2::info("Starting change password", "REDFISH_MESSAGE_ID", 212 std::string("OpenBMC.0.1.DrivePasswordChanged")); 213 } 214 215 bool EStoraged::isLocked() const 216 { 217 return lockedProperty; 218 } 219 220 std::string_view EStoraged::getMountPoint() const 221 { 222 return mountPoint; 223 } 224 225 void EStoraged::formatLuksDev(struct crypt_device* cd, 226 std::vector<uint8_t> password) 227 { 228 lg2::info("Formatting device {DEV}", "DEV", devPath, "REDFISH_MESSAGE_ID", 229 std::string("OpenBMC.0.1.FormatLuksDev")); 230 231 /* Generate the volume key. */ 232 const std::size_t keySize = 64; 233 std::vector<uint8_t> volumeKey(keySize); 234 if (RAND_bytes(volumeKey.data(), keySize) != 1) 235 { 236 lg2::error("Failed to create volume key", "REDFISH_MESSAGE_ID", 237 std::string("OpenBMC.0.1.FormatLuksDevFail")); 238 throw InternalFailure(); 239 } 240 /* Format the LUKS encrypted device. */ 241 int retval = 242 cryptIface->cryptFormat(cd, CRYPT_LUKS2, "aes", "xts-plain64", nullptr, 243 reinterpret_cast<const char*>(volumeKey.data()), 244 volumeKey.size(), nullptr); 245 if (retval < 0) 246 { 247 lg2::error("Failed to format encrypted device: {RETVAL}", "RETVAL", 248 retval, "REDFISH_MESSAGE_ID", 249 std::string("OpenBMC.0.1.FormatLuksDevFail")); 250 throw InternalFailure(); 251 } 252 253 /* Device is now encrypted. */ 254 locked(true); 255 256 /* Set the password. */ 257 retval = cryptIface->cryptKeyslotAddByVolumeKey( 258 cd, CRYPT_ANY_SLOT, nullptr, 0, 259 reinterpret_cast<const char*>(password.data()), password.size()); 260 261 if (retval < 0) 262 { 263 lg2::error("Failed to set encryption password", "REDFISH_MESSAGE_ID", 264 std::string("OpenBMC.0.1.FormatLuksDevFail")); 265 throw InternalFailure(); 266 } 267 268 lg2::info("Encrypted device {DEV} successfully formatted", "DEV", devPath, 269 "REDFISH_MESSAGE_ID", 270 std::string("OpenBMC.0.1.FormatLuksDevSuccess")); 271 } 272 273 void EStoraged::activateLuksDev(struct crypt_device* cd, 274 std::vector<uint8_t> password) 275 { 276 lg2::info("Activating LUKS dev {DEV}", "DEV", devPath, "REDFISH_MESSAGE_ID", 277 std::string("OpenBMC.0.1.ActivateLuksDev")); 278 279 int retval = cryptIface->cryptLoad(cd, CRYPT_LUKS2, nullptr); 280 if (retval < 0) 281 { 282 lg2::error("Failed to load LUKS header: {RETVAL}", "RETVAL", retval, 283 "REDFISH_MESSAGE_ID", 284 std::string("OpenBMC.0.1.ActivateLuksDevFail")); 285 throw InternalFailure(); 286 } 287 288 retval = cryptIface->cryptActivateByPassphrase( 289 cd, containerName.c_str(), CRYPT_ANY_SLOT, 290 reinterpret_cast<const char*>(password.data()), password.size(), 0); 291 292 if (retval < 0) 293 { 294 lg2::error("Failed to activate LUKS dev: {RETVAL}", "RETVAL", retval, 295 "REDFISH_MESSAGE_ID", 296 std::string("OpenBMC.0.1.ActivateLuksDevFail")); 297 throw InternalFailure(); 298 } 299 300 /* Device is now unlocked. */ 301 locked(false); 302 303 lg2::info("Successfully activated LUKS dev {DEV}", "DEV", devPath, 304 "REDFISH_MESSAGE_ID", 305 std::string("OpenBMC.0.1.ActivateLuksDevSuccess")); 306 } 307 308 void EStoraged::createFilesystem() 309 { 310 /* Run the command to create the filesystem. */ 311 int retval = fsIface->runMkfs(containerName); 312 if (retval != 0) 313 { 314 lg2::error("Failed to create filesystem: {RETVAL}", "RETVAL", retval, 315 "REDFISH_MESSAGE_ID", 316 std::string("OpenBMC.0.1.CreateFilesystemFail")); 317 throw InternalFailure(); 318 } 319 lg2::info("Successfully created filesystem for /dev/mapper/{CONTAINER}", 320 "CONTAINER", containerName, "REDFISH_MESSAGE_ID", 321 std::string("OpenBMC.0.1.CreateFilesystemSuccess")); 322 } 323 324 void EStoraged::mountFilesystem() 325 { 326 /* 327 * Create directory for the filesystem, if it's not already present. It 328 * might already exist if, for example, the BMC reboots after creating the 329 * directory. 330 */ 331 if (!fsIface->directoryExists(std::filesystem::path(mountPoint))) 332 { 333 bool success = 334 fsIface->createDirectory(std::filesystem::path(mountPoint)); 335 if (!success) 336 { 337 lg2::error("Failed to create mount point: {DIR}", "DIR", mountPoint, 338 "REDFISH_MESSAGE_ID", 339 std::string("OpenBMC.0.1.MountFilesystemFail")); 340 throw InternalFailure(); 341 } 342 } 343 344 /* Run the command to mount the filesystem. */ 345 std::string luksContainer("/dev/mapper/" + containerName); 346 int retval = fsIface->doMount(luksContainer.c_str(), mountPoint.c_str(), 347 "ext4", 0, nullptr); 348 if (retval != 0) 349 { 350 lg2::error("Failed to mount filesystem: {RETVAL}", "RETVAL", retval, 351 "REDFISH_MESSAGE_ID", 352 std::string("OpenBMC.0.1.MountFilesystemFail")); 353 bool removeSuccess = 354 fsIface->removeDirectory(std::filesystem::path(mountPoint)); 355 if (!removeSuccess) 356 { 357 lg2::error("Failed to remove mount point: {DIR}", "DIR", mountPoint, 358 "REDFISH_MESSAGE_ID", 359 std::string("OpenBMC.0.1.MountFilesystemFail")); 360 } 361 throw InternalFailure(); 362 } 363 364 lg2::info("Successfully mounted filesystem at {DIR}", "DIR", mountPoint, 365 "REDFISH_MESSAGE_ID", 366 std::string("OpenBMC.0.1.MountFilesystemSuccess")); 367 } 368 369 void EStoraged::unmountFilesystem() 370 { 371 int retval = fsIface->doUnmount(mountPoint.c_str()); 372 if (retval != 0) 373 { 374 lg2::error("Failed to unmount filesystem: {RETVAL}", "RETVAL", retval, 375 "REDFISH_MESSAGE_ID", 376 std::string("OpenBMC.0.1.UnmountFilesystemFail")); 377 throw InternalFailure(); 378 } 379 380 /* Remove the mount point. */ 381 bool success = fsIface->removeDirectory(std::filesystem::path(mountPoint)); 382 if (!success) 383 { 384 lg2::error("Failed to remove mount point {DIR}", "DIR", mountPoint, 385 "REDFISH_MESSAGE_ID", 386 std::string("OpenBMC.0.1.UnmountFilesystemFail")); 387 throw InternalFailure(); 388 } 389 390 lg2::info("Successfully unmounted filesystem at {DIR}", "DIR", mountPoint, 391 "REDFISH_MESSAGE_ID", 392 std::string("OpenBMC.0.1.MountFilesystemSuccess")); 393 } 394 395 void EStoraged::deactivateLuksDev() 396 { 397 lg2::info("Deactivating LUKS device {DEV}", "DEV", devPath, 398 "REDFISH_MESSAGE_ID", 399 std::string("OpenBMC.0.1.DeactivateLuksDev")); 400 401 int retval = cryptIface->cryptDeactivate(nullptr, containerName.c_str()); 402 if (retval < 0) 403 { 404 lg2::error("Failed to deactivate crypt device: {RETVAL}", "RETVAL", 405 retval, "REDFISH_MESSAGE_ID", 406 std::string("OpenBMC.0.1.DeactivateLuksDevFail")); 407 throw InternalFailure(); 408 } 409 410 /* Device is now locked. */ 411 locked(true); 412 413 lg2::info("Successfully deactivated LUKS device {DEV}", "DEV", devPath, 414 "REDFISH_MESSAGE_ID", 415 std::string("OpenBMC.0.1.DeactivateLuksDevSuccess")); 416 } 417 418 void EStoraged::locked(bool isLocked) 419 { 420 lockedProperty = isLocked; 421 } 422 423 } // namespace estoraged 424