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