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