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