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