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