xref: /openbmc/bmcweb/redfish-core/lib/certificate_service.hpp (revision d98a2f9388b9cab1100d30de401da43c32c98ef4)
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* dbusPropIntf = "org.freedesktop.DBus.Properties";
69  constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
70  constexpr const char* httpsServiceName =
71      "xyz.openbmc_project.Certs.Manager.Server.Https";
72  constexpr const char* ldapServiceName =
73      "xyz.openbmc_project.Certs.Manager.Client.Ldap";
74  constexpr const char* authorityServiceName =
75      "xyz.openbmc_project.Certs.Manager.Authority.Truststore";
76  constexpr const char* baseObjectPath = "/xyz/openbmc_project/certs";
77  constexpr const char* httpsObjectPath =
78      "/xyz/openbmc_project/certs/server/https";
79  constexpr const char* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
80  constexpr const char* authorityObjectPath =
81      "/xyz/openbmc_project/certs/authority/truststore";
82  } // namespace certs
83  
84  /**
85   * The Certificate schema defines a Certificate Service which represents the
86   * actions available to manage certificates and links to where certificates
87   * are installed.
88   */
89  
getCertificateFromReqBody(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const crow::Request & req)90  inline std::string getCertificateFromReqBody(
91      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
92      const crow::Request& req)
93  {
94      nlohmann::json reqJson;
95      JsonParseResult ret = parseRequestAsJson(req, reqJson);
96      if (ret != JsonParseResult::Success)
97      {
98          // We did not receive JSON request, proceed as it is RAW data
99          return req.body();
100      }
101  
102      std::string certificate;
103      std::optional<std::string> certificateType = "PEM";
104  
105      if (!json_util::readJsonPatch(             //
106              req, asyncResp->res,               //
107              "CertificateString", certificate,  //
108              "CertificateType", certificateType //
109              ))
110      {
111          BMCWEB_LOG_ERROR("Required parameters are missing");
112          messages::internalError(asyncResp->res);
113          return {};
114      }
115  
116      if (*certificateType != "PEM")
117      {
118          messages::propertyValueNotInList(asyncResp->res, *certificateType,
119                                           "CertificateType");
120          return {};
121      }
122  
123      return certificate;
124  }
125  
126  /**
127   * Class to create a temporary certificate file for uploading to system
128   */
129  class CertificateFile
130  {
131    public:
132      CertificateFile() = delete;
133      CertificateFile(const CertificateFile&) = delete;
134      CertificateFile& operator=(const CertificateFile&) = delete;
135      CertificateFile(CertificateFile&&) = delete;
136      CertificateFile& operator=(CertificateFile&&) = delete;
CertificateFile(const std::string & certString)137      explicit CertificateFile(const std::string& certString)
138      {
139          std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
140                                              'e', 'r', 't', 's', '.', 'X',
141                                              'X', 'X', 'X', 'X', 'X', '\0'};
142          // NOLINTNEXTLINE(misc-include-cleaner)
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      }
~CertificateFile()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      }
getCertFilePath()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   */
updateCertIssuerOrSubject(nlohmann::json & out,std::string_view value)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   */
getCertificateList(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & basePath,const nlohmann::json::json_pointer & listPtr,const nlohmann::json::json_pointer & countPtr)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   */
getCertificateProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & objectPath,const std::string & service,const std::string & certId,const boost::urls::url & certURL,const std::string & name)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  
deleteCertificate(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & service,const sdbusplus::message::object_path & objectPath)421  inline void deleteCertificate(
422      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  
handleCertificateServiceGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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  
handleCertificateLocationsGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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  
handleError(const std::string_view dbusErrorName,const std::string & id,const std::string & certificate,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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  
handleReplaceCertificateAction(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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   */
getCSR(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & certURI,const std::string & service,const std::string & certObjPath,const std::string & csrObjPath)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  
handleGenerateCSRAction(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)681  inline void handleGenerateCSRAction(
682      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      // Make this static so it survives outside this method
851      static boost::asio::steady_timer timeout(getIoContext());
852      timeout.expires_after(std::chrono::seconds(timeOut));
853      timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
854          csrMatcher = nullptr;
855          if (ec)
856          {
857              // operation_aborted is expected if timer is canceled
858              // before completion.
859              if (ec != boost::asio::error::operation_aborted)
860              {
861                  BMCWEB_LOG_ERROR("Async_wait failed {}", ec);
862              }
863              return;
864          }
865          BMCWEB_LOG_ERROR("Timed out waiting for Generating CSR");
866          messages::internalError(asyncResp->res);
867      });
868  
869      // create a matcher to wait on CSR object
870      BMCWEB_LOG_DEBUG("create matcher with path {}", objectPath);
871      std::string match("type='signal',"
872                        "interface='org.freedesktop.DBus.ObjectManager',"
873                        "path='" +
874                        objectPath +
875                        "',"
876                        "member='InterfacesAdded'");
877      csrMatcher = std::make_unique<sdbusplus::bus::match_t>(
878          *crow::connections::systemBus, match,
879          [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) {
880              timeout.cancel();
881              if (m.is_method_error())
882              {
883                  BMCWEB_LOG_ERROR("Dbus method error!!!");
884                  messages::internalError(asyncResp->res);
885                  return;
886              }
887  
888              dbus::utility::DBusInterfacesMap interfacesProperties;
889  
890              sdbusplus::message::object_path csrObjectPath;
891              m.read(csrObjectPath, interfacesProperties);
892              BMCWEB_LOG_DEBUG("CSR object added{}", csrObjectPath.str);
893              for (const auto& interface : interfacesProperties)
894              {
895                  if (interface.first == "xyz.openbmc_project.Certs.CSR")
896                  {
897                      getCSR(asyncResp, certURI, service, objectPath,
898                             csrObjectPath.str);
899                      break;
900                  }
901              }
902          });
903      crow::connections::systemBus->async_method_call(
904          [asyncResp](const boost::system::error_code& ec, const std::string&) {
905              if (ec)
906              {
907                  BMCWEB_LOG_ERROR("DBUS response error: {}", ec.message());
908                  messages::internalError(asyncResp->res);
909                  return;
910              }
911          },
912          service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
913          "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
914          commonName, *optContactPerson, country, *optEmail, *optGivenName,
915          *optInitials, *optKeyBitLength, *optKeyCurveId, *optKeyPairAlgorithm,
916          *optKeyUsage, organization, organizationalUnit, state, *optSurname,
917          *optUnstructuredName);
918  }
919  
requestRoutesCertificateService(App & app)920  inline void requestRoutesCertificateService(App& app)
921  {
922      BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
923          .privileges(redfish::privileges::getCertificateService)
924          .methods(boost::beast::http::verb::get)(
925              std::bind_front(handleCertificateServiceGet, std::ref(app)));
926  
927      BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
928          .privileges(redfish::privileges::getCertificateLocations)
929          .methods(boost::beast::http::verb::get)(
930              std::bind_front(handleCertificateLocationsGet, std::ref(app)));
931  
932      BMCWEB_ROUTE(
933          app,
934          "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
935          .privileges(redfish::privileges::postCertificateService)
936          .methods(boost::beast::http::verb::post)(
937              std::bind_front(handleReplaceCertificateAction, std::ref(app)));
938  
939      BMCWEB_ROUTE(
940          app,
941          "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
942          .privileges(redfish::privileges::postCertificateService)
943          .methods(boost::beast::http::verb::post)(
944              std::bind_front(handleGenerateCSRAction, std::ref(app)));
945  } // requestRoutesCertificateService
946  
handleHTTPSCertificateCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)947  inline void handleHTTPSCertificateCollectionGet(
948      App& app, const crow::Request& req,
949      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
950      const std::string& managerId)
951  {
952      if (!redfish::setUpRedfishRoute(app, req, asyncResp))
953      {
954          return;
955      }
956  
957      if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
958      {
959          messages::resourceNotFound(asyncResp->res, "Manager", managerId);
960          return;
961      }
962  
963      asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
964          "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates",
965          BMCWEB_REDFISH_MANAGER_URI_NAME);
966      asyncResp->res.jsonValue["@odata.type"] =
967          "#CertificateCollection.CertificateCollection";
968      asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
969      asyncResp->res.jsonValue["Description"] =
970          "A Collection of HTTPS certificate instances";
971  
972      getCertificateList(asyncResp, certs::httpsObjectPath,
973                         "/Members"_json_pointer,
974                         "/Members@odata.count"_json_pointer);
975  }
976  
handleHTTPSCertificateCollectionPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)977  inline void handleHTTPSCertificateCollectionPost(
978      App& app, const crow::Request& req,
979      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
980      const std::string& managerId)
981  {
982      if (!redfish::setUpRedfishRoute(app, req, asyncResp))
983      {
984          return;
985      }
986  
987      if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
988      {
989          messages::resourceNotFound(asyncResp->res, "Manager", managerId);
990          return;
991      }
992  
993      BMCWEB_LOG_DEBUG("HTTPSCertificateCollection::doPost");
994  
995      asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
996      asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
997  
998      std::string certHttpBody = getCertificateFromReqBody(asyncResp, req);
999  
1000      if (certHttpBody.empty())
1001      {
1002          BMCWEB_LOG_ERROR("Cannot get certificate from request body.");
1003          messages::unrecognizedRequestBody(asyncResp->res);
1004          return;
1005      }
1006  
1007      std::shared_ptr<CertificateFile> certFile =
1008          std::make_shared<CertificateFile>(certHttpBody);
1009  
1010      crow::connections::systemBus->async_method_call(
1011          [asyncResp, certFile](const boost::system::error_code& ec,
1012                                const std::string& objectPath) {
1013              if (ec)
1014              {
1015                  BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
1016                  messages::internalError(asyncResp->res);
1017                  return;
1018              }
1019  
1020              sdbusplus::message::object_path path(objectPath);
1021              std::string certId = path.filename();
1022              const boost::urls::url certURL = boost::urls::format(
1023                  "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates/{}",
1024                  BMCWEB_REDFISH_MANAGER_URI_NAME, certId);
1025              getCertificateProperties(asyncResp, objectPath,
1026                                       certs::httpsServiceName, certId, certURL,
1027                                       "HTTPS Certificate");
1028              BMCWEB_LOG_DEBUG("HTTPS certificate install file={}",
1029                               certFile->getCertFilePath());
1030          },
1031          certs::httpsServiceName, certs::httpsObjectPath, certs::certInstallIntf,
1032          "Install", certFile->getCertFilePath());
1033  }
1034  
handleHTTPSCertificateGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId,const std::string & certId)1035  inline void handleHTTPSCertificateGet(
1036      App& app, const crow::Request& req,
1037      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1038      const std::string& managerId, const std::string& certId)
1039  {
1040      if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1041      {
1042          return;
1043      }
1044  
1045      if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1046      {
1047          messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1048          return;
1049      }
1050  
1051      BMCWEB_LOG_DEBUG("HTTPS Certificate ID={}", certId);
1052      const boost::urls::url certURL = boost::urls::format(
1053          "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates/{}",
1054          BMCWEB_REDFISH_MANAGER_URI_NAME, certId);
1055      std::string objPath =
1056          sdbusplus::message::object_path(certs::httpsObjectPath) / certId;
1057      getCertificateProperties(asyncResp, objPath, certs::httpsServiceName,
1058                               certId, certURL, "HTTPS Certificate");
1059  }
1060  
requestRoutesHTTPSCertificate(App & app)1061  inline void requestRoutesHTTPSCertificate(App& app)
1062  {
1063      BMCWEB_ROUTE(
1064          app, "/redfish/v1/Managers/<str>/NetworkProtocol/HTTPS/Certificates/")
1065          .privileges(redfish::privileges::getCertificateCollection)
1066          .methods(boost::beast::http::verb::get)(std::bind_front(
1067              handleHTTPSCertificateCollectionGet, std::ref(app)));
1068  
1069      BMCWEB_ROUTE(
1070          app, "/redfish/v1/Managers/<str>/NetworkProtocol/HTTPS/Certificates/")
1071          .privileges(redfish::privileges::postCertificateCollection)
1072          .methods(boost::beast::http::verb::post)(std::bind_front(
1073              handleHTTPSCertificateCollectionPost, std::ref(app)));
1074  
1075      BMCWEB_ROUTE(
1076          app,
1077          "/redfish/v1/Managers/<str>/NetworkProtocol/HTTPS/Certificates/<str>/")
1078          .privileges(redfish::privileges::getCertificate)
1079          .methods(boost::beast::http::verb::get)(
1080              std::bind_front(handleHTTPSCertificateGet, std::ref(app)));
1081  }
1082  
handleLDAPCertificateCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1083  inline void handleLDAPCertificateCollectionGet(
1084      App& app, const crow::Request& req,
1085      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1086  {
1087      if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1088      {
1089          return;
1090      }
1091  
1092      asyncResp->res.jsonValue["@odata.id"] =
1093          "/redfish/v1/AccountService/LDAP/Certificates";
1094      asyncResp->res.jsonValue["@odata.type"] =
1095          "#CertificateCollection.CertificateCollection";
1096      asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1097      asyncResp->res.jsonValue["Description"] =
1098          "A Collection of LDAP certificate instances";
1099  
1100      getCertificateList(asyncResp, certs::ldapObjectPath,
1101                         "/Members"_json_pointer,
1102                         "/Members@odata.count"_json_pointer);
1103  }
1104  
handleLDAPCertificateCollectionPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1105  inline void handleLDAPCertificateCollectionPost(
1106      App& app, const crow::Request& req,
1107      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1108  {
1109      if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1110      {
1111          return;
1112      }
1113      std::string certHttpBody = getCertificateFromReqBody(asyncResp, req);
1114  
1115      if (certHttpBody.empty())
1116      {
1117          BMCWEB_LOG_ERROR("Cannot get certificate from request body.");
1118          messages::unrecognizedRequestBody(asyncResp->res);
1119          return;
1120      }
1121  
1122      std::shared_ptr<CertificateFile> certFile =
1123          std::make_shared<CertificateFile>(certHttpBody);
1124  
1125      crow::connections::systemBus->async_method_call(
1126          [asyncResp, certFile](const boost::system::error_code& ec,
1127                                const std::string& objectPath) {
1128              if (ec)
1129              {
1130                  BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
1131                  messages::internalError(asyncResp->res);
1132                  return;
1133              }
1134  
1135              sdbusplus::message::object_path path(objectPath);
1136              std::string certId = path.filename();
1137              const boost::urls::url certURL = boost::urls::format(
1138                  "/redfish/v1/AccountService/LDAP/Certificates/{}", certId);
1139              getCertificateProperties(asyncResp, objectPath,
1140                                       certs::ldapServiceName, certId, certURL,
1141                                       "LDAP Certificate");
1142              BMCWEB_LOG_DEBUG("LDAP certificate install file={}",
1143                               certFile->getCertFilePath());
1144          },
1145          certs::ldapServiceName, certs::ldapObjectPath, certs::certInstallIntf,
1146          "Install", certFile->getCertFilePath());
1147  }
1148  
handleLDAPCertificateGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id)1149  inline void handleLDAPCertificateGet(
1150      App& app, const crow::Request& req,
1151      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1152  {
1153      if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1154      {
1155          return;
1156      }
1157  
1158      BMCWEB_LOG_DEBUG("LDAP Certificate ID={}", id);
1159      const boost::urls::url certURL = boost::urls::format(
1160          "/redfish/v1/AccountService/LDAP/Certificates/{}", id);
1161      std::string objPath =
1162          sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1163      getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id,
1164                               certURL, "LDAP Certificate");
1165  }
1166  
handleLDAPCertificateDelete(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id)1167  inline void handleLDAPCertificateDelete(
1168      App& app, const crow::Request& req,
1169      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1170  {
1171      if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1172      {
1173          return;
1174      }
1175  
1176      BMCWEB_LOG_DEBUG("Delete LDAP Certificate ID={}", id);
1177      std::string objPath =
1178          sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1179  
1180      deleteCertificate(asyncResp, certs::ldapServiceName, objPath);
1181  }
1182  
requestRoutesLDAPCertificate(App & app)1183  inline void requestRoutesLDAPCertificate(App& app)
1184  {
1185      BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1186          .privileges(redfish::privileges::getCertificateCollection)
1187          .methods(boost::beast::http::verb::get)(
1188              std::bind_front(handleLDAPCertificateCollectionGet, std::ref(app)));
1189  
1190      BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1191          .privileges(redfish::privileges::postCertificateCollection)
1192          .methods(boost::beast::http::verb::post)(std::bind_front(
1193              handleLDAPCertificateCollectionPost, std::ref(app)));
1194  
1195      BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
1196          .privileges(redfish::privileges::getCertificate)
1197          .methods(boost::beast::http::verb::get)(
1198              std::bind_front(handleLDAPCertificateGet, std::ref(app)));
1199  
1200      BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
1201          .privileges(redfish::privileges::deleteCertificate)
1202          .methods(boost::beast::http::verb::delete_)(
1203              std::bind_front(handleLDAPCertificateDelete, std::ref(app)));
1204  } // requestRoutesLDAPCertificate
1205  
handleTrustStoreCertificateCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)1206  inline void handleTrustStoreCertificateCollectionGet(
1207      App& app, const crow::Request& req,
1208      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1209      const std::string& managerId)
1210  {
1211      if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1212      {
1213          return;
1214      }
1215  
1216      if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1217      {
1218          messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1219          return;
1220      }
1221  
1222      asyncResp->res.jsonValue["@odata.id"] =
1223          boost::urls::format("/redfish/v1/Managers/{}/Truststore/Certificates/",
1224                              BMCWEB_REDFISH_MANAGER_URI_NAME);
1225      asyncResp->res.jsonValue["@odata.type"] =
1226          "#CertificateCollection.CertificateCollection";
1227      asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1228      asyncResp->res.jsonValue["Description"] =
1229          "A Collection of TrustStore certificate instances";
1230  
1231      getCertificateList(asyncResp, certs::authorityObjectPath,
1232                         "/Members"_json_pointer,
1233                         "/Members@odata.count"_json_pointer);
1234  }
1235  
handleTrustStoreCertificateCollectionPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)1236  inline void handleTrustStoreCertificateCollectionPost(
1237      App& app, const crow::Request& req,
1238      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1239      const std::string& managerId)
1240  {
1241      if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1242      {
1243          return;
1244      }
1245  
1246      if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1247      {
1248          messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1249          return;
1250      }
1251  
1252      std::string certHttpBody = getCertificateFromReqBody(asyncResp, req);
1253  
1254      if (certHttpBody.empty())
1255      {
1256          BMCWEB_LOG_ERROR("Cannot get certificate from request body.");
1257          messages::unrecognizedRequestBody(asyncResp->res);
1258          return;
1259      }
1260  
1261      std::shared_ptr<CertificateFile> certFile =
1262          std::make_shared<CertificateFile>(certHttpBody);
1263      crow::connections::systemBus->async_method_call(
1264          [asyncResp, certFile](const boost::system::error_code& ec,
1265                                const std::string& objectPath) {
1266              if (ec)
1267              {
1268                  BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
1269                  messages::internalError(asyncResp->res);
1270                  return;
1271              }
1272  
1273              sdbusplus::message::object_path path(objectPath);
1274              std::string certId = path.filename();
1275              const boost::urls::url certURL = boost::urls::format(
1276                  "/redfish/v1/Managers/{}/Truststore/Certificates/{}",
1277                  BMCWEB_REDFISH_MANAGER_URI_NAME, certId);
1278              getCertificateProperties(asyncResp, objectPath,
1279                                       certs::authorityServiceName, certId,
1280                                       certURL, "TrustStore Certificate");
1281              BMCWEB_LOG_DEBUG("TrustStore certificate install file={}",
1282                               certFile->getCertFilePath());
1283          },
1284          certs::authorityServiceName, certs::authorityObjectPath,
1285          certs::certInstallIntf, "Install", certFile->getCertFilePath());
1286  }
1287  
handleTrustStoreCertificateGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId,const std::string & certId)1288  inline void handleTrustStoreCertificateGet(
1289      App& app, const crow::Request& req,
1290      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1291      const std::string& managerId, const std::string& certId)
1292  {
1293      if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1294      {
1295          return;
1296      }
1297  
1298      if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1299      {
1300          messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1301          return;
1302      }
1303  
1304      BMCWEB_LOG_DEBUG("Truststore Certificate ID={}", certId);
1305      const boost::urls::url certURL = boost::urls::format(
1306          "/redfish/v1/Managers/{}/Truststore/Certificates/{}",
1307          BMCWEB_REDFISH_MANAGER_URI_NAME, certId);
1308      std::string objPath =
1309          sdbusplus::message::object_path(certs::authorityObjectPath) / certId;
1310      getCertificateProperties(asyncResp, objPath, certs::authorityServiceName,
1311                               certId, certURL, "TrustStore Certificate");
1312  }
1313  
handleTrustStoreCertificateDelete(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId,const std::string & certId)1314  inline void handleTrustStoreCertificateDelete(
1315      App& app, const crow::Request& req,
1316      const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1317      const std::string& managerId, const std::string& certId)
1318  {
1319      if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1320      {
1321          return;
1322      }
1323  
1324      if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1325      {
1326          messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1327          return;
1328      }
1329  
1330      BMCWEB_LOG_DEBUG("Delete TrustStore Certificate ID={}", certId);
1331      std::string objPath =
1332          sdbusplus::message::object_path(certs::authorityObjectPath) / certId;
1333  
1334      deleteCertificate(asyncResp, certs::authorityServiceName, objPath);
1335  }
1336  
requestRoutesTrustStoreCertificate(App & app)1337  inline void requestRoutesTrustStoreCertificate(App& app)
1338  {
1339      BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/Truststore/Certificates/")
1340          .privileges(redfish::privileges::getCertificate)
1341          .methods(boost::beast::http::verb::get)(std::bind_front(
1342              handleTrustStoreCertificateCollectionGet, std::ref(app)));
1343  
1344      BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/Truststore/Certificates/")
1345          .privileges(redfish::privileges::postCertificateCollection)
1346          .methods(boost::beast::http::verb::post)(std::bind_front(
1347              handleTrustStoreCertificateCollectionPost, std::ref(app)));
1348  
1349      BMCWEB_ROUTE(app,
1350                   "/redfish/v1/Managers/<str>/Truststore/Certificates/<str>/")
1351          .privileges(redfish::privileges::getCertificate)
1352          .methods(boost::beast::http::verb::get)(
1353              std::bind_front(handleTrustStoreCertificateGet, std::ref(app)));
1354  
1355      BMCWEB_ROUTE(app,
1356                   "/redfish/v1/Managers/<str>/Truststore/Certificates/<str>/")
1357          .privileges(redfish::privileges::deleteCertificate)
1358          .methods(boost::beast::http::verb::delete_)(
1359              std::bind_front(handleTrustStoreCertificateDelete, std::ref(app)));
1360  } // requestRoutesTrustStoreCertificate
1361  } // namespace redfish
1362