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