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