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