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 CryptHandle cryptHandle(devPath.c_str()); 106 107 formatLuksDev(cryptHandle.get(), password); 108 activateLuksDev(cryptHandle.get(), password); 109 110 createFilesystem(); 111 mountFilesystem(); 112 } 113 114 void EStoraged::erase(Volume::EraseMethod inEraseMethod) 115 { 116 std::cerr << "Erasing encrypted eMMC" << std::endl; 117 lg2::info("Starting erase", "REDFISH_MESSAGE_ID", 118 std::string("OpenBMC.0.1.DriveErase")); 119 switch (inEraseMethod) 120 { 121 case Volume::EraseMethod::CryptoErase: 122 { 123 CryptErase myCryptErase(devPath); 124 myCryptErase.doErase(); 125 break; 126 } 127 case Volume::EraseMethod::VerifyGeometry: 128 { 129 VerifyDriveGeometry myVerifyGeometry(devPath); 130 myVerifyGeometry.geometryOkay(); 131 break; 132 } 133 case Volume::EraseMethod::LogicalOverWrite: 134 { 135 Pattern myErasePattern(devPath); 136 myErasePattern.writePattern(); 137 break; 138 } 139 case Volume::EraseMethod::LogicalVerify: 140 { 141 Pattern myErasePattern(devPath); 142 myErasePattern.verifyPattern(); 143 break; 144 } 145 case Volume::EraseMethod::VendorSanitize: 146 { 147 Sanitize mySanitize(devPath); 148 mySanitize.doSanitize(); 149 break; 150 } 151 case Volume::EraseMethod::ZeroOverWrite: 152 { 153 Zero myZero(devPath); 154 myZero.writeZero(); 155 break; 156 } 157 case Volume::EraseMethod::ZeroVerify: 158 { 159 Zero myZero(devPath); 160 myZero.verifyZero(); 161 break; 162 } 163 case Volume::EraseMethod::SecuredLocked: 164 { 165 if (isLocked()) 166 { 167 lock(); 168 } 169 // TODO: implement hardware locking 170 // Until that is done, we can lock using eStoraged::lock() 171 break; 172 } 173 } 174 } 175 176 void EStoraged::lock() 177 { 178 std::string msg = "OpenBMC.0.1.DriveLock"; 179 lg2::info("Starting lock", "REDFISH_MESSAGE_ID", msg); 180 181 unmountFilesystem(); 182 deactivateLuksDev(); 183 } 184 185 void EStoraged::unlock(std::vector<uint8_t> password) 186 { 187 std::string msg = "OpenBMC.0.1.DriveUnlock"; 188 lg2::info("Starting unlock", "REDFISH_MESSAGE_ID", msg); 189 190 CryptHandle cryptHandle(devPath.c_str()); 191 192 activateLuksDev(cryptHandle.get(), std::move(password)); 193 mountFilesystem(); 194 } 195 196 void EStoraged::changePassword(const std::vector<uint8_t>& /*oldPassword*/, 197 const std::vector<uint8_t>& /*newPassword*/) 198 { 199 std::cerr << "Changing password for encrypted eMMC" << std::endl; 200 lg2::info("Starting change password", "REDFISH_MESSAGE_ID", 201 std::string("OpenBMC.0.1.DrivePasswordChanged")); 202 } 203 204 bool EStoraged::isLocked() const 205 { 206 return lockedProperty; 207 } 208 209 std::string_view EStoraged::getMountPoint() const 210 { 211 return mountPoint; 212 } 213 214 void EStoraged::formatLuksDev(struct crypt_device* cd, 215 std::vector<uint8_t> password) 216 { 217 lg2::info("Formatting device {DEV}", "DEV", devPath, "REDFISH_MESSAGE_ID", 218 std::string("OpenBMC.0.1.FormatLuksDev")); 219 220 /* Generate the volume key. */ 221 const std::size_t keySize = 64; 222 std::vector<uint8_t> volumeKey(keySize); 223 if (RAND_bytes(volumeKey.data(), keySize) != 1) 224 { 225 lg2::error("Failed to create volume key", "REDFISH_MESSAGE_ID", 226 std::string("OpenBMC.0.1.FormatLuksDevFail")); 227 throw InternalFailure(); 228 } 229 /* Format the LUKS encrypted device. */ 230 int retval = 231 cryptIface->cryptFormat(cd, CRYPT_LUKS2, "aes", "xts-plain64", nullptr, 232 reinterpret_cast<const char*>(volumeKey.data()), 233 volumeKey.size(), nullptr); 234 if (retval < 0) 235 { 236 lg2::error("Failed to format encrypted device: {RETVAL}", "RETVAL", 237 retval, "REDFISH_MESSAGE_ID", 238 std::string("OpenBMC.0.1.FormatLuksDevFail")); 239 throw InternalFailure(); 240 } 241 242 /* Device is now encrypted. */ 243 locked(true); 244 245 /* Set the password. */ 246 retval = cryptIface->cryptKeyslotAddByVolumeKey( 247 cd, CRYPT_ANY_SLOT, nullptr, 0, 248 reinterpret_cast<const char*>(password.data()), password.size()); 249 250 if (retval < 0) 251 { 252 lg2::error("Failed to set encryption password", "REDFISH_MESSAGE_ID", 253 std::string("OpenBMC.0.1.FormatLuksDevFail")); 254 throw InternalFailure(); 255 } 256 257 lg2::info("Encrypted device {DEV} successfully formatted", "DEV", devPath, 258 "REDFISH_MESSAGE_ID", 259 std::string("OpenBMC.0.1.FormatLuksDevSuccess")); 260 } 261 262 void EStoraged::activateLuksDev(struct crypt_device* cd, 263 std::vector<uint8_t> password) 264 { 265 lg2::info("Activating LUKS dev {DEV}", "DEV", devPath, "REDFISH_MESSAGE_ID", 266 std::string("OpenBMC.0.1.ActivateLuksDev")); 267 268 int retval = cryptIface->cryptLoad(cd, CRYPT_LUKS2, nullptr); 269 if (retval < 0) 270 { 271 lg2::error("Failed to load LUKS header: {RETVAL}", "RETVAL", retval, 272 "REDFISH_MESSAGE_ID", 273 std::string("OpenBMC.0.1.ActivateLuksDevFail")); 274 throw InternalFailure(); 275 } 276 277 retval = cryptIface->cryptActivateByPassphrase( 278 cd, containerName.c_str(), CRYPT_ANY_SLOT, 279 reinterpret_cast<const char*>(password.data()), password.size(), 0); 280 281 if (retval < 0) 282 { 283 lg2::error("Failed to activate LUKS dev: {RETVAL}", "RETVAL", retval, 284 "REDFISH_MESSAGE_ID", 285 std::string("OpenBMC.0.1.ActivateLuksDevFail")); 286 throw InternalFailure(); 287 } 288 289 /* Device is now unlocked. */ 290 locked(false); 291 292 lg2::info("Successfully activated LUKS dev {DEV}", "DEV", devPath, 293 "REDFISH_MESSAGE_ID", 294 std::string("OpenBMC.0.1.ActivateLuksDevSuccess")); 295 } 296 297 void EStoraged::createFilesystem() 298 { 299 /* Run the command to create the filesystem. */ 300 int retval = fsIface->runMkfs(containerName); 301 if (retval != 0) 302 { 303 lg2::error("Failed to create filesystem: {RETVAL}", "RETVAL", retval, 304 "REDFISH_MESSAGE_ID", 305 std::string("OpenBMC.0.1.CreateFilesystemFail")); 306 throw InternalFailure(); 307 } 308 lg2::info("Successfully created filesystem for /dev/mapper/{CONTAINER}", 309 "CONTAINER", containerName, "REDFISH_MESSAGE_ID", 310 std::string("OpenBMC.0.1.CreateFilesystemSuccess")); 311 } 312 313 void EStoraged::mountFilesystem() 314 { 315 /* 316 * Create directory for the filesystem, if it's not already present. It 317 * might already exist if, for example, the BMC reboots after creating the 318 * directory. 319 */ 320 if (!fsIface->directoryExists(std::filesystem::path(mountPoint))) 321 { 322 bool success = 323 fsIface->createDirectory(std::filesystem::path(mountPoint)); 324 if (!success) 325 { 326 lg2::error("Failed to create mount point: {DIR}", "DIR", mountPoint, 327 "REDFISH_MESSAGE_ID", 328 std::string("OpenBMC.0.1.MountFilesystemFail")); 329 throw InternalFailure(); 330 } 331 } 332 333 /* Run the command to mount the filesystem. */ 334 std::string luksContainer("/dev/mapper/" + containerName); 335 int retval = fsIface->doMount(luksContainer.c_str(), mountPoint.c_str(), 336 "ext4", 0, nullptr); 337 if (retval != 0) 338 { 339 lg2::error("Failed to mount filesystem: {RETVAL}", "RETVAL", retval, 340 "REDFISH_MESSAGE_ID", 341 std::string("OpenBMC.0.1.MountFilesystemFail")); 342 bool removeSuccess = 343 fsIface->removeDirectory(std::filesystem::path(mountPoint)); 344 if (!removeSuccess) 345 { 346 lg2::error("Failed to remove mount point: {DIR}", "DIR", mountPoint, 347 "REDFISH_MESSAGE_ID", 348 std::string("OpenBMC.0.1.MountFilesystemFail")); 349 } 350 throw InternalFailure(); 351 } 352 353 lg2::info("Successfully mounted filesystem at {DIR}", "DIR", mountPoint, 354 "REDFISH_MESSAGE_ID", 355 std::string("OpenBMC.0.1.MountFilesystemSuccess")); 356 } 357 358 void EStoraged::unmountFilesystem() 359 { 360 int retval = fsIface->doUnmount(mountPoint.c_str()); 361 if (retval != 0) 362 { 363 lg2::error("Failed to unmount filesystem: {RETVAL}", "RETVAL", retval, 364 "REDFISH_MESSAGE_ID", 365 std::string("OpenBMC.0.1.UnmountFilesystemFail")); 366 throw InternalFailure(); 367 } 368 369 /* Remove the mount point. */ 370 bool success = fsIface->removeDirectory(std::filesystem::path(mountPoint)); 371 if (!success) 372 { 373 lg2::error("Failed to remove mount point {DIR}", "DIR", mountPoint, 374 "REDFISH_MESSAGE_ID", 375 std::string("OpenBMC.0.1.UnmountFilesystemFail")); 376 throw InternalFailure(); 377 } 378 379 lg2::info("Successfully unmounted filesystem at {DIR}", "DIR", mountPoint, 380 "REDFISH_MESSAGE_ID", 381 std::string("OpenBMC.0.1.MountFilesystemSuccess")); 382 } 383 384 void EStoraged::deactivateLuksDev() 385 { 386 lg2::info("Deactivating LUKS device {DEV}", "DEV", devPath, 387 "REDFISH_MESSAGE_ID", 388 std::string("OpenBMC.0.1.DeactivateLuksDev")); 389 390 int retval = cryptIface->cryptDeactivate(nullptr, containerName.c_str()); 391 if (retval < 0) 392 { 393 lg2::error("Failed to deactivate crypt device: {RETVAL}", "RETVAL", 394 retval, "REDFISH_MESSAGE_ID", 395 std::string("OpenBMC.0.1.DeactivateLuksDevFail")); 396 throw InternalFailure(); 397 } 398 399 /* Device is now locked. */ 400 locked(true); 401 402 lg2::info("Successfully deactivated LUKS device {DEV}", "DEV", devPath, 403 "REDFISH_MESSAGE_ID", 404 std::string("OpenBMC.0.1.DeactivateLuksDevSuccess")); 405 } 406 407 void EStoraged::locked(bool isLocked) 408 { 409 lockedProperty = isLocked; 410 } 411 412 } // namespace estoraged 413