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