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