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 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 */
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 static 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 static 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 static 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
handleReplaceCertificateAction(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)468 inline void handleReplaceCertificateAction(
469 App& app, const crow::Request& req,
470 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
471 {
472 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
473 {
474 return;
475 }
476 std::string certificate;
477 std::string certURI;
478 std::optional<std::string> certificateType = "PEM";
479
480 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
481 certificate, "CertificateUri/@odata.id",
482 certURI, "CertificateType", certificateType))
483 {
484 BMCWEB_LOG_ERROR("Required parameters are missing");
485 return;
486 }
487
488 if (!certificateType)
489 {
490 // should never happen, but it never hurts to be paranoid.
491 return;
492 }
493 if (certificateType != "PEM")
494 {
495 messages::actionParameterNotSupported(asyncResp->res, "CertificateType",
496 "ReplaceCertificate");
497 return;
498 }
499
500 BMCWEB_LOG_INFO("Certificate URI to replace: {}", certURI);
501
502 boost::system::result<boost::urls::url> parsedUrl =
503 boost::urls::parse_relative_ref(certURI);
504 if (!parsedUrl)
505 {
506 messages::actionParameterValueFormatError(
507 asyncResp->res, certURI, "CertificateUri", "ReplaceCertificate");
508 return;
509 }
510
511 std::string id;
512 sdbusplus::message::object_path objectPath;
513 std::string name;
514 std::string service;
515 if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1", "Managers",
516 "bmc", "NetworkProtocol", "HTTPS",
517 "Certificates", std::ref(id)))
518 {
519 objectPath = sdbusplus::message::object_path(certs::httpsObjectPath) /
520 id;
521 name = "HTTPS certificate";
522 service = certs::httpsServiceName;
523 }
524 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
525 "AccountService", "LDAP",
526 "Certificates", std::ref(id)))
527 {
528 objectPath = sdbusplus::message::object_path(certs::ldapObjectPath) /
529 id;
530 name = "LDAP certificate";
531 service = certs::ldapServiceName;
532 }
533 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
534 "Managers", "bmc", "Truststore",
535 "Certificates", std::ref(id)))
536 {
537 objectPath =
538 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
539 name = "TrustStore certificate";
540 service = certs::authorityServiceName;
541 }
542 else
543 {
544 messages::actionParameterNotSupported(asyncResp->res, "CertificateUri",
545 "ReplaceCertificate");
546 return;
547 }
548
549 std::shared_ptr<CertificateFile> certFile =
550 std::make_shared<CertificateFile>(certificate);
551 crow::connections::systemBus->async_method_call(
552 [asyncResp, certFile, objectPath, service, url{*parsedUrl}, id,
553 name](const boost::system::error_code& ec) {
554 if (ec)
555 {
556 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
557 if (ec.value() ==
558 boost::system::linux_error::bad_request_descriptor)
559 {
560 messages::resourceNotFound(asyncResp->res, "Certificate",
561 id);
562 return;
563 }
564 messages::internalError(asyncResp->res);
565 return;
566 }
567 getCertificateProperties(asyncResp, objectPath, service, id, url,
568 name);
569 BMCWEB_LOG_DEBUG("HTTPS certificate install file={}",
570 certFile->getCertFilePath());
571 },
572 service, objectPath, certs::certReplaceIntf, "Replace",
573 certFile->getCertFilePath());
574 }
575
576 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
577 static std::unique_ptr<sdbusplus::bus::match_t> csrMatcher;
578 /**
579 * @brief Read data from CSR D-bus object and set to response
580 *
581 * @param[in] asyncResp Shared pointer to the response message
582 * @param[in] certURI Link to certificate collection URI
583 * @param[in] service D-Bus service name
584 * @param[in] certObjPath certificate D-Bus object path
585 * @param[in] csrObjPath CSR D-Bus object path
586 * @return None
587 */
getCSR(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & certURI,const std::string & service,const std::string & certObjPath,const std::string & csrObjPath)588 static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
589 const std::string& certURI, const std::string& service,
590 const std::string& certObjPath,
591 const std::string& csrObjPath)
592 {
593 BMCWEB_LOG_DEBUG("getCSR CertObjectPath{} CSRObjectPath={} service={}",
594 certObjPath, csrObjPath, service);
595 crow::connections::systemBus->async_method_call(
596 [asyncResp,
597 certURI](const boost::system::error_code& ec, const std::string& csr) {
598 if (ec)
599 {
600 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
601 messages::internalError(asyncResp->res);
602 return;
603 }
604 if (csr.empty())
605 {
606 BMCWEB_LOG_ERROR("CSR read is empty");
607 messages::internalError(asyncResp->res);
608 return;
609 }
610 asyncResp->res.jsonValue["CSRString"] = csr;
611 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
612 certURI;
613 },
614 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
615 }
616
617 inline void
handleGenerateCSRAction(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)618 handleGenerateCSRAction(App& app, const crow::Request& req,
619 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
620 {
621 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
622 {
623 return;
624 }
625 static const int rsaKeyBitLength = 2048;
626
627 // Required parameters
628 std::string city;
629 std::string commonName;
630 std::string country;
631 std::string organization;
632 std::string organizationalUnit;
633 std::string state;
634 std::string certURI;
635
636 // Optional parameters
637 std::optional<std::vector<std::string>> optAlternativeNames =
638 std::vector<std::string>();
639 std::optional<std::string> optContactPerson = "";
640 std::optional<std::string> optChallengePassword = "";
641 std::optional<std::string> optEmail = "";
642 std::optional<std::string> optGivenName = "";
643 std::optional<std::string> optInitials = "";
644 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
645 std::optional<std::string> optKeyCurveId = "secp384r1";
646 std::optional<std::string> optKeyPairAlgorithm = "EC";
647 std::optional<std::vector<std::string>> optKeyUsage =
648 std::vector<std::string>();
649 std::optional<std::string> optSurname = "";
650 std::optional<std::string> optUnstructuredName = "";
651 if (!json_util::readJsonAction(
652 req, asyncResp->res, "City", city, "CommonName", commonName,
653 "ContactPerson", optContactPerson, "Country", country,
654 "Organization", organization, "OrganizationalUnit",
655 organizationalUnit, "State", state,
656 "CertificateCollection/@odata.id", certURI, "AlternativeNames",
657 optAlternativeNames, "ChallengePassword", optChallengePassword,
658 "Email", optEmail, "GivenName", optGivenName, "Initials",
659 optInitials, "KeyBitLength", optKeyBitLength, "KeyCurveId",
660 optKeyCurveId, "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
661 optKeyUsage, "Surname", optSurname, "UnstructuredName",
662 optUnstructuredName))
663 {
664 return;
665 }
666
667 // bmcweb has no way to store or decode a private key challenge
668 // password, which will likely cause bmcweb to crash on startup
669 // if this is not set on a post so not allowing the user to set
670 // value
671 if (!optChallengePassword->empty())
672 {
673 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
674 "ChallengePassword");
675 return;
676 }
677
678 std::string objectPath;
679 std::string service;
680 if (certURI.starts_with(std::format(
681 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates",
682 BMCWEB_REDFISH_MANAGER_URI_NAME)))
683 {
684 objectPath = certs::httpsObjectPath;
685 service = certs::httpsServiceName;
686 }
687 else if (certURI.starts_with(
688 "/redfish/v1/AccountService/LDAP/Certificates"))
689 {
690 objectPath = certs::ldapObjectPath;
691 service = certs::ldapServiceName;
692 }
693 else
694 {
695 messages::actionParameterNotSupported(
696 asyncResp->res, "CertificateCollection", "GenerateCSR");
697 return;
698 }
699
700 // supporting only EC and RSA algorithm
701 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
702 {
703 messages::actionParameterNotSupported(
704 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
705 return;
706 }
707
708 // supporting only 2048 key bit length for RSA algorithm due to
709 // time consumed in generating private key
710 if (*optKeyPairAlgorithm == "RSA" && *optKeyBitLength != rsaKeyBitLength)
711 {
712 messages::propertyValueNotInList(asyncResp->res, *optKeyBitLength,
713 "KeyBitLength");
714 return;
715 }
716
717 // validate KeyUsage supporting only 1 type based on URL
718 if (certURI.starts_with(std::format(
719 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates",
720 BMCWEB_REDFISH_MANAGER_URI_NAME)))
721 {
722 if (optKeyUsage->empty())
723 {
724 optKeyUsage->emplace_back("ServerAuthentication");
725 }
726 else if (optKeyUsage->size() == 1)
727 {
728 if ((*optKeyUsage)[0] != "ServerAuthentication")
729 {
730 messages::propertyValueNotInList(asyncResp->res,
731 (*optKeyUsage)[0], "KeyUsage");
732 return;
733 }
734 }
735 else
736 {
737 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage",
738 "GenerateCSR");
739 return;
740 }
741 }
742 else if (certURI.starts_with(
743 "/redfish/v1/AccountService/LDAP/Certificates"))
744 {
745 if (optKeyUsage->empty())
746 {
747 optKeyUsage->emplace_back("ClientAuthentication");
748 }
749 else if (optKeyUsage->size() == 1)
750 {
751 if ((*optKeyUsage)[0] != "ClientAuthentication")
752 {
753 messages::propertyValueNotInList(asyncResp->res,
754 (*optKeyUsage)[0], "KeyUsage");
755 return;
756 }
757 }
758 else
759 {
760 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage",
761 "GenerateCSR");
762 return;
763 }
764 }
765
766 // Only allow one CSR matcher at a time so setting retry
767 // time-out and timer expiry to 10 seconds for now.
768 static const int timeOut = 10;
769 if (csrMatcher)
770 {
771 messages::serviceTemporarilyUnavailable(asyncResp->res,
772 std::to_string(timeOut));
773 return;
774 }
775
776 if (req.ioService == nullptr)
777 {
778 messages::internalError(asyncResp->res);
779 return;
780 }
781
782 // Make this static so it survives outside this method
783 static boost::asio::steady_timer timeout(*req.ioService);
784 timeout.expires_after(std::chrono::seconds(timeOut));
785 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
786 csrMatcher = nullptr;
787 if (ec)
788 {
789 // operation_aborted is expected if timer is canceled
790 // before completion.
791 if (ec != boost::asio::error::operation_aborted)
792 {
793 BMCWEB_LOG_ERROR("Async_wait failed {}", ec);
794 }
795 return;
796 }
797 BMCWEB_LOG_ERROR("Timed out waiting for Generating CSR");
798 messages::internalError(asyncResp->res);
799 });
800
801 // create a matcher to wait on CSR object
802 BMCWEB_LOG_DEBUG("create matcher with path {}", objectPath);
803 std::string match("type='signal',"
804 "interface='org.freedesktop.DBus.ObjectManager',"
805 "path='" +
806 objectPath +
807 "',"
808 "member='InterfacesAdded'");
809 csrMatcher = std::make_unique<sdbusplus::bus::match_t>(
810 *crow::connections::systemBus, match,
811 [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) {
812 timeout.cancel();
813 if (m.is_method_error())
814 {
815 BMCWEB_LOG_ERROR("Dbus method error!!!");
816 messages::internalError(asyncResp->res);
817 return;
818 }
819
820 dbus::utility::DBusInterfacesMap interfacesProperties;
821
822 sdbusplus::message::object_path csrObjectPath;
823 m.read(csrObjectPath, interfacesProperties);
824 BMCWEB_LOG_DEBUG("CSR object added{}", csrObjectPath.str);
825 for (const auto& interface : interfacesProperties)
826 {
827 if (interface.first == "xyz.openbmc_project.Certs.CSR")
828 {
829 getCSR(asyncResp, certURI, service, objectPath,
830 csrObjectPath.str);
831 break;
832 }
833 }
834 });
835 crow::connections::systemBus->async_method_call(
836 [asyncResp](const boost::system::error_code& ec, const std::string&) {
837 if (ec)
838 {
839 BMCWEB_LOG_ERROR("DBUS response error: {}", ec.message());
840 messages::internalError(asyncResp->res);
841 return;
842 }
843 },
844 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
845 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
846 commonName, *optContactPerson, country, *optEmail, *optGivenName,
847 *optInitials, *optKeyBitLength, *optKeyCurveId, *optKeyPairAlgorithm,
848 *optKeyUsage, organization, organizationalUnit, state, *optSurname,
849 *optUnstructuredName);
850 }
851
requestRoutesCertificateService(App & app)852 inline void requestRoutesCertificateService(App& app)
853 {
854 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
855 .privileges(redfish::privileges::getCertificateService)
856 .methods(boost::beast::http::verb::get)(
857 std::bind_front(handleCertificateServiceGet, std::ref(app)));
858
859 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
860 .privileges(redfish::privileges::getCertificateLocations)
861 .methods(boost::beast::http::verb::get)(
862 std::bind_front(handleCertificateLocationsGet, std::ref(app)));
863
864 BMCWEB_ROUTE(
865 app,
866 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
867 .privileges(redfish::privileges::postCertificateService)
868 .methods(boost::beast::http::verb::post)(
869 std::bind_front(handleReplaceCertificateAction, std::ref(app)));
870
871 BMCWEB_ROUTE(
872 app,
873 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
874 .privileges(redfish::privileges::postCertificateService)
875 .methods(boost::beast::http::verb::post)(
876 std::bind_front(handleGenerateCSRAction, std::ref(app)));
877 } // requestRoutesCertificateService
878
handleHTTPSCertificateCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)879 inline void handleHTTPSCertificateCollectionGet(
880 App& app, const crow::Request& req,
881 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
882 const std::string& managerId)
883 {
884 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
885 {
886 return;
887 }
888
889 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
890 {
891 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
892 return;
893 }
894
895 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
896 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates",
897 BMCWEB_REDFISH_MANAGER_URI_NAME);
898 asyncResp->res.jsonValue["@odata.type"] =
899 "#CertificateCollection.CertificateCollection";
900 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
901 asyncResp->res.jsonValue["Description"] =
902 "A Collection of HTTPS certificate instances";
903
904 getCertificateList(asyncResp, certs::httpsObjectPath,
905 "/Members"_json_pointer,
906 "/Members@odata.count"_json_pointer);
907 }
908
handleHTTPSCertificateCollectionPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)909 inline void handleHTTPSCertificateCollectionPost(
910 App& app, const crow::Request& req,
911 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
912 const std::string& managerId)
913 {
914 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
915 {
916 return;
917 }
918
919 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
920 {
921 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
922 return;
923 }
924
925 BMCWEB_LOG_DEBUG("HTTPSCertificateCollection::doPost");
926
927 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
928 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
929
930 std::string certHttpBody = getCertificateFromReqBody(asyncResp, req);
931
932 if (certHttpBody.empty())
933 {
934 BMCWEB_LOG_ERROR("Cannot get certificate from request body.");
935 messages::unrecognizedRequestBody(asyncResp->res);
936 return;
937 }
938
939 std::shared_ptr<CertificateFile> certFile =
940 std::make_shared<CertificateFile>(certHttpBody);
941
942 crow::connections::systemBus->async_method_call(
943 [asyncResp, certFile](const boost::system::error_code& ec,
944 const std::string& objectPath) {
945 if (ec)
946 {
947 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
948 messages::internalError(asyncResp->res);
949 return;
950 }
951
952 sdbusplus::message::object_path path(objectPath);
953 std::string certId = path.filename();
954 const boost::urls::url certURL = boost::urls::format(
955 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates/{}",
956 BMCWEB_REDFISH_MANAGER_URI_NAME, certId);
957 getCertificateProperties(asyncResp, objectPath,
958 certs::httpsServiceName, certId, certURL,
959 "HTTPS Certificate");
960 BMCWEB_LOG_DEBUG("HTTPS certificate install file={}",
961 certFile->getCertFilePath());
962 },
963 certs::httpsServiceName, certs::httpsObjectPath, certs::certInstallIntf,
964 "Install", certFile->getCertFilePath());
965 }
966
handleHTTPSCertificateGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId,const std::string & certId)967 inline void handleHTTPSCertificateGet(
968 App& app, const crow::Request& req,
969 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
970 const std::string& managerId, const std::string& certId)
971 {
972 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
973 {
974 return;
975 }
976
977 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
978 {
979 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
980 return;
981 }
982
983 BMCWEB_LOG_DEBUG("HTTPS Certificate ID={}", certId);
984 const boost::urls::url certURL = boost::urls::format(
985 "/redfish/v1/Managers/{}/NetworkProtocol/HTTPS/Certificates/{}",
986 BMCWEB_REDFISH_MANAGER_URI_NAME, certId);
987 std::string objPath =
988 sdbusplus::message::object_path(certs::httpsObjectPath) / certId;
989 getCertificateProperties(asyncResp, objPath, certs::httpsServiceName,
990 certId, certURL, "HTTPS Certificate");
991 }
992
requestRoutesHTTPSCertificate(App & app)993 inline void requestRoutesHTTPSCertificate(App& app)
994 {
995 BMCWEB_ROUTE(
996 app, "/redfish/v1/Managers/<str>/NetworkProtocol/HTTPS/Certificates/")
997 .privileges(redfish::privileges::getCertificateCollection)
998 .methods(boost::beast::http::verb::get)(std::bind_front(
999 handleHTTPSCertificateCollectionGet, std::ref(app)));
1000
1001 BMCWEB_ROUTE(
1002 app, "/redfish/v1/Managers/<str>/NetworkProtocol/HTTPS/Certificates/")
1003 .privileges(redfish::privileges::postCertificateCollection)
1004 .methods(boost::beast::http::verb::post)(std::bind_front(
1005 handleHTTPSCertificateCollectionPost, std::ref(app)));
1006
1007 BMCWEB_ROUTE(
1008 app,
1009 "/redfish/v1/Managers/<str>/NetworkProtocol/HTTPS/Certificates/<str>/")
1010 .privileges(redfish::privileges::getCertificate)
1011 .methods(boost::beast::http::verb::get)(
1012 std::bind_front(handleHTTPSCertificateGet, std::ref(app)));
1013 }
1014
handleLDAPCertificateCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1015 inline void handleLDAPCertificateCollectionGet(
1016 App& app, const crow::Request& req,
1017 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1018 {
1019 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1020 {
1021 return;
1022 }
1023
1024 asyncResp->res.jsonValue["@odata.id"] =
1025 "/redfish/v1/AccountService/LDAP/Certificates";
1026 asyncResp->res.jsonValue["@odata.type"] =
1027 "#CertificateCollection.CertificateCollection";
1028 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1029 asyncResp->res.jsonValue["Description"] =
1030 "A Collection of LDAP certificate instances";
1031
1032 getCertificateList(asyncResp, certs::ldapObjectPath,
1033 "/Members"_json_pointer,
1034 "/Members@odata.count"_json_pointer);
1035 }
1036
handleLDAPCertificateCollectionPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1037 inline void handleLDAPCertificateCollectionPost(
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 std::string certHttpBody = getCertificateFromReqBody(asyncResp, req);
1046
1047 if (certHttpBody.empty())
1048 {
1049 BMCWEB_LOG_ERROR("Cannot get certificate from request body.");
1050 messages::unrecognizedRequestBody(asyncResp->res);
1051 return;
1052 }
1053
1054 std::shared_ptr<CertificateFile> certFile =
1055 std::make_shared<CertificateFile>(certHttpBody);
1056
1057 crow::connections::systemBus->async_method_call(
1058 [asyncResp, certFile](const boost::system::error_code& ec,
1059 const std::string& objectPath) {
1060 if (ec)
1061 {
1062 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
1063 messages::internalError(asyncResp->res);
1064 return;
1065 }
1066
1067 sdbusplus::message::object_path path(objectPath);
1068 std::string certId = path.filename();
1069 const boost::urls::url certURL = boost::urls::format(
1070 "/redfish/v1/AccountService/LDAP/Certificates/{}", certId);
1071 getCertificateProperties(asyncResp, objectPath,
1072 certs::ldapServiceName, certId, certURL,
1073 "LDAP Certificate");
1074 BMCWEB_LOG_DEBUG("LDAP certificate install file={}",
1075 certFile->getCertFilePath());
1076 },
1077 certs::ldapServiceName, certs::ldapObjectPath, certs::certInstallIntf,
1078 "Install", certFile->getCertFilePath());
1079 }
1080
handleLDAPCertificateGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id)1081 inline void handleLDAPCertificateGet(
1082 App& app, const crow::Request& req,
1083 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1084 {
1085 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1086 {
1087 return;
1088 }
1089
1090 BMCWEB_LOG_DEBUG("LDAP Certificate ID={}", id);
1091 const boost::urls::url certURL = boost::urls::format(
1092 "/redfish/v1/AccountService/LDAP/Certificates/{}", id);
1093 std::string objPath =
1094 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1095 getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id,
1096 certURL, "LDAP Certificate");
1097 }
1098
handleLDAPCertificateDelete(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id)1099 inline void handleLDAPCertificateDelete(
1100 App& app, const crow::Request& req,
1101 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1102 {
1103 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1104 {
1105 return;
1106 }
1107
1108 BMCWEB_LOG_DEBUG("Delete LDAP Certificate ID={}", id);
1109 std::string objPath =
1110 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1111
1112 deleteCertificate(asyncResp, certs::ldapServiceName, objPath);
1113 }
1114
requestRoutesLDAPCertificate(App & app)1115 inline void requestRoutesLDAPCertificate(App& app)
1116 {
1117 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1118 .privileges(redfish::privileges::getCertificateCollection)
1119 .methods(boost::beast::http::verb::get)(
1120 std::bind_front(handleLDAPCertificateCollectionGet, std::ref(app)));
1121
1122 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1123 .privileges(redfish::privileges::postCertificateCollection)
1124 .methods(boost::beast::http::verb::post)(std::bind_front(
1125 handleLDAPCertificateCollectionPost, std::ref(app)));
1126
1127 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
1128 .privileges(redfish::privileges::getCertificate)
1129 .methods(boost::beast::http::verb::get)(
1130 std::bind_front(handleLDAPCertificateGet, std::ref(app)));
1131
1132 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
1133 .privileges(redfish::privileges::deleteCertificate)
1134 .methods(boost::beast::http::verb::delete_)(
1135 std::bind_front(handleLDAPCertificateDelete, std::ref(app)));
1136 } // requestRoutesLDAPCertificate
1137
handleTrustStoreCertificateCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)1138 inline void handleTrustStoreCertificateCollectionGet(
1139 App& app, const crow::Request& req,
1140 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1141 const std::string& managerId)
1142 {
1143 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1144 {
1145 return;
1146 }
1147
1148 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1149 {
1150 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1151 return;
1152 }
1153
1154 asyncResp->res.jsonValue["@odata.id"] =
1155 boost::urls::format("/redfish/v1/Managers/{}/Truststore/Certificates/",
1156 BMCWEB_REDFISH_MANAGER_URI_NAME);
1157 asyncResp->res.jsonValue["@odata.type"] =
1158 "#CertificateCollection.CertificateCollection";
1159 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1160 asyncResp->res.jsonValue["Description"] =
1161 "A Collection of TrustStore certificate instances";
1162
1163 getCertificateList(asyncResp, certs::authorityObjectPath,
1164 "/Members"_json_pointer,
1165 "/Members@odata.count"_json_pointer);
1166 }
1167
handleTrustStoreCertificateCollectionPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)1168 inline void handleTrustStoreCertificateCollectionPost(
1169 App& app, const crow::Request& req,
1170 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1171 const std::string& managerId)
1172 {
1173 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1174 {
1175 return;
1176 }
1177
1178 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1179 {
1180 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1181 return;
1182 }
1183
1184 std::string certHttpBody = getCertificateFromReqBody(asyncResp, req);
1185
1186 if (certHttpBody.empty())
1187 {
1188 BMCWEB_LOG_ERROR("Cannot get certificate from request body.");
1189 messages::unrecognizedRequestBody(asyncResp->res);
1190 return;
1191 }
1192
1193 std::shared_ptr<CertificateFile> certFile =
1194 std::make_shared<CertificateFile>(certHttpBody);
1195 crow::connections::systemBus->async_method_call(
1196 [asyncResp, certFile](const boost::system::error_code& ec,
1197 const std::string& objectPath) {
1198 if (ec)
1199 {
1200 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
1201 messages::internalError(asyncResp->res);
1202 return;
1203 }
1204
1205 sdbusplus::message::object_path path(objectPath);
1206 std::string certId = path.filename();
1207 const boost::urls::url certURL = boost::urls::format(
1208 "/redfish/v1/Managers/{}/Truststore/Certificates/{}",
1209 BMCWEB_REDFISH_MANAGER_URI_NAME, certId);
1210 getCertificateProperties(asyncResp, objectPath,
1211 certs::authorityServiceName, certId,
1212 certURL, "TrustStore Certificate");
1213 BMCWEB_LOG_DEBUG("TrustStore certificate install file={}",
1214 certFile->getCertFilePath());
1215 },
1216 certs::authorityServiceName, certs::authorityObjectPath,
1217 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1218 }
1219
handleTrustStoreCertificateGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId,const std::string & certId)1220 inline void handleTrustStoreCertificateGet(
1221 App& app, const crow::Request& req,
1222 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1223 const std::string& managerId, const std::string& certId)
1224 {
1225 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1226 {
1227 return;
1228 }
1229
1230 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1231 {
1232 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1233 return;
1234 }
1235
1236 BMCWEB_LOG_DEBUG("Truststore Certificate ID={}", certId);
1237 const boost::urls::url certURL = boost::urls::format(
1238 "/redfish/v1/Managers/{}/Truststore/Certificates/{}",
1239 BMCWEB_REDFISH_MANAGER_URI_NAME, certId);
1240 std::string objPath =
1241 sdbusplus::message::object_path(certs::authorityObjectPath) / certId;
1242 getCertificateProperties(asyncResp, objPath, certs::authorityServiceName,
1243 certId, certURL, "TrustStore Certificate");
1244 }
1245
handleTrustStoreCertificateDelete(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId,const std::string & certId)1246 inline void handleTrustStoreCertificateDelete(
1247 App& app, const crow::Request& req,
1248 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1249 const std::string& managerId, const std::string& certId)
1250 {
1251 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1252 {
1253 return;
1254 }
1255
1256 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1257 {
1258 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1259 return;
1260 }
1261
1262 BMCWEB_LOG_DEBUG("Delete TrustStore Certificate ID={}", certId);
1263 std::string objPath =
1264 sdbusplus::message::object_path(certs::authorityObjectPath) / certId;
1265
1266 deleteCertificate(asyncResp, certs::authorityServiceName, objPath);
1267 }
1268
requestRoutesTrustStoreCertificate(App & app)1269 inline void requestRoutesTrustStoreCertificate(App& app)
1270 {
1271 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/Truststore/Certificates/")
1272 .privileges(redfish::privileges::getCertificate)
1273 .methods(boost::beast::http::verb::get)(std::bind_front(
1274 handleTrustStoreCertificateCollectionGet, std::ref(app)));
1275
1276 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/Truststore/Certificates/")
1277 .privileges(redfish::privileges::postCertificateCollection)
1278 .methods(boost::beast::http::verb::post)(std::bind_front(
1279 handleTrustStoreCertificateCollectionPost, std::ref(app)));
1280
1281 BMCWEB_ROUTE(app,
1282 "/redfish/v1/Managers/<str>/Truststore/Certificates/<str>/")
1283 .privileges(redfish::privileges::getCertificate)
1284 .methods(boost::beast::http::verb::get)(
1285 std::bind_front(handleTrustStoreCertificateGet, std::ref(app)));
1286
1287 BMCWEB_ROUTE(app,
1288 "/redfish/v1/Managers/<str>/Truststore/Certificates/<str>/")
1289 .privileges(redfish::privileges::deleteCertificate)
1290 .methods(boost::beast::http::verb::delete_)(
1291 std::bind_front(handleTrustStoreCertificateDelete, std::ref(app)));
1292 } // requestRoutesTrustStoreCertificate
1293 } // namespace redfish
1294