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