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