1 #pragma once 2 3 #include <app.hpp> 4 #include <boost/convert.hpp> 5 #include <boost/convert/strtol.hpp> 6 #include <boost/system/linux_error.hpp> 7 #include <dbus_utility.hpp> 8 #include <query.hpp> 9 #include <registries/privilege_registry.hpp> 10 11 namespace redfish 12 { 13 namespace certs 14 { 15 constexpr char const* httpsObjectPath = 16 "/xyz/openbmc_project/certs/server/https"; 17 constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install"; 18 constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace"; 19 constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete"; 20 constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate"; 21 constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties"; 22 constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager"; 23 constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap"; 24 constexpr char const* httpsServiceName = 25 "xyz.openbmc_project.Certs.Manager.Server.Https"; 26 constexpr char const* ldapServiceName = 27 "xyz.openbmc_project.Certs.Manager.Client.Ldap"; 28 constexpr char const* authorityServiceName = 29 "xyz.openbmc_project.Certs.Manager.Authority.Ldap"; 30 constexpr char const* authorityObjectPath = 31 "/xyz/openbmc_project/certs/authority/ldap"; 32 } // namespace certs 33 34 /** 35 * The Certificate schema defines a Certificate Service which represents the 36 * actions available to manage certificates and links to where certificates 37 * are installed. 38 */ 39 40 // TODO: Issue#61 No entries are available for Certificate 41 // service at https://www.dmtf.org/standards/redfish 42 // "redfish standard registries". Need to modify after DMTF 43 // publish Privilege details for certificate service 44 45 inline void requestRoutesCertificateService(App& app) 46 { 47 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/") 48 .privileges(redfish::privileges::getCertificateService) 49 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req, 50 const std::shared_ptr< 51 bmcweb::AsyncResp>& 52 asyncResp) { 53 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 54 { 55 return; 56 } 57 58 asyncResp->res.jsonValue["@odata.type"] = 59 "#CertificateService.v1_0_0.CertificateService"; 60 asyncResp->res.jsonValue["@odata.id"] = 61 "/redfish/v1/CertificateService"; 62 asyncResp->res.jsonValue["Id"] = "CertificateService"; 63 asyncResp->res.jsonValue["Name"] = "Certificate Service"; 64 asyncResp->res.jsonValue["Description"] = 65 "Actions available to manage certificates"; 66 // /redfish/v1/CertificateService/CertificateLocations is something 67 // only ConfigureManager can access then only display when the user 68 // has permissions ConfigureManager 69 Privileges effectiveUserPrivileges = 70 redfish::getUserPrivileges(req.userRole); 71 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}}, 72 effectiveUserPrivileges)) 73 { 74 asyncResp->res.jsonValue["CertificateLocations"]["@odata.id"] = 75 "/redfish/v1/CertificateService/CertificateLocations"; 76 } 77 asyncResp->res 78 .jsonValue["Actions"] 79 ["#CertificateService.ReplaceCertificate"] = { 80 {"target", 81 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"}, 82 {"CertificateType@Redfish.AllowableValues", {"PEM"}}}; 83 asyncResp->res 84 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = { 85 {"target", 86 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR"}}; 87 }); 88 } // requestRoutesCertificateService 89 90 /** 91 * @brief Find the ID specified in the URL 92 * Finds the numbers specified after the last "/" in the URL and returns. 93 * @param[in] path URL 94 * @return -1 on failure and number on success 95 */ 96 inline long getIDFromURL(const std::string_view url) 97 { 98 std::size_t found = url.rfind('/'); 99 if (found == std::string::npos) 100 { 101 return -1; 102 } 103 104 if ((found + 1) < url.length()) 105 { 106 std::string_view str = url.substr(found + 1); 107 108 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1); 109 } 110 111 return -1; 112 } 113 114 inline std::string getCertificateFromReqBody( 115 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 116 const crow::Request& req) 117 { 118 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false); 119 120 if (reqJson.is_discarded()) 121 { 122 // We did not receive JSON request, proceed as it is RAW data 123 return req.body; 124 } 125 126 std::string certificate; 127 std::optional<std::string> certificateType = "PEM"; 128 129 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString", 130 certificate, "CertificateType", 131 certificateType)) 132 { 133 BMCWEB_LOG_ERROR << "Required parameters are missing"; 134 messages::internalError(asyncResp->res); 135 return {}; 136 } 137 138 if (*certificateType != "PEM") 139 { 140 messages::propertyValueNotInList(asyncResp->res, *certificateType, 141 "CertificateType"); 142 return {}; 143 } 144 145 return certificate; 146 } 147 148 /** 149 * Class to create a temporary certificate file for uploading to system 150 */ 151 class CertificateFile 152 { 153 public: 154 CertificateFile() = delete; 155 CertificateFile(const CertificateFile&) = delete; 156 CertificateFile& operator=(const CertificateFile&) = delete; 157 CertificateFile(CertificateFile&&) = delete; 158 CertificateFile& operator=(CertificateFile&&) = delete; 159 CertificateFile(const std::string& certString) 160 { 161 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C', 162 'e', 'r', 't', 's', '.', 'X', 163 'X', 'X', 'X', 'X', 'X', '\0'}; 164 char* tempDirectory = mkdtemp(dirTemplate.data()); 165 if (tempDirectory != nullptr) 166 { 167 certDirectory = tempDirectory; 168 certificateFile = certDirectory / "cert.pem"; 169 std::ofstream out(certificateFile, std::ofstream::out | 170 std::ofstream::binary | 171 std::ofstream::trunc); 172 out << certString; 173 out.close(); 174 BMCWEB_LOG_DEBUG << "Creating certificate file" 175 << certificateFile.string(); 176 } 177 } 178 ~CertificateFile() 179 { 180 if (std::filesystem::exists(certDirectory)) 181 { 182 BMCWEB_LOG_DEBUG << "Removing certificate file" 183 << certificateFile.string(); 184 std::error_code ec; 185 std::filesystem::remove_all(certDirectory, ec); 186 if (ec) 187 { 188 BMCWEB_LOG_ERROR << "Failed to remove temp directory" 189 << certDirectory.string(); 190 } 191 } 192 } 193 std::string getCertFilePath() 194 { 195 return certificateFile; 196 } 197 198 private: 199 std::filesystem::path certificateFile; 200 std::filesystem::path certDirectory; 201 }; 202 203 static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher; 204 /** 205 * @brief Read data from CSR D-bus object and set to response 206 * 207 * @param[in] asyncResp Shared pointer to the response message 208 * @param[in] certURI Link to certifiate collection URI 209 * @param[in] service D-Bus service name 210 * @param[in] certObjPath certificate D-Bus object path 211 * @param[in] csrObjPath CSR D-Bus object path 212 * @return None 213 */ 214 static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 215 const std::string& certURI, const std::string& service, 216 const std::string& certObjPath, 217 const std::string& csrObjPath) 218 { 219 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath 220 << " CSRObjectPath=" << csrObjPath 221 << " service=" << service; 222 crow::connections::systemBus->async_method_call( 223 [asyncResp, certURI](const boost::system::error_code ec, 224 const std::string& csr) { 225 if (ec) 226 { 227 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 228 messages::internalError(asyncResp->res); 229 return; 230 } 231 if (csr.empty()) 232 { 233 BMCWEB_LOG_ERROR << "CSR read is empty"; 234 messages::internalError(asyncResp->res); 235 return; 236 } 237 asyncResp->res.jsonValue["CSRString"] = csr; 238 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] = 239 certURI; 240 }, 241 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR"); 242 } 243 244 /** 245 * Action to Generate CSR 246 */ 247 inline void requestRoutesCertificateActionGenerateCSR(App& app) 248 { 249 BMCWEB_ROUTE( 250 app, 251 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/") 252 .privileges(redfish::privileges::postCertificateService) 253 .methods( 254 boost::beast::http::verb:: 255 post)([&app]( 256 const crow::Request& req, 257 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 258 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 259 { 260 return; 261 } 262 static const int rsaKeyBitLength = 2048; 263 264 // Required parameters 265 std::string city; 266 std::string commonName; 267 std::string country; 268 std::string organization; 269 std::string organizationalUnit; 270 std::string state; 271 nlohmann::json certificateCollection; 272 273 // Optional parameters 274 std::optional<std::vector<std::string>> optAlternativeNames = 275 std::vector<std::string>(); 276 std::optional<std::string> optContactPerson = ""; 277 std::optional<std::string> optChallengePassword = ""; 278 std::optional<std::string> optEmail = ""; 279 std::optional<std::string> optGivenName = ""; 280 std::optional<std::string> optInitials = ""; 281 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength; 282 std::optional<std::string> optKeyCurveId = "secp384r1"; 283 std::optional<std::string> optKeyPairAlgorithm = "EC"; 284 std::optional<std::vector<std::string>> optKeyUsage = 285 std::vector<std::string>(); 286 std::optional<std::string> optSurname = ""; 287 std::optional<std::string> optUnstructuredName = ""; 288 if (!json_util::readJsonAction( 289 req, asyncResp->res, "City", city, "CommonName", commonName, 290 "ContactPerson", optContactPerson, "Country", country, 291 "Organization", organization, "OrganizationalUnit", 292 organizationalUnit, "State", state, "CertificateCollection", 293 certificateCollection, "AlternativeNames", 294 optAlternativeNames, "ChallengePassword", 295 optChallengePassword, "Email", optEmail, "GivenName", 296 optGivenName, "Initials", optInitials, "KeyBitLength", 297 optKeyBitLength, "KeyCurveId", optKeyCurveId, 298 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage", 299 optKeyUsage, "Surname", optSurname, "UnstructuredName", 300 optUnstructuredName)) 301 { 302 return; 303 } 304 305 // bmcweb has no way to store or decode a private key challenge 306 // password, which will likely cause bmcweb to crash on startup 307 // if this is not set on a post so not allowing the user to set 308 // value 309 if (!optChallengePassword->empty()) 310 { 311 messages::actionParameterNotSupported( 312 asyncResp->res, "GenerateCSR", "ChallengePassword"); 313 return; 314 } 315 316 std::string certURI; 317 if (!redfish::json_util::readJson(certificateCollection, 318 asyncResp->res, "@odata.id", 319 certURI)) 320 { 321 return; 322 } 323 324 std::string objectPath; 325 std::string service; 326 if (boost::starts_with( 327 certURI, 328 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates")) 329 { 330 objectPath = certs::httpsObjectPath; 331 service = certs::httpsServiceName; 332 } 333 else if (boost::starts_with( 334 certURI, 335 "/redfish/v1/AccountService/LDAP/Certificates")) 336 { 337 objectPath = certs::ldapObjectPath; 338 service = certs::ldapServiceName; 339 } 340 else 341 { 342 messages::actionParameterNotSupported( 343 asyncResp->res, "CertificateCollection", "GenerateCSR"); 344 return; 345 } 346 347 // supporting only EC and RSA algorithm 348 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA") 349 { 350 messages::actionParameterNotSupported( 351 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR"); 352 return; 353 } 354 355 // supporting only 2048 key bit length for RSA algorithm due to 356 // time consumed in generating private key 357 if (*optKeyPairAlgorithm == "RSA" && 358 *optKeyBitLength != rsaKeyBitLength) 359 { 360 messages::propertyValueNotInList( 361 asyncResp->res, std::to_string(*optKeyBitLength), 362 "KeyBitLength"); 363 return; 364 } 365 366 // validate KeyUsage supporting only 1 type based on URL 367 if (boost::starts_with( 368 certURI, 369 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates")) 370 { 371 if (optKeyUsage->empty()) 372 { 373 optKeyUsage->push_back("ServerAuthentication"); 374 } 375 else if (optKeyUsage->size() == 1) 376 { 377 if ((*optKeyUsage)[0] != "ServerAuthentication") 378 { 379 messages::propertyValueNotInList( 380 asyncResp->res, (*optKeyUsage)[0], "KeyUsage"); 381 return; 382 } 383 } 384 else 385 { 386 messages::actionParameterNotSupported( 387 asyncResp->res, "KeyUsage", "GenerateCSR"); 388 return; 389 } 390 } 391 else if (boost::starts_with( 392 certURI, 393 "/redfish/v1/AccountService/LDAP/Certificates")) 394 { 395 if (optKeyUsage->empty()) 396 { 397 optKeyUsage->push_back("ClientAuthentication"); 398 } 399 else if (optKeyUsage->size() == 1) 400 { 401 if ((*optKeyUsage)[0] != "ClientAuthentication") 402 { 403 messages::propertyValueNotInList( 404 asyncResp->res, (*optKeyUsage)[0], "KeyUsage"); 405 return; 406 } 407 } 408 else 409 { 410 messages::actionParameterNotSupported( 411 asyncResp->res, "KeyUsage", "GenerateCSR"); 412 return; 413 } 414 } 415 416 // Only allow one CSR matcher at a time so setting retry 417 // time-out and timer expiry to 10 seconds for now. 418 static const int timeOut = 10; 419 if (csrMatcher) 420 { 421 messages::serviceTemporarilyUnavailable( 422 asyncResp->res, std::to_string(timeOut)); 423 return; 424 } 425 426 // Make this static so it survives outside this method 427 static boost::asio::steady_timer timeout(*req.ioService); 428 timeout.expires_after(std::chrono::seconds(timeOut)); 429 timeout.async_wait( 430 [asyncResp](const boost::system::error_code& ec) { 431 csrMatcher = nullptr; 432 if (ec) 433 { 434 // operation_aborted is expected if timer is canceled 435 // before completion. 436 if (ec != boost::asio::error::operation_aborted) 437 { 438 BMCWEB_LOG_ERROR << "Async_wait failed " << ec; 439 } 440 return; 441 } 442 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR"; 443 messages::internalError(asyncResp->res); 444 }); 445 446 // create a matcher to wait on CSR object 447 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath; 448 std::string match("type='signal'," 449 "interface='org.freedesktop.DBus.ObjectManager'," 450 "path='" + 451 objectPath + 452 "'," 453 "member='InterfacesAdded'"); 454 csrMatcher = std::make_unique<sdbusplus::bus::match::match>( 455 *crow::connections::systemBus, match, 456 [asyncResp, service, objectPath, 457 certURI](sdbusplus::message::message& m) { 458 timeout.cancel(); 459 if (m.is_method_error()) 460 { 461 BMCWEB_LOG_ERROR << "Dbus method error!!!"; 462 messages::internalError(asyncResp->res); 463 return; 464 } 465 466 dbus::utility::DBusInteracesMap interfacesProperties; 467 468 sdbusplus::message::object_path csrObjectPath; 469 m.read(csrObjectPath, interfacesProperties); 470 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str; 471 for (auto& interface : interfacesProperties) 472 { 473 if (interface.first == "xyz.openbmc_project.Certs.CSR") 474 { 475 getCSR(asyncResp, certURI, service, objectPath, 476 csrObjectPath.str); 477 break; 478 } 479 } 480 }); 481 crow::connections::systemBus->async_method_call( 482 [asyncResp](const boost::system::error_code ec, 483 const std::string&) { 484 if (ec) 485 { 486 BMCWEB_LOG_ERROR << "DBUS response error: " 487 << ec.message(); 488 messages::internalError(asyncResp->res); 489 return; 490 } 491 }, 492 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create", 493 "GenerateCSR", *optAlternativeNames, *optChallengePassword, 494 city, commonName, *optContactPerson, country, *optEmail, 495 *optGivenName, *optInitials, *optKeyBitLength, *optKeyCurveId, 496 *optKeyPairAlgorithm, *optKeyUsage, organization, 497 organizationalUnit, state, *optSurname, *optUnstructuredName); 498 }); 499 } // requestRoutesCertificateActionGenerateCSR 500 501 /** 502 * @brief Parse and update Certificate Issue/Subject property 503 * 504 * @param[in] asyncResp Shared pointer to the response message 505 * @param[in] str Issuer/Subject value in key=value pairs 506 * @param[in] type Issuer/Subject 507 * @return None 508 */ 509 static void updateCertIssuerOrSubject(nlohmann::json& out, 510 const std::string_view value) 511 { 512 // example: O=openbmc-project.xyz,CN=localhost 513 std::string_view::iterator i = value.begin(); 514 while (i != value.end()) 515 { 516 std::string_view::iterator tokenBegin = i; 517 while (i != value.end() && *i != '=') 518 { 519 ++i; 520 } 521 if (i == value.end()) 522 { 523 break; 524 } 525 const std::string_view key(tokenBegin, 526 static_cast<size_t>(i - tokenBegin)); 527 ++i; 528 tokenBegin = i; 529 while (i != value.end() && *i != ',') 530 { 531 ++i; 532 } 533 const std::string_view val(tokenBegin, 534 static_cast<size_t>(i - tokenBegin)); 535 if (key == "L") 536 { 537 out["City"] = val; 538 } 539 else if (key == "CN") 540 { 541 out["CommonName"] = val; 542 } 543 else if (key == "C") 544 { 545 out["Country"] = val; 546 } 547 else if (key == "O") 548 { 549 out["Organization"] = val; 550 } 551 else if (key == "OU") 552 { 553 out["OrganizationalUnit"] = val; 554 } 555 else if (key == "ST") 556 { 557 out["State"] = val; 558 } 559 // skip comma character 560 if (i != value.end()) 561 { 562 ++i; 563 } 564 } 565 } 566 567 /** 568 * @brief Retrieve the certificates properties and append to the response 569 * message 570 * 571 * @param[in] asyncResp Shared pointer to the response message 572 * @param[in] objectPath Path of the D-Bus service object 573 * @param[in] certId Id of the certificate 574 * @param[in] certURL URL of the certificate object 575 * @param[in] name name of the certificate 576 * @return None 577 */ 578 static void getCertificateProperties( 579 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 580 const std::string& objectPath, const std::string& service, long certId, 581 const std::string& certURL, const std::string& name) 582 { 583 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath 584 << " certId=" << certId << " certURl=" << certURL; 585 crow::connections::systemBus->async_method_call( 586 [asyncResp, certURL, certId, 587 name](const boost::system::error_code ec, 588 const dbus::utility::DBusPropertiesMap& properties) { 589 if (ec) 590 { 591 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 592 messages::resourceNotFound(asyncResp->res, name, 593 std::to_string(certId)); 594 return; 595 } 596 asyncResp->res.jsonValue["@odata.id"] = certURL; 597 asyncResp->res.jsonValue["@odata.type"] = 598 "#Certificate.v1_0_0.Certificate"; 599 asyncResp->res.jsonValue["Id"] = std::to_string(certId); 600 asyncResp->res.jsonValue["Name"] = name; 601 asyncResp->res.jsonValue["Description"] = name; 602 for (const auto& property : properties) 603 { 604 if (property.first == "CertificateString") 605 { 606 asyncResp->res.jsonValue["CertificateString"] = ""; 607 const std::string* value = 608 std::get_if<std::string>(&property.second); 609 if (value != nullptr) 610 { 611 asyncResp->res.jsonValue["CertificateString"] = *value; 612 } 613 } 614 else if (property.first == "KeyUsage") 615 { 616 nlohmann::json& keyUsage = 617 asyncResp->res.jsonValue["KeyUsage"]; 618 keyUsage = nlohmann::json::array(); 619 const std::vector<std::string>* value = 620 std::get_if<std::vector<std::string>>(&property.second); 621 if (value != nullptr) 622 { 623 for (const std::string& usage : *value) 624 { 625 keyUsage.push_back(usage); 626 } 627 } 628 } 629 else if (property.first == "Issuer") 630 { 631 const std::string* value = 632 std::get_if<std::string>(&property.second); 633 if (value != nullptr) 634 { 635 updateCertIssuerOrSubject( 636 asyncResp->res.jsonValue["Issuer"], *value); 637 } 638 } 639 else if (property.first == "Subject") 640 { 641 const std::string* value = 642 std::get_if<std::string>(&property.second); 643 if (value != nullptr) 644 { 645 updateCertIssuerOrSubject( 646 asyncResp->res.jsonValue["Subject"], *value); 647 } 648 } 649 else if (property.first == "ValidNotAfter") 650 { 651 const uint64_t* value = 652 std::get_if<uint64_t>(&property.second); 653 if (value != nullptr) 654 { 655 asyncResp->res.jsonValue["ValidNotAfter"] = 656 crow::utility::getDateTimeUint(*value); 657 } 658 } 659 else if (property.first == "ValidNotBefore") 660 { 661 const uint64_t* value = 662 std::get_if<uint64_t>(&property.second); 663 if (value != nullptr) 664 { 665 asyncResp->res.jsonValue["ValidNotBefore"] = 666 crow::utility::getDateTimeUint(*value); 667 } 668 } 669 } 670 asyncResp->res.addHeader("Location", certURL); 671 }, 672 service, objectPath, certs::dbusPropIntf, "GetAll", 673 certs::certPropIntf); 674 } 675 676 /** 677 * Action to replace an existing certificate 678 */ 679 inline void requestRoutesCertificateActionsReplaceCertificate(App& app) 680 { 681 BMCWEB_ROUTE( 682 app, 683 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/") 684 .privileges(redfish::privileges::postCertificateService) 685 .methods( 686 boost::beast::http::verb:: 687 post)([&app]( 688 const crow::Request& req, 689 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 690 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 691 { 692 return; 693 } 694 std::string certificate; 695 nlohmann::json certificateUri; 696 std::optional<std::string> certificateType = "PEM"; 697 698 if (!json_util::readJsonAction(req, asyncResp->res, 699 "CertificateString", certificate, 700 "CertificateUri", certificateUri, 701 "CertificateType", certificateType)) 702 { 703 BMCWEB_LOG_ERROR << "Required parameters are missing"; 704 messages::internalError(asyncResp->res); 705 return; 706 } 707 708 if (!certificateType) 709 { 710 // should never happen, but it never hurts to be paranoid. 711 return; 712 } 713 if (certificateType != "PEM") 714 { 715 messages::actionParameterNotSupported( 716 asyncResp->res, "CertificateType", "ReplaceCertificate"); 717 return; 718 } 719 720 std::string certURI; 721 if (!redfish::json_util::readJson(certificateUri, asyncResp->res, 722 "@odata.id", certURI)) 723 { 724 messages::actionParameterMissing( 725 asyncResp->res, "ReplaceCertificate", "CertificateUri"); 726 return; 727 } 728 729 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI; 730 long id = getIDFromURL(certURI); 731 if (id < 0) 732 { 733 messages::actionParameterValueFormatError( 734 asyncResp->res, certURI, "CertificateUri", 735 "ReplaceCertificate"); 736 return; 737 } 738 std::string objectPath; 739 std::string name; 740 std::string service; 741 if (boost::starts_with( 742 certURI, 743 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")) 744 { 745 objectPath = std::string(certs::httpsObjectPath) + "/" + 746 std::to_string(id); 747 name = "HTTPS certificate"; 748 service = certs::httpsServiceName; 749 } 750 else if (boost::starts_with( 751 certURI, 752 "/redfish/v1/AccountService/LDAP/Certificates/")) 753 { 754 objectPath = std::string(certs::ldapObjectPath) + "/" + 755 std::to_string(id); 756 name = "LDAP certificate"; 757 service = certs::ldapServiceName; 758 } 759 else if (boost::starts_with( 760 certURI, 761 "/redfish/v1/Managers/bmc/Truststore/Certificates/")) 762 { 763 objectPath = std::string(certs::authorityObjectPath) + "/" + 764 std::to_string(id); 765 name = "TrustStore certificate"; 766 service = certs::authorityServiceName; 767 } 768 else 769 { 770 messages::actionParameterNotSupported( 771 asyncResp->res, "CertificateUri", "ReplaceCertificate"); 772 return; 773 } 774 775 std::shared_ptr<CertificateFile> certFile = 776 std::make_shared<CertificateFile>(certificate); 777 crow::connections::systemBus->async_method_call( 778 [asyncResp, certFile, objectPath, service, certURI, id, 779 name](const boost::system::error_code ec) { 780 if (ec) 781 { 782 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 783 if (ec.value() == 784 boost::system::linux_error::bad_request_descriptor) 785 { 786 messages::resourceNotFound(asyncResp->res, name, 787 std::to_string(id)); 788 return; 789 } 790 messages::internalError(asyncResp->res); 791 return; 792 } 793 getCertificateProperties(asyncResp, objectPath, service, id, 794 certURI, name); 795 BMCWEB_LOG_DEBUG << "HTTPS certificate install file=" 796 << certFile->getCertFilePath(); 797 }, 798 service, objectPath, certs::certReplaceIntf, "Replace", 799 certFile->getCertFilePath()); 800 }); 801 } // requestRoutesCertificateActionsReplaceCertificate 802 803 /** 804 * Certificate resource describes a certificate used to prove the identity 805 * of a component, account or service. 806 */ 807 808 inline void requestRoutesHTTPSCertificate(App& app) 809 { 810 BMCWEB_ROUTE( 811 app, 812 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/") 813 .privileges(redfish::privileges::getCertificate) 814 .methods( 815 boost::beast::http::verb:: 816 get)([&app](const crow::Request& req, 817 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 818 const std::string& param) -> void { 819 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 820 { 821 return; 822 } 823 if (param.empty()) 824 { 825 messages::internalError(asyncResp->res); 826 return; 827 } 828 long id = getIDFromURL(req.url); 829 830 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" 831 << std::to_string(id); 832 std::string certURL = 833 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" + 834 std::to_string(id); 835 std::string objectPath = certs::httpsObjectPath; 836 objectPath += "/"; 837 objectPath += std::to_string(id); 838 getCertificateProperties(asyncResp, objectPath, 839 certs::httpsServiceName, id, certURL, 840 "HTTPS Certificate"); 841 }); 842 } 843 844 /** 845 * Collection of HTTPS certificates 846 */ 847 inline void requestRoutesHTTPSCertificateCollection(App& app) 848 { 849 BMCWEB_ROUTE(app, 850 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/") 851 .privileges(redfish::privileges::getCertificateCollection) 852 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req, 853 const std::shared_ptr< 854 bmcweb::AsyncResp>& 855 asyncResp) { 856 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 857 { 858 return; 859 } 860 861 asyncResp->res.jsonValue["@odata.id"] = 862 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"; 863 asyncResp->res.jsonValue["@odata.type"] = 864 "#CertificateCollection.CertificateCollection"; 865 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection"; 866 asyncResp->res.jsonValue["Description"] = 867 "A Collection of HTTPS certificate instances"; 868 869 crow::connections::systemBus->async_method_call( 870 [asyncResp](const boost::system::error_code ec, 871 const dbus::utility::ManagedObjectType& certs) { 872 if (ec) 873 { 874 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 875 messages::internalError(asyncResp->res); 876 return; 877 } 878 nlohmann::json& members = 879 asyncResp->res.jsonValue["Members"]; 880 members = nlohmann::json::array(); 881 for (const auto& cert : certs) 882 { 883 long id = getIDFromURL(cert.first.str); 884 if (id >= 0) 885 { 886 nlohmann::json::object_t member; 887 member["@odata.id"] = 888 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" + 889 std::to_string(id); 890 members.push_back(std::move(member)); 891 } 892 } 893 asyncResp->res.jsonValue["Members@odata.count"] = 894 members.size(); 895 }, 896 certs::httpsServiceName, certs::httpsObjectPath, 897 certs::dbusObjManagerIntf, "GetManagedObjects"); 898 }); 899 900 BMCWEB_ROUTE(app, 901 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/") 902 .privileges(redfish::privileges::postCertificateCollection) 903 .methods( 904 boost::beast::http::verb:: 905 post)([&app]( 906 const crow::Request& req, 907 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 908 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 909 { 910 return; 911 } 912 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost"; 913 914 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate"; 915 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate"; 916 917 std::string certFileBody = 918 getCertificateFromReqBody(asyncResp, req); 919 920 if (certFileBody.empty()) 921 { 922 BMCWEB_LOG_ERROR << "Cannot get certificate from request body."; 923 messages::unrecognizedRequestBody(asyncResp->res); 924 return; 925 } 926 927 std::shared_ptr<CertificateFile> certFile = 928 std::make_shared<CertificateFile>(certFileBody); 929 930 crow::connections::systemBus->async_method_call( 931 [asyncResp, certFile](const boost::system::error_code ec, 932 const std::string& objectPath) { 933 if (ec) 934 { 935 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 936 messages::internalError(asyncResp->res); 937 return; 938 } 939 long certId = getIDFromURL(objectPath); 940 if (certId < 0) 941 { 942 BMCWEB_LOG_ERROR << "Invalid objectPath value" 943 << objectPath; 944 messages::internalError(asyncResp->res); 945 return; 946 } 947 std::string certURL = 948 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" + 949 std::to_string(certId); 950 getCertificateProperties(asyncResp, objectPath, 951 certs::httpsServiceName, certId, 952 certURL, "HTTPS Certificate"); 953 BMCWEB_LOG_DEBUG << "HTTPS certificate install file=" 954 << certFile->getCertFilePath(); 955 }, 956 certs::httpsServiceName, certs::httpsObjectPath, 957 certs::certInstallIntf, "Install", certFile->getCertFilePath()); 958 }); 959 } // requestRoutesHTTPSCertificateCollection 960 961 /** 962 * @brief Retrieve the certificates installed list and append to the 963 * response 964 * 965 * @param[in] asyncResp Shared pointer to the response message 966 * @param[in] certURL Path of the certificate object 967 * @param[in] path Path of the D-Bus service object 968 * @return None 969 */ 970 inline void 971 getCertificateLocations(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 972 const std::string& certURL, const std::string& path, 973 const std::string& service) 974 { 975 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL 976 << " Path=" << path << " service= " << service; 977 crow::connections::systemBus->async_method_call( 978 [asyncResp, certURL](const boost::system::error_code ec, 979 const dbus::utility::ManagedObjectType& certs) { 980 if (ec) 981 { 982 BMCWEB_LOG_WARNING 983 << "Certificate collection query failed: " << ec 984 << ", skipping " << certURL; 985 return; 986 } 987 nlohmann::json& links = 988 asyncResp->res.jsonValue["Links"]["Certificates"]; 989 for (const auto& cert : certs) 990 { 991 long id = getIDFromURL(cert.first.str); 992 if (id >= 0) 993 { 994 nlohmann::json::object_t link; 995 link["@odata.id"] = certURL + std::to_string(id); 996 links.push_back(std::move(link)); 997 } 998 } 999 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] = 1000 links.size(); 1001 }, 1002 service, path, certs::dbusObjManagerIntf, "GetManagedObjects"); 1003 } 1004 1005 /** 1006 * The certificate location schema defines a resource that an administrator 1007 * can use in order to locate all certificates installed on a given service. 1008 */ 1009 inline void requestRoutesCertificateLocations(App& app) 1010 { 1011 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/") 1012 .privileges(redfish::privileges::getCertificateLocations) 1013 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req, 1014 const std::shared_ptr< 1015 bmcweb::AsyncResp>& 1016 asyncResp) { 1017 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1018 { 1019 return; 1020 } 1021 asyncResp->res.jsonValue["@odata.id"] = 1022 "/redfish/v1/CertificateService/CertificateLocations"; 1023 asyncResp->res.jsonValue["@odata.type"] = 1024 "#CertificateLocations.v1_0_0.CertificateLocations"; 1025 asyncResp->res.jsonValue["Name"] = "Certificate Locations"; 1026 asyncResp->res.jsonValue["Id"] = "CertificateLocations"; 1027 asyncResp->res.jsonValue["Description"] = 1028 "Defines a resource that an administrator can use in order to " 1029 "locate all certificates installed on a given service"; 1030 1031 nlohmann::json& links = 1032 asyncResp->res.jsonValue["Links"]["Certificates"]; 1033 links = nlohmann::json::array(); 1034 getCertificateLocations( 1035 asyncResp, 1036 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/", 1037 certs::httpsObjectPath, certs::httpsServiceName); 1038 getCertificateLocations( 1039 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/", 1040 certs::ldapObjectPath, certs::ldapServiceName); 1041 getCertificateLocations( 1042 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/", 1043 certs::authorityObjectPath, certs::authorityServiceName); 1044 }); 1045 } 1046 // requestRoutesCertificateLocations 1047 1048 /** 1049 * Collection of LDAP certificates 1050 */ 1051 inline void requestRoutesLDAPCertificateCollection(App& app) 1052 { 1053 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/") 1054 .privileges(redfish::privileges::getCertificateCollection) 1055 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req, 1056 const std::shared_ptr< 1057 bmcweb::AsyncResp>& 1058 asyncResp) { 1059 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1060 { 1061 return; 1062 } 1063 1064 asyncResp->res.jsonValue["@odata.id"] = 1065 "/redfish/v1/AccountService/LDAP/Certificates"; 1066 asyncResp->res.jsonValue["@odata.type"] = 1067 "#CertificateCollection.CertificateCollection"; 1068 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection"; 1069 asyncResp->res.jsonValue["Description"] = 1070 "A Collection of LDAP certificate instances"; 1071 1072 crow::connections::systemBus->async_method_call( 1073 [asyncResp](const boost::system::error_code ec, 1074 const dbus::utility::ManagedObjectType& certs) { 1075 nlohmann::json& members = 1076 asyncResp->res.jsonValue["Members"]; 1077 nlohmann::json& count = 1078 asyncResp->res.jsonValue["Members@odata.count"]; 1079 members = nlohmann::json::array(); 1080 count = 0; 1081 if (ec) 1082 { 1083 BMCWEB_LOG_WARNING << "LDAP certificate query failed: " 1084 << ec; 1085 return; 1086 } 1087 for (const auto& cert : certs) 1088 { 1089 long id = getIDFromURL(cert.first.str); 1090 if (id >= 0) 1091 { 1092 nlohmann::json::object_t member; 1093 member["@odata.id"] = 1094 "/redfish/v1/AccountService/LDAP/Certificates/" + 1095 std::to_string(id); 1096 members.push_back(std::move(member)); 1097 } 1098 } 1099 count = members.size(); 1100 }, 1101 certs::ldapServiceName, certs::ldapObjectPath, 1102 certs::dbusObjManagerIntf, "GetManagedObjects"); 1103 }); 1104 1105 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/") 1106 .privileges(redfish::privileges::postCertificateCollection) 1107 .methods(boost::beast::http::verb::post)( 1108 [&app](const crow::Request& req, 1109 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1110 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1111 { 1112 return; 1113 } 1114 std::string certFileBody = 1115 getCertificateFromReqBody(asyncResp, req); 1116 1117 if (certFileBody.empty()) 1118 { 1119 BMCWEB_LOG_ERROR 1120 << "Cannot get certificate from request body."; 1121 messages::unrecognizedRequestBody(asyncResp->res); 1122 return; 1123 } 1124 1125 std::shared_ptr<CertificateFile> certFile = 1126 std::make_shared<CertificateFile>(certFileBody); 1127 1128 crow::connections::systemBus->async_method_call( 1129 [asyncResp, certFile](const boost::system::error_code ec, 1130 const std::string& objectPath) { 1131 if (ec) 1132 { 1133 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 1134 messages::internalError(asyncResp->res); 1135 return; 1136 } 1137 long certId = getIDFromURL(objectPath); 1138 if (certId < 0) 1139 { 1140 BMCWEB_LOG_ERROR << "Invalid objectPath value" 1141 << objectPath; 1142 messages::internalError(asyncResp->res); 1143 return; 1144 } 1145 std::string certURL = 1146 "/redfish/v1/AccountService/LDAP/Certificates/" + 1147 std::to_string(certId); 1148 getCertificateProperties(asyncResp, objectPath, 1149 certs::ldapServiceName, certId, 1150 certURL, "LDAP Certificate"); 1151 BMCWEB_LOG_DEBUG << "LDAP certificate install file=" 1152 << certFile->getCertFilePath(); 1153 }, 1154 certs::ldapServiceName, certs::ldapObjectPath, 1155 certs::certInstallIntf, "Install", 1156 certFile->getCertFilePath()); 1157 }); 1158 } // requestRoutesLDAPCertificateCollection 1159 1160 /** 1161 * Certificate resource describes a certificate used to prove the identity 1162 * of a component, account or service. 1163 */ 1164 inline void requestRoutesLDAPCertificate(App& app) 1165 { 1166 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/") 1167 .privileges(redfish::privileges::getCertificate) 1168 .methods(boost::beast::http::verb::get)( 1169 [&app](const crow::Request& req, 1170 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1171 const std::string&) { 1172 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1173 { 1174 return; 1175 } 1176 long id = getIDFromURL(req.url); 1177 if (id < 0) 1178 { 1179 BMCWEB_LOG_ERROR << "Invalid url value" << req.url; 1180 messages::internalError(asyncResp->res); 1181 return; 1182 } 1183 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" 1184 << std::to_string(id); 1185 std::string certURL = 1186 "/redfish/v1/AccountService/LDAP/Certificates/" + 1187 std::to_string(id); 1188 std::string objectPath = certs::ldapObjectPath; 1189 objectPath += "/"; 1190 objectPath += std::to_string(id); 1191 getCertificateProperties(asyncResp, objectPath, 1192 certs::ldapServiceName, id, certURL, 1193 "LDAP Certificate"); 1194 }); 1195 } // requestRoutesLDAPCertificate 1196 /** 1197 * Collection of TrustStoreCertificate certificates 1198 */ 1199 inline void requestRoutesTrustStoreCertificateCollection(App& app) 1200 { 1201 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/") 1202 .privileges(redfish::privileges::getCertificate) 1203 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req, 1204 const std::shared_ptr< 1205 bmcweb::AsyncResp>& 1206 asyncResp) { 1207 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1208 { 1209 return; 1210 } 1211 1212 asyncResp->res.jsonValue["@odata.id"] = 1213 "/redfish/v1/Managers/bmc/Truststore/Certificates/"; 1214 asyncResp->res.jsonValue["@odata.type"] = 1215 "#CertificateCollection.CertificateCollection"; 1216 asyncResp->res.jsonValue["Name"] = 1217 "TrustStore Certificates Collection"; 1218 asyncResp->res.jsonValue["Description"] = 1219 "A Collection of TrustStore certificate instances"; 1220 1221 crow::connections::systemBus->async_method_call( 1222 [asyncResp](const boost::system::error_code ec, 1223 const dbus::utility::ManagedObjectType& certs) { 1224 if (ec) 1225 { 1226 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 1227 messages::internalError(asyncResp->res); 1228 return; 1229 } 1230 nlohmann::json& members = 1231 asyncResp->res.jsonValue["Members"]; 1232 members = nlohmann::json::array(); 1233 for (const auto& cert : certs) 1234 { 1235 long id = getIDFromURL(cert.first.str); 1236 if (id >= 0) 1237 { 1238 nlohmann::json::object_t member; 1239 member["@odata.id"] = 1240 "/redfish/v1/Managers/bmc/Truststore/Certificates/" + 1241 std::to_string(id); 1242 members.push_back(std::move(member)); 1243 } 1244 } 1245 asyncResp->res.jsonValue["Members@odata.count"] = 1246 members.size(); 1247 }, 1248 certs::authorityServiceName, certs::authorityObjectPath, 1249 certs::dbusObjManagerIntf, "GetManagedObjects"); 1250 }); 1251 1252 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/") 1253 .privileges(redfish::privileges::postCertificateCollection) 1254 .methods( 1255 boost::beast::http::verb:: 1256 post)([&app]( 1257 const crow::Request& req, 1258 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1259 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1260 { 1261 return; 1262 } 1263 std::string certFileBody = 1264 getCertificateFromReqBody(asyncResp, req); 1265 1266 if (certFileBody.empty()) 1267 { 1268 BMCWEB_LOG_ERROR << "Cannot get certificate from request body."; 1269 messages::unrecognizedRequestBody(asyncResp->res); 1270 return; 1271 } 1272 1273 std::shared_ptr<CertificateFile> certFile = 1274 std::make_shared<CertificateFile>(certFileBody); 1275 crow::connections::systemBus->async_method_call( 1276 [asyncResp, certFile](const boost::system::error_code ec, 1277 const std::string& objectPath) { 1278 if (ec) 1279 { 1280 BMCWEB_LOG_ERROR << "DBUS response error: " << ec; 1281 messages::internalError(asyncResp->res); 1282 return; 1283 } 1284 long certId = getIDFromURL(objectPath); 1285 if (certId < 0) 1286 { 1287 BMCWEB_LOG_ERROR << "Invalid objectPath value" 1288 << objectPath; 1289 messages::internalError(asyncResp->res); 1290 return; 1291 } 1292 std::string certURL = 1293 "/redfish/v1/Managers/bmc/Truststore/Certificates/" + 1294 std::to_string(certId); 1295 1296 getCertificateProperties( 1297 asyncResp, objectPath, certs::authorityServiceName, 1298 certId, certURL, "TrustStore Certificate"); 1299 BMCWEB_LOG_DEBUG << "TrustStore certificate install file=" 1300 << certFile->getCertFilePath(); 1301 }, 1302 certs::authorityServiceName, certs::authorityObjectPath, 1303 certs::certInstallIntf, "Install", certFile->getCertFilePath()); 1304 }); 1305 } // requestRoutesTrustStoreCertificateCollection 1306 1307 /** 1308 * Certificate resource describes a certificate used to prove the identity 1309 * of a component, account or service. 1310 */ 1311 inline void requestRoutesTrustStoreCertificate(App& app) 1312 { 1313 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/") 1314 .privileges(redfish::privileges::getCertificate) 1315 .methods(boost::beast::http::verb::get)( 1316 [&app](const crow::Request& req, 1317 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1318 const std::string&) { 1319 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1320 { 1321 return; 1322 } 1323 long id = getIDFromURL(req.url); 1324 if (id < 0) 1325 { 1326 BMCWEB_LOG_ERROR << "Invalid url value" << req.url; 1327 messages::internalError(asyncResp->res); 1328 return; 1329 } 1330 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID=" 1331 << std::to_string(id); 1332 std::string certURL = 1333 "/redfish/v1/Managers/bmc/Truststore/Certificates/" + 1334 std::to_string(id); 1335 std::string objectPath = certs::authorityObjectPath; 1336 objectPath += "/"; 1337 objectPath += std::to_string(id); 1338 getCertificateProperties(asyncResp, objectPath, 1339 certs::authorityServiceName, id, 1340 certURL, "TrustStore Certificate"); 1341 }); 1342 1343 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/") 1344 .privileges(redfish::privileges::deleteCertificate) 1345 .methods(boost::beast::http::verb::delete_)( 1346 [&app](const crow::Request& req, 1347 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1348 const std::string& param) { 1349 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res)) 1350 { 1351 return; 1352 } 1353 if (param.empty()) 1354 { 1355 messages::internalError(asyncResp->res); 1356 return; 1357 } 1358 1359 long id = getIDFromURL(req.url); 1360 if (id < 0) 1361 { 1362 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url; 1363 messages::resourceNotFound(asyncResp->res, 1364 "TrustStore Certificate", 1365 std::string(req.url)); 1366 return; 1367 } 1368 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID=" 1369 << std::to_string(id); 1370 std::string certPath = certs::authorityObjectPath; 1371 certPath += "/"; 1372 certPath += std::to_string(id); 1373 1374 crow::connections::systemBus->async_method_call( 1375 [asyncResp, id](const boost::system::error_code ec) { 1376 if (ec) 1377 { 1378 messages::resourceNotFound(asyncResp->res, 1379 "TrustStore Certificate", 1380 std::to_string(id)); 1381 return; 1382 } 1383 BMCWEB_LOG_INFO << "Certificate deleted"; 1384 asyncResp->res.result( 1385 boost::beast::http::status::no_content); 1386 }, 1387 certs::authorityServiceName, certPath, certs::objDeleteIntf, 1388 "Delete"); 1389 }); 1390 } // requestRoutesTrustStoreCertificate 1391 } // namespace redfish 1392