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