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 "io_context_singleton.hpp" 16 #include "logging.hpp" 17 #include "privileges.hpp" 18 #include "query.hpp" 19 #include "registries/privilege_registry.hpp" 20 #include "utility.hpp" 21 #include "utils/dbus_utils.hpp" 22 #include "utils/json_utils.hpp" 23 #include "utils/time_utils.hpp" 24 25 #include <systemd/sd-bus.h> 26 27 #include <boost/asio/error.hpp> 28 #include <boost/asio/steady_timer.hpp> 29 #include <boost/beast/http/field.hpp> 30 #include <boost/beast/http/status.hpp> 31 #include <boost/beast/http/verb.hpp> 32 #include <boost/system/result.hpp> 33 #include <boost/url/format.hpp> 34 #include <boost/url/parse.hpp> 35 #include <boost/url/url.hpp> 36 #include <nlohmann/json.hpp> 37 #include <sdbusplus/bus/match.hpp> 38 #include <sdbusplus/message.hpp> 39 #include <sdbusplus/message/native_types.hpp> 40 #include <sdbusplus/unpack_properties.hpp> 41 42 #include <array> 43 #include <chrono> 44 #include <cstddef> 45 #include <cstdint> 46 #include <cstdlib> 47 #include <filesystem> 48 #include <format> 49 #include <fstream> 50 #include <functional> 51 #include <iterator> 52 #include <memory> 53 #include <optional> 54 #include <string> 55 #include <string_view> 56 #include <system_error> 57 #include <utility> 58 #include <vector> 59 60 namespace redfish 61 { 62 namespace certs 63 { 64 constexpr const char* certInstallIntf = "xyz.openbmc_project.Certs.Install"; 65 constexpr const char* certReplaceIntf = "xyz.openbmc_project.Certs.Replace"; 66 constexpr const char* objDeleteIntf = "xyz.openbmc_project.Object.Delete"; 67 constexpr const char* certPropIntf = "xyz.openbmc_project.Certs.Certificate"; 68 constexpr const char* httpsServiceName = 69 "xyz.openbmc_project.Certs.Manager.Server.Https"; 70 constexpr const char* ldapServiceName = 71 "xyz.openbmc_project.Certs.Manager.Client.Ldap"; 72 constexpr const char* authorityServiceName = 73 "xyz.openbmc_project.Certs.Manager.Authority.Truststore"; 74 constexpr const char* baseObjectPath = "/xyz/openbmc_project/certs"; 75 constexpr const char* httpsObjectPath = 76 "/xyz/openbmc_project/certs/server/https"; 77 constexpr const char* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap"; 78 constexpr const char* authorityObjectPath = 79 "/xyz/openbmc_project/certs/authority/truststore"; 80 } // namespace certs 81 82 /** 83 * The Certificate schema defines a Certificate Service which represents the 84 * actions available to manage certificates and links to where certificates 85 * are installed. 86 */ 87 88 inline std::string getCertificateFromReqBody( 89 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 90 const crow::Request& req) 91 { 92 nlohmann::json reqJson; 93 JsonParseResult ret = parseRequestAsJson(req, reqJson); 94 if (ret != JsonParseResult::Success) 95 { 96 // We did not receive JSON request, proceed as it is RAW data 97 return req.body(); 98 } 99 100 std::string certificate; 101 std::optional<std::string> certificateType = "PEM"; 102 103 if (!json_util::readJsonPatch( // 104 req, asyncResp->res, // 105 "CertificateString", certificate, // 106 "CertificateType", certificateType // 107 )) 108 { 109 BMCWEB_LOG_ERROR("Required parameters are missing"); 110 messages::internalError(asyncResp->res); 111 return {}; 112 } 113 114 if (*certificateType != "PEM") 115 { 116 messages::propertyValueNotInList(asyncResp->res, *certificateType, 117 "CertificateType"); 118 return {}; 119 } 120 121 return certificate; 122 } 123 124 /** 125 * Class to create a temporary certificate file for uploading to system 126 */ 127 class CertificateFile 128 { 129 public: 130 CertificateFile() = delete; 131 CertificateFile(const CertificateFile&) = delete; 132 CertificateFile& operator=(const CertificateFile&) = delete; 133 CertificateFile(CertificateFile&&) = delete; 134 CertificateFile& operator=(CertificateFile&&) = delete; 135 explicit CertificateFile(const std::string& certString) 136 { 137 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C', 138 'e', 'r', 't', 's', '.', 'X', 139 'X', 'X', 'X', 'X', 'X', '\0'}; 140 // NOLINTNEXTLINE(misc-include-cleaner) 141 char* tempDirectory = mkdtemp(dirTemplate.data()); 142 if (tempDirectory != nullptr) 143 { 144 certDirectory = tempDirectory; 145 certificateFile = certDirectory / "cert.pem"; 146 std::ofstream out(certificateFile, 147 std::ofstream::out | std::ofstream::binary | 148 std::ofstream::trunc); 149 out << certString; 150 out.close(); 151 BMCWEB_LOG_DEBUG("Creating certificate file{}", 152 certificateFile.string()); 153 } 154 } 155 ~CertificateFile() 156 { 157 if (std::filesystem::exists(certDirectory)) 158 { 159 BMCWEB_LOG_DEBUG("Removing certificate file{}", 160 certificateFile.string()); 161 std::error_code ec; 162 std::filesystem::remove_all(certDirectory, ec); 163 if (ec) 164 { 165 BMCWEB_LOG_ERROR("Failed to remove temp directory{}", 166 certDirectory.string()); 167 } 168 } 169 } 170 std::string getCertFilePath() 171 { 172 return certificateFile; 173 } 174 175 private: 176 std::filesystem::path certificateFile; 177 std::filesystem::path certDirectory; 178 }; 179 180 /** 181 * @brief Parse and update Certificate Issue/Subject property 182 * 183 * @param[in] asyncResp Shared pointer to the response message 184 * @param[in] str Issuer/Subject value in key=value pairs 185 * @param[in] type Issuer/Subject 186 * @return None 187 */ 188 inline void updateCertIssuerOrSubject(nlohmann::json& out, 189 std::string_view value) 190 { 191 // example: O=openbmc-project.xyz,CN=localhost 192 std::string_view::iterator i = value.begin(); 193 while (i != value.end()) 194 { 195 std::string_view::iterator tokenBegin = i; 196 while (i != value.end() && *i != '=') 197 { 198 std::advance(i, 1); 199 } 200 if (i == value.end()) 201 { 202 break; 203 } 204 std::string_view key(tokenBegin, static_cast<size_t>(i - tokenBegin)); 205 std::advance(i, 1); 206 tokenBegin = i; 207 while (i != value.end() && *i != ',') 208 { 209 std::advance(i, 1); 210 } 211 std::string_view val(tokenBegin, static_cast<size_t>(i - tokenBegin)); 212 if (key == "L") 213 { 214 out["City"] = val; 215 } 216 else if (key == "CN") 217 { 218 out["CommonName"] = val; 219 } 220 else if (key == "C") 221 { 222 out["Country"] = val; 223 } 224 else if (key == "O") 225 { 226 out["Organization"] = val; 227 } 228 else if (key == "OU") 229 { 230 out["OrganizationalUnit"] = val; 231 } 232 else if (key == "ST") 233 { 234 out["State"] = val; 235 } 236 // skip comma character 237 if (i != value.end()) 238 { 239 std::advance(i, 1); 240 } 241 } 242 } 243 244 /** 245 * @brief Retrieve the installed certificate list 246 * 247 * @param[in] asyncResp Shared pointer to the response message 248 * @param[in] basePath DBus object path to search 249 * @param[in] listPtr Json pointer to the list in asyncResp 250 * @param[in] countPtr Json pointer to the count in asyncResp 251 * @return None 252 */ 253 inline void getCertificateList( 254 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 255 const std::string& basePath, const nlohmann::json::json_pointer& listPtr, 256 const nlohmann::json::json_pointer& countPtr) 257 { 258 constexpr std::array<std::string_view, 1> interfaces = { 259 certs::certPropIntf}; 260 dbus::utility::getSubTreePaths( 261 basePath, 0, interfaces, 262 [asyncResp, listPtr, countPtr]( 263 const boost::system::error_code& ec, 264 const dbus::utility::MapperGetSubTreePathsResponse& certPaths) { 265 if (ec) 266 { 267 BMCWEB_LOG_ERROR("Certificate collection query failed: {}", ec); 268 messages::internalError(asyncResp->res); 269 return; 270 } 271 272 nlohmann::json& links = asyncResp->res.jsonValue[listPtr]; 273 links = nlohmann::json::array(); 274 for (const auto& certPath : certPaths) 275 { 276 sdbusplus::message::object_path objPath(certPath); 277 std::string certId = objPath.filename(); 278 if (certId.empty()) 279 { 280 BMCWEB_LOG_ERROR("Invalid certificate objPath {}", 281 certPath); 282 continue; 283 } 284 285 boost::urls::url certURL; 286 if (objPath.parent_path() == certs::httpsObjectPath) 287 { 288 certURL = boost::urls::format( 289 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates/{}", 290 BMCWEB_REDFISH_MANAGER_URI_NAME, certId); 291 } 292 else if (objPath.parent_path() == certs::ldapObjectPath) 293 { 294 certURL = boost::urls::format( 295 "/redfish/v1/AccountService/LDAP/Certificates/{}", 296 certId); 297 } 298 else if (objPath.parent_path() == certs::authorityObjectPath) 299 { 300 certURL = boost::urls::format( 301 "/redfish/v1/Managers/{}/Truststore/Certificates/{}", 302 BMCWEB_REDFISH_MANAGER_URI_NAME, certId); 303 } 304 else 305 { 306 continue; 307 } 308 309 nlohmann::json::object_t link; 310 link["@odata.id"] = certURL; 311 links.emplace_back(std::move(link)); 312 } 313 314 asyncResp->res.jsonValue[countPtr] = links.size(); 315 }); 316 } 317 318 /** 319 * @brief Retrieve the certificates properties and append to the response 320 * message 321 * 322 * @param[in] asyncResp Shared pointer to the response message 323 * @param[in] objectPath Path of the D-Bus service object 324 * @param[in] certId Id of the certificate 325 * @param[in] certURL URL of the certificate object 326 * @param[in] name name of the certificate 327 * @return None 328 */ 329 inline void getCertificateProperties( 330 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 331 const std::string& objectPath, const std::string& service, 332 const std::string& certId, const boost::urls::url& certURL, 333 const std::string& name) 334 { 335 BMCWEB_LOG_DEBUG("getCertificateProperties Path={} certId={} certURl={}", 336 objectPath, certId, certURL); 337 dbus::utility::getAllProperties( 338 service, objectPath, certs::certPropIntf, 339 [asyncResp, certURL, certId, 340 name](const boost::system::error_code& ec, 341 const dbus::utility::DBusPropertiesMap& properties) { 342 if (ec) 343 { 344 BMCWEB_LOG_ERROR("DBUS response error: {}", ec); 345 messages::resourceNotFound(asyncResp->res, "Certificate", 346 certId); 347 return; 348 } 349 350 const std::string* certificateString = nullptr; 351 const std::vector<std::string>* keyUsage = nullptr; 352 const std::string* issuer = nullptr; 353 const std::string* subject = nullptr; 354 const uint64_t* validNotAfter = nullptr; 355 const uint64_t* validNotBefore = nullptr; 356 357 const bool success = sdbusplus::unpackPropertiesNoThrow( 358 dbus_utils::UnpackErrorPrinter(), properties, 359 "CertificateString", certificateString, "KeyUsage", keyUsage, 360 "Issuer", issuer, "Subject", subject, "ValidNotAfter", 361 validNotAfter, "ValidNotBefore", validNotBefore); 362 363 if (!success) 364 { 365 messages::internalError(asyncResp->res); 366 return; 367 } 368 369 asyncResp->res.jsonValue["@odata.id"] = certURL; 370 asyncResp->res.jsonValue["@odata.type"] = 371 "#Certificate.v1_0_0.Certificate"; 372 asyncResp->res.jsonValue["Id"] = certId; 373 asyncResp->res.jsonValue["Name"] = name; 374 asyncResp->res.jsonValue["Description"] = name; 375 asyncResp->res.jsonValue["CertificateString"] = ""; 376 asyncResp->res.jsonValue["KeyUsage"] = nlohmann::json::array(); 377 378 if (certificateString != nullptr) 379 { 380 asyncResp->res.jsonValue["CertificateString"] = 381 *certificateString; 382 } 383 384 if (keyUsage != nullptr) 385 { 386 asyncResp->res.jsonValue["KeyUsage"] = *keyUsage; 387 } 388 389 if (issuer != nullptr) 390 { 391 updateCertIssuerOrSubject(asyncResp->res.jsonValue["Issuer"], 392 *issuer); 393 } 394 395 if (subject != nullptr) 396 { 397 updateCertIssuerOrSubject(asyncResp->res.jsonValue["Subject"], 398 *subject); 399 } 400 401 if (validNotAfter != nullptr) 402 { 403 asyncResp->res.jsonValue["ValidNotAfter"] = 404 redfish::time_utils::getDateTimeUint(*validNotAfter); 405 } 406 407 if (validNotBefore != nullptr) 408 { 409 asyncResp->res.jsonValue["ValidNotBefore"] = 410 redfish::time_utils::getDateTimeUint(*validNotBefore); 411 } 412 413 asyncResp->res.addHeader( 414 boost::beast::http::field::location, 415 std::string_view(certURL.data(), certURL.size())); 416 }); 417 } 418 419 inline void deleteCertificate( 420 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 421 const std::string& service, 422 const sdbusplus::message::object_path& objectPath) 423 { 424 dbus::utility::async_method_call( 425 asyncResp, 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 dbus::utility::async_method_call( 613 asyncResp, 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 dbus::utility::async_method_call( 660 asyncResp, 661 [asyncResp, 662 certURI](const boost::system::error_code& ec, const std::string& csr) { 663 if (ec) 664 { 665 BMCWEB_LOG_ERROR("DBUS response error: {}", ec); 666 messages::internalError(asyncResp->res); 667 return; 668 } 669 if (csr.empty()) 670 { 671 BMCWEB_LOG_ERROR("CSR read is empty"); 672 messages::internalError(asyncResp->res); 673 return; 674 } 675 asyncResp->res.jsonValue["CSRString"] = csr; 676 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] = 677 certURI; 678 }, 679 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR"); 680 } 681 682 inline void handleGenerateCSRAction( 683 App& app, const crow::Request& req, 684 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 685 { 686 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 687 { 688 return; 689 } 690 static const int rsaKeyBitLength = 2048; 691 692 // Required parameters 693 std::string city; 694 std::string commonName; 695 std::string country; 696 std::string organization; 697 std::string organizationalUnit; 698 std::string state; 699 std::string certURI; 700 701 // Optional parameters 702 std::optional<std::vector<std::string>> optAlternativeNames = 703 std::vector<std::string>(); 704 std::optional<std::string> optContactPerson = ""; 705 std::optional<std::string> optChallengePassword = ""; 706 std::optional<std::string> optEmail = ""; 707 std::optional<std::string> optGivenName = ""; 708 std::optional<std::string> optInitials = ""; 709 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength; 710 std::optional<std::string> optKeyCurveId = "secp384r1"; 711 std::optional<std::string> optKeyPairAlgorithm = "EC"; 712 std::optional<std::vector<std::string>> optKeyUsage = 713 std::vector<std::string>(); 714 std::optional<std::string> optSurname = ""; 715 std::optional<std::string> optUnstructuredName = ""; 716 if (!json_util::readJsonAction( // 717 req, asyncResp->res, // 718 "AlternativeNames", optAlternativeNames, // 719 "CertificateCollection/@odata.id", certURI, // 720 "ChallengePassword", optChallengePassword, // 721 "City", city, // 722 "CommonName", commonName, // 723 "ContactPerson", optContactPerson, // 724 "Country", country, // 725 "Email", optEmail, // 726 "GivenName", optGivenName, // 727 "Initials", optInitials, // 728 "KeyBitLength", optKeyBitLength, // 729 "KeyCurveId", optKeyCurveId, // 730 "KeyPairAlgorithm", optKeyPairAlgorithm, // 731 "KeyUsage", optKeyUsage, // 732 "Organization", organization, // 733 "OrganizationalUnit", organizationalUnit, // 734 "State", state, // 735 "Surname", optSurname, // 736 "UnstructuredName", optUnstructuredName // 737 )) 738 { 739 return; 740 } 741 742 // bmcweb has no way to store or decode a private key challenge 743 // password, which will likely cause bmcweb to crash on startup 744 // if this is not set on a post so not allowing the user to set 745 // value 746 if (!optChallengePassword->empty()) 747 { 748 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR", 749 "ChallengePassword"); 750 return; 751 } 752 753 std::string objectPath; 754 std::string service; 755 if (certURI.starts_with(std::format( 756 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates", 757 BMCWEB_REDFISH_MANAGER_URI_NAME))) 758 { 759 objectPath = certs::httpsObjectPath; 760 service = certs::httpsServiceName; 761 } 762 else if (certURI.starts_with( 763 "/redfish/v1/AccountService/LDAP/Certificates")) 764 { 765 objectPath = certs::ldapObjectPath; 766 service = certs::ldapServiceName; 767 } 768 else 769 { 770 messages::actionParameterNotSupported( 771 asyncResp->res, "CertificateCollection", "GenerateCSR"); 772 return; 773 } 774 775 // supporting only EC and RSA algorithm 776 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA") 777 { 778 messages::actionParameterNotSupported( 779 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR"); 780 return; 781 } 782 783 // supporting only 2048 key bit length for RSA algorithm due to 784 // time consumed in generating private key 785 if (*optKeyPairAlgorithm == "RSA" && *optKeyBitLength != rsaKeyBitLength) 786 { 787 messages::propertyValueNotInList(asyncResp->res, *optKeyBitLength, 788 "KeyBitLength"); 789 return; 790 } 791 792 // validate KeyUsage supporting only 1 type based on URL 793 if (certURI.starts_with(std::format( 794 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates", 795 BMCWEB_REDFISH_MANAGER_URI_NAME))) 796 { 797 if (optKeyUsage->empty()) 798 { 799 optKeyUsage->emplace_back("ServerAuthentication"); 800 } 801 else if (optKeyUsage->size() == 1) 802 { 803 if ((*optKeyUsage)[0] != "ServerAuthentication") 804 { 805 messages::propertyValueNotInList(asyncResp->res, 806 (*optKeyUsage)[0], "KeyUsage"); 807 return; 808 } 809 } 810 else 811 { 812 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage", 813 "GenerateCSR"); 814 return; 815 } 816 } 817 else if (certURI.starts_with( 818 "/redfish/v1/AccountService/LDAP/Certificates")) 819 { 820 if (optKeyUsage->empty()) 821 { 822 optKeyUsage->emplace_back("ClientAuthentication"); 823 } 824 else if (optKeyUsage->size() == 1) 825 { 826 if ((*optKeyUsage)[0] != "ClientAuthentication") 827 { 828 messages::propertyValueNotInList(asyncResp->res, 829 (*optKeyUsage)[0], "KeyUsage"); 830 return; 831 } 832 } 833 else 834 { 835 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage", 836 "GenerateCSR"); 837 return; 838 } 839 } 840 841 // Only allow one CSR matcher at a time so setting retry 842 // time-out and timer expiry to 10 seconds for now. 843 static const int timeOut = 10; 844 if (csrMatcher) 845 { 846 messages::serviceTemporarilyUnavailable(asyncResp->res, 847 std::to_string(timeOut)); 848 return; 849 } 850 851 // Make this static so it survives outside this method 852 static boost::asio::steady_timer timeout(getIoContext()); 853 timeout.expires_after(std::chrono::seconds(timeOut)); 854 timeout.async_wait([asyncResp](const boost::system::error_code& ec) { 855 csrMatcher = nullptr; 856 if (ec) 857 { 858 // operation_aborted is expected if timer is canceled 859 // before completion. 860 if (ec != boost::asio::error::operation_aborted) 861 { 862 BMCWEB_LOG_ERROR("Async_wait failed {}", ec); 863 } 864 return; 865 } 866 BMCWEB_LOG_ERROR("Timed out waiting for Generating CSR"); 867 messages::internalError(asyncResp->res); 868 }); 869 870 // create a matcher to wait on CSR object 871 BMCWEB_LOG_DEBUG("create matcher with path {}", objectPath); 872 std::string match("type='signal'," 873 "interface='org.freedesktop.DBus.ObjectManager'," 874 "path='" + 875 objectPath + 876 "'," 877 "member='InterfacesAdded'"); 878 csrMatcher = std::make_unique<sdbusplus::bus::match_t>( 879 *crow::connections::systemBus, match, 880 [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) { 881 timeout.cancel(); 882 if (m.is_method_error()) 883 { 884 BMCWEB_LOG_ERROR("Dbus method error!!!"); 885 messages::internalError(asyncResp->res); 886 return; 887 } 888 889 dbus::utility::DBusInterfacesMap interfacesProperties; 890 891 sdbusplus::message::object_path csrObjectPath; 892 m.read(csrObjectPath, interfacesProperties); 893 BMCWEB_LOG_DEBUG("CSR object added{}", csrObjectPath.str); 894 for (const auto& interface : interfacesProperties) 895 { 896 if (interface.first == "xyz.openbmc_project.Certs.CSR") 897 { 898 getCSR(asyncResp, certURI, service, objectPath, 899 csrObjectPath.str); 900 break; 901 } 902 } 903 }); 904 dbus::utility::async_method_call( 905 asyncResp, 906 [asyncResp](const boost::system::error_code& ec, const std::string&) { 907 if (ec) 908 { 909 BMCWEB_LOG_ERROR("DBUS response error: {}", ec.message()); 910 messages::internalError(asyncResp->res); 911 return; 912 } 913 }, 914 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create", 915 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city, 916 commonName, *optContactPerson, country, *optEmail, *optGivenName, 917 *optInitials, *optKeyBitLength, *optKeyCurveId, *optKeyPairAlgorithm, 918 *optKeyUsage, organization, organizationalUnit, state, *optSurname, 919 *optUnstructuredName); 920 } 921 922 inline void requestRoutesCertificateService(App& app) 923 { 924 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/") 925 .privileges(redfish::privileges::getCertificateService) 926 .methods(boost::beast::http::verb::get)( 927 std::bind_front(handleCertificateServiceGet, std::ref(app))); 928 929 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/") 930 .privileges(redfish::privileges::getCertificateLocations) 931 .methods(boost::beast::http::verb::get)( 932 std::bind_front(handleCertificateLocationsGet, std::ref(app))); 933 934 BMCWEB_ROUTE( 935 app, 936 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/") 937 .privileges(redfish::privileges::postCertificateService) 938 .methods(boost::beast::http::verb::post)( 939 std::bind_front(handleReplaceCertificateAction, std::ref(app))); 940 941 BMCWEB_ROUTE( 942 app, 943 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/") 944 .privileges(redfish::privileges::postCertificateService) 945 .methods(boost::beast::http::verb::post)( 946 std::bind_front(handleGenerateCSRAction, std::ref(app))); 947 } // requestRoutesCertificateService 948 949 inline void handleHTTPSCertificateCollectionGet( 950 App& app, const crow::Request& req, 951 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 952 const std::string& managerId) 953 { 954 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 955 { 956 return; 957 } 958 959 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 960 { 961 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 962 return; 963 } 964 965 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 966 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates", 967 BMCWEB_REDFISH_MANAGER_URI_NAME); 968 asyncResp->res.jsonValue["@odata.type"] = 969 "#CertificateCollection.CertificateCollection"; 970 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection"; 971 asyncResp->res.jsonValue["Description"] = 972 "A Collection of HTTPS certificate instances"; 973 974 getCertificateList(asyncResp, certs::httpsObjectPath, 975 "/Members"_json_pointer, 976 "/Members@odata.count"_json_pointer); 977 } 978 979 inline void handleHTTPSCertificateCollectionPost( 980 App& app, const crow::Request& req, 981 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 982 const std::string& managerId) 983 { 984 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 985 { 986 return; 987 } 988 989 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 990 { 991 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 992 return; 993 } 994 995 BMCWEB_LOG_DEBUG("HTTPSCertificateCollection::doPost"); 996 997 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate"; 998 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate"; 999 1000 std::string certHttpBody = getCertificateFromReqBody(asyncResp, req); 1001 1002 if (certHttpBody.empty()) 1003 { 1004 BMCWEB_LOG_ERROR("Cannot get certificate from request body."); 1005 messages::unrecognizedRequestBody(asyncResp->res); 1006 return; 1007 } 1008 1009 std::shared_ptr<CertificateFile> certFile = 1010 std::make_shared<CertificateFile>(certHttpBody); 1011 1012 dbus::utility::async_method_call( 1013 asyncResp, 1014 [asyncResp, certFile](const boost::system::error_code& ec, 1015 const std::string& objectPath) { 1016 if (ec) 1017 { 1018 BMCWEB_LOG_ERROR("DBUS response error: {}", ec); 1019 messages::internalError(asyncResp->res); 1020 return; 1021 } 1022 1023 sdbusplus::message::object_path path(objectPath); 1024 std::string certId = path.filename(); 1025 const boost::urls::url certURL = boost::urls::format( 1026 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates/{}", 1027 BMCWEB_REDFISH_MANAGER_URI_NAME, certId); 1028 getCertificateProperties(asyncResp, objectPath, 1029 certs::httpsServiceName, certId, certURL, 1030 "HTTPS Certificate"); 1031 BMCWEB_LOG_DEBUG("HTTPS certificate install file={}", 1032 certFile->getCertFilePath()); 1033 }, 1034 certs::httpsServiceName, certs::httpsObjectPath, certs::certInstallIntf, 1035 "Install", certFile->getCertFilePath()); 1036 } 1037 1038 inline void handleHTTPSCertificateGet( 1039 App& app, const crow::Request& req, 1040 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1041 const std::string& managerId, const std::string& certId) 1042 { 1043 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1044 { 1045 return; 1046 } 1047 1048 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1049 { 1050 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1051 return; 1052 } 1053 1054 BMCWEB_LOG_DEBUG("HTTPS Certificate ID={}", certId); 1055 const boost::urls::url certURL = boost::urls::format( 1056 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates/{}", 1057 BMCWEB_REDFISH_MANAGER_URI_NAME, certId); 1058 std::string objPath = 1059 sdbusplus::message::object_path(certs::httpsObjectPath) / certId; 1060 getCertificateProperties(asyncResp, objPath, certs::httpsServiceName, 1061 certId, certURL, "HTTPS Certificate"); 1062 } 1063 1064 inline void requestRoutesHTTPSCertificate(App& app) 1065 { 1066 BMCWEB_ROUTE( 1067 app, "/redfish/v1/Managers/<str>/NetworkProtocol/HTTPS/Certificates/") 1068 .privileges(redfish::privileges::getCertificateCollection) 1069 .methods(boost::beast::http::verb::get)(std::bind_front( 1070 handleHTTPSCertificateCollectionGet, std::ref(app))); 1071 1072 BMCWEB_ROUTE( 1073 app, "/redfish/v1/Managers/<str>/NetworkProtocol/HTTPS/Certificates/") 1074 .privileges(redfish::privileges::postCertificateCollection) 1075 .methods(boost::beast::http::verb::post)(std::bind_front( 1076 handleHTTPSCertificateCollectionPost, std::ref(app))); 1077 1078 BMCWEB_ROUTE( 1079 app, 1080 "/redfish/v1/Managers/<str>/NetworkProtocol/HTTPS/Certificates/<str>/") 1081 .privileges(redfish::privileges::getCertificate) 1082 .methods(boost::beast::http::verb::get)( 1083 std::bind_front(handleHTTPSCertificateGet, std::ref(app))); 1084 } 1085 1086 inline void handleLDAPCertificateCollectionGet( 1087 App& app, const crow::Request& req, 1088 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1089 { 1090 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1091 { 1092 return; 1093 } 1094 1095 asyncResp->res.jsonValue["@odata.id"] = 1096 "/redfish/v1/AccountService/LDAP/Certificates"; 1097 asyncResp->res.jsonValue["@odata.type"] = 1098 "#CertificateCollection.CertificateCollection"; 1099 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection"; 1100 asyncResp->res.jsonValue["Description"] = 1101 "A Collection of LDAP certificate instances"; 1102 1103 getCertificateList(asyncResp, certs::ldapObjectPath, 1104 "/Members"_json_pointer, 1105 "/Members@odata.count"_json_pointer); 1106 } 1107 1108 inline void handleLDAPCertificateCollectionPost( 1109 App& app, const crow::Request& req, 1110 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1111 { 1112 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1113 { 1114 return; 1115 } 1116 std::string certHttpBody = getCertificateFromReqBody(asyncResp, req); 1117 1118 if (certHttpBody.empty()) 1119 { 1120 BMCWEB_LOG_ERROR("Cannot get certificate from request body."); 1121 messages::unrecognizedRequestBody(asyncResp->res); 1122 return; 1123 } 1124 1125 std::shared_ptr<CertificateFile> certFile = 1126 std::make_shared<CertificateFile>(certHttpBody); 1127 1128 dbus::utility::async_method_call( 1129 asyncResp, 1130 [asyncResp, certFile](const boost::system::error_code& ec, 1131 const std::string& objectPath) { 1132 if (ec) 1133 { 1134 BMCWEB_LOG_ERROR("DBUS response error: {}", ec); 1135 messages::internalError(asyncResp->res); 1136 return; 1137 } 1138 1139 sdbusplus::message::object_path path(objectPath); 1140 std::string certId = path.filename(); 1141 const boost::urls::url certURL = boost::urls::format( 1142 "/redfish/v1/AccountService/LDAP/Certificates/{}", certId); 1143 getCertificateProperties(asyncResp, objectPath, 1144 certs::ldapServiceName, certId, certURL, 1145 "LDAP Certificate"); 1146 BMCWEB_LOG_DEBUG("LDAP certificate install file={}", 1147 certFile->getCertFilePath()); 1148 }, 1149 certs::ldapServiceName, certs::ldapObjectPath, certs::certInstallIntf, 1150 "Install", certFile->getCertFilePath()); 1151 } 1152 1153 inline void handleLDAPCertificateGet( 1154 App& app, const crow::Request& req, 1155 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) 1156 { 1157 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1158 { 1159 return; 1160 } 1161 1162 BMCWEB_LOG_DEBUG("LDAP Certificate ID={}", id); 1163 const boost::urls::url certURL = boost::urls::format( 1164 "/redfish/v1/AccountService/LDAP/Certificates/{}", id); 1165 std::string objPath = 1166 sdbusplus::message::object_path(certs::ldapObjectPath) / id; 1167 getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id, 1168 certURL, "LDAP Certificate"); 1169 } 1170 1171 inline void handleLDAPCertificateDelete( 1172 App& app, const crow::Request& req, 1173 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) 1174 { 1175 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1176 { 1177 return; 1178 } 1179 1180 BMCWEB_LOG_DEBUG("Delete LDAP Certificate ID={}", id); 1181 std::string objPath = 1182 sdbusplus::message::object_path(certs::ldapObjectPath) / id; 1183 1184 deleteCertificate(asyncResp, certs::ldapServiceName, objPath); 1185 } 1186 1187 inline void requestRoutesLDAPCertificate(App& app) 1188 { 1189 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/") 1190 .privileges(redfish::privileges::getCertificateCollection) 1191 .methods(boost::beast::http::verb::get)( 1192 std::bind_front(handleLDAPCertificateCollectionGet, std::ref(app))); 1193 1194 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/") 1195 .privileges(redfish::privileges::postCertificateCollection) 1196 .methods(boost::beast::http::verb::post)(std::bind_front( 1197 handleLDAPCertificateCollectionPost, std::ref(app))); 1198 1199 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/") 1200 .privileges(redfish::privileges::getCertificate) 1201 .methods(boost::beast::http::verb::get)( 1202 std::bind_front(handleLDAPCertificateGet, std::ref(app))); 1203 1204 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/") 1205 .privileges(redfish::privileges::deleteCertificate) 1206 .methods(boost::beast::http::verb::delete_)( 1207 std::bind_front(handleLDAPCertificateDelete, std::ref(app))); 1208 } // requestRoutesLDAPCertificate 1209 1210 inline void handleTrustStoreCertificateCollectionGet( 1211 App& app, const crow::Request& req, 1212 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1213 const std::string& managerId) 1214 { 1215 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1216 { 1217 return; 1218 } 1219 1220 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1221 { 1222 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1223 return; 1224 } 1225 1226 asyncResp->res.jsonValue["@odata.id"] = 1227 boost::urls::format("/redfish/v1/Managers/{}/Truststore/Certificates/", 1228 BMCWEB_REDFISH_MANAGER_URI_NAME); 1229 asyncResp->res.jsonValue["@odata.type"] = 1230 "#CertificateCollection.CertificateCollection"; 1231 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection"; 1232 asyncResp->res.jsonValue["Description"] = 1233 "A Collection of TrustStore certificate instances"; 1234 1235 getCertificateList(asyncResp, certs::authorityObjectPath, 1236 "/Members"_json_pointer, 1237 "/Members@odata.count"_json_pointer); 1238 } 1239 1240 inline void handleTrustStoreCertificateCollectionPost( 1241 App& app, const crow::Request& req, 1242 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1243 const std::string& managerId) 1244 { 1245 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1246 { 1247 return; 1248 } 1249 1250 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME) 1251 { 1252 messages::resourceNotFound(asyncResp->res, "Manager", managerId); 1253 return; 1254 } 1255 1256 std::string certHttpBody = getCertificateFromReqBody(asyncResp, req); 1257 1258 if (certHttpBody.empty()) 1259 { 1260 BMCWEB_LOG_ERROR("Cannot get certificate from request body."); 1261 messages::unrecognizedRequestBody(asyncResp->res); 1262 return; 1263 } 1264 1265 std::shared_ptr<CertificateFile> certFile = 1266 std::make_shared<CertificateFile>(certHttpBody); 1267 dbus::utility::async_method_call( 1268 asyncResp, 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