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