blob: 65f526207780fd4b6b11c75da6e1b221e095fa2e [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001#pragma once
2
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "app.hpp"
4#include "async_resp.hpp"
George Liu7a1dbc42022-12-07 16:03:22 +08005#include "dbus_utility.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08006#include "http_response.hpp"
7#include "query.hpp"
8#include "registries/privilege_registry.hpp"
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +02009#include "utils/dbus_utils.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080010#include "utils/json_utils.hpp"
11#include "utils/time_utils.hpp"
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +020012
Jiaqing Zhao90d2d1e2022-04-13 17:01:57 +080013#include <boost/system/linux_error.hpp>
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +020014#include <sdbusplus/asio/property.hpp>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080015#include <sdbusplus/bus/match.hpp>
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +020016#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050017
George Liu7a1dbc42022-12-07 16:03:22 +080018#include <array>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080019#include <memory>
George Liu7a1dbc42022-12-07 16:03:22 +080020#include <string_view>
21
Marri Devender Rao5968cae2019-01-21 10:27:12 -060022namespace redfish
23{
24namespace certs
25{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
27constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
28constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
29constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
30constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
31constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050032constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060033 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050034constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060035 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050036constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050037 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +080038constexpr char const* baseObjectPath = "/xyz/openbmc_project/certs";
39constexpr char const* httpsObjectPath =
40 "/xyz/openbmc_project/certs/server/https";
41constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050042constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050043 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060044} // namespace certs
45
46/**
47 * The Certificate schema defines a Certificate Service which represents the
48 * actions available to manage certificates and links to where certificates
49 * are installed.
50 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060051
zhanghch058d1b46d2021-04-01 11:18:24 +080052inline std::string getCertificateFromReqBody(
53 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
54 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020055{
56 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
57
58 if (reqJson.is_discarded())
59 {
60 // We did not receive JSON request, proceed as it is RAW data
61 return req.body;
62 }
63
64 std::string certificate;
65 std::optional<std::string> certificateType = "PEM";
66
Willy Tu15ed6782021-12-14 11:03:16 -080067 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
68 certificate, "CertificateType",
69 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020070 {
71 BMCWEB_LOG_ERROR << "Required parameters are missing";
72 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -070073 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020074 }
75
76 if (*certificateType != "PEM")
77 {
78 messages::propertyValueNotInList(asyncResp->res, *certificateType,
79 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -070080 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020081 }
82
83 return certificate;
84}
85
Marri Devender Rao5968cae2019-01-21 10:27:12 -060086/**
87 * Class to create a temporary certificate file for uploading to system
88 */
89class CertificateFile
90{
91 public:
92 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050093 CertificateFile(const CertificateFile&) = delete;
94 CertificateFile& operator=(const CertificateFile&) = delete;
95 CertificateFile(CertificateFile&&) = delete;
96 CertificateFile& operator=(CertificateFile&&) = delete;
Ed Tanous4e23a442022-06-06 09:57:26 -070097 explicit CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060098 {
Ed Tanous72d52d22020-10-12 07:46:27 -070099 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700100 'e', 'r', 't', 's', '.', 'X',
101 'X', 'X', 'X', 'X', 'X', '\0'};
102 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800103 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600104 {
105 certDirectory = tempDirectory;
106 certificateFile = certDirectory / "cert.pem";
107 std::ofstream out(certificateFile, std::ofstream::out |
108 std::ofstream::binary |
109 std::ofstream::trunc);
110 out << certString;
111 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800112 BMCWEB_LOG_DEBUG << "Creating certificate file"
113 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600114 }
115 }
116 ~CertificateFile()
117 {
118 if (std::filesystem::exists(certDirectory))
119 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800120 BMCWEB_LOG_DEBUG << "Removing certificate file"
121 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000122 std::error_code ec;
123 std::filesystem::remove_all(certDirectory, ec);
124 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600125 {
126 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800127 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600128 }
129 }
130 }
131 std::string getCertFilePath()
132 {
133 return certificateFile;
134 }
135
136 private:
137 std::filesystem::path certificateFile;
138 std::filesystem::path certDirectory;
139};
140
141/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500142 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600143 *
144 * @param[in] asyncResp Shared pointer to the response message
145 * @param[in] str Issuer/Subject value in key=value pairs
146 * @param[in] type Issuer/Subject
147 * @return None
148 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500149static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600150 const std::string_view value)
151{
152 // example: O=openbmc-project.xyz,CN=localhost
153 std::string_view::iterator i = value.begin();
154 while (i != value.end())
155 {
156 std::string_view::iterator tokenBegin = i;
157 while (i != value.end() && *i != '=')
158 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530159 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600160 }
161 if (i == value.end())
162 {
163 break;
164 }
Ed Tanous271584a2019-07-09 16:24:22 -0700165 const std::string_view key(tokenBegin,
166 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530167 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600168 tokenBegin = i;
169 while (i != value.end() && *i != ',')
170 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530171 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600172 }
Ed Tanous271584a2019-07-09 16:24:22 -0700173 const std::string_view val(tokenBegin,
174 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600175 if (key == "L")
176 {
177 out["City"] = val;
178 }
179 else if (key == "CN")
180 {
181 out["CommonName"] = val;
182 }
183 else if (key == "C")
184 {
185 out["Country"] = val;
186 }
187 else if (key == "O")
188 {
189 out["Organization"] = val;
190 }
191 else if (key == "OU")
192 {
193 out["OrganizationalUnit"] = val;
194 }
195 else if (key == "ST")
196 {
197 out["State"] = val;
198 }
199 // skip comma character
200 if (i != value.end())
201 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530202 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600203 }
204 }
205}
206
207/**
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800208 * @brief Retrieve the installed certificate list
209 *
210 * @param[in] asyncResp Shared pointer to the response message
211 * @param[in] basePath DBus object path to search
212 * @param[in] listPtr Json pointer to the list in asyncResp
213 * @param[in] countPtr Json pointer to the count in asyncResp
214 * @return None
215 */
216static void
217 getCertificateList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
218 const std::string& basePath,
219 const nlohmann::json::json_pointer& listPtr,
220 const nlohmann::json::json_pointer& countPtr)
221{
George Liu7a1dbc42022-12-07 16:03:22 +0800222 constexpr std::array<std::string_view, 1> interfaces = {
223 certs::certPropIntf};
224 dbus::utility::getSubTreePaths(
225 basePath, 0, interfaces,
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800226 [asyncResp, listPtr, countPtr](
George Liu7a1dbc42022-12-07 16:03:22 +0800227 const boost::system::error_code& ec,
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800228 const dbus::utility::MapperGetSubTreePathsResponse& certPaths) {
229 if (ec)
230 {
231 BMCWEB_LOG_ERROR << "Certificate collection query failed: " << ec;
232 messages::internalError(asyncResp->res);
233 return;
234 }
235
236 nlohmann::json& links = asyncResp->res.jsonValue[listPtr];
237 links = nlohmann::json::array();
238 for (const auto& certPath : certPaths)
239 {
240 sdbusplus::message::object_path objPath(certPath);
241 std::string certId = objPath.filename();
242 if (certId.empty())
243 {
244 BMCWEB_LOG_ERROR << "Invalid certificate objPath " << certPath;
245 continue;
246 }
247
248 boost::urls::url certURL;
249 if (objPath.parent_path() == certs::httpsObjectPath)
250 {
251 certURL = crow::utility::urlFromPieces(
252 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
253 "HTTPS", "Certificates", certId);
254 }
255 else if (objPath.parent_path() == certs::ldapObjectPath)
256 {
257 certURL = crow::utility::urlFromPieces("redfish", "v1",
258 "AccountService", "LDAP",
259 "Certificates", certId);
260 }
261 else if (objPath.parent_path() == certs::authorityObjectPath)
262 {
263 certURL = crow::utility::urlFromPieces(
264 "redfish", "v1", "Managers", "bmc", "Truststore",
265 "Certificates", certId);
266 }
267 else
268 {
269 continue;
270 }
271
272 nlohmann::json::object_t link;
273 link["@odata.id"] = certURL;
274 links.emplace_back(std::move(link));
275 }
276
277 asyncResp->res.jsonValue[countPtr] = links.size();
George Liu7a1dbc42022-12-07 16:03:22 +0800278 });
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800279}
280
281/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600282 * @brief Retrieve the certificates properties and append to the response
283 * message
284 *
285 * @param[in] asyncResp Shared pointer to the response message
286 * @param[in] objectPath Path of the D-Bus service object
287 * @param[in] certId Id of the certificate
288 * @param[in] certURL URL of the certificate object
289 * @param[in] name name of the certificate
290 * @return None
291 */
292static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800293 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800294 const std::string& objectPath, const std::string& service,
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800295 const std::string& certId, const boost::urls::url& certURL,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800296 const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600297{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600298 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
299 << " certId=" << certId << " certURl=" << certURL;
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200300 sdbusplus::asio::getAllProperties(
301 *crow::connections::systemBus, service, objectPath, certs::certPropIntf,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800302 [asyncResp, certURL, certId,
303 name](const boost::system::error_code ec,
304 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700305 if (ec)
306 {
307 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800308 messages::resourceNotFound(asyncResp->res, "Certificate", certId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700309 return;
310 }
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200311
312 const std::string* certificateString = nullptr;
313 const std::vector<std::string>* keyUsage = nullptr;
314 const std::string* issuer = nullptr;
315 const std::string* subject = nullptr;
316 const uint64_t* validNotAfter = nullptr;
317 const uint64_t* validNotBefore = nullptr;
318
319 const bool success = sdbusplus::unpackPropertiesNoThrow(
320 dbus_utils::UnpackErrorPrinter(), properties, "CertificateString",
321 certificateString, "KeyUsage", keyUsage, "Issuer", issuer,
322 "Subject", subject, "ValidNotAfter", validNotAfter,
323 "ValidNotBefore", validNotBefore);
324
325 if (!success)
326 {
327 messages::internalError(asyncResp->res);
328 return;
329 }
330
Ed Tanous002d39b2022-05-31 08:59:27 -0700331 asyncResp->res.jsonValue["@odata.id"] = certURL;
332 asyncResp->res.jsonValue["@odata.type"] =
333 "#Certificate.v1_0_0.Certificate";
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800334 asyncResp->res.jsonValue["Id"] = certId;
Ed Tanous002d39b2022-05-31 08:59:27 -0700335 asyncResp->res.jsonValue["Name"] = name;
336 asyncResp->res.jsonValue["Description"] = name;
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200337 asyncResp->res.jsonValue["CertificateString"] = "";
338 asyncResp->res.jsonValue["KeyUsage"] = nlohmann::json::array();
339
340 if (certificateString != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -0700341 {
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200342 asyncResp->res.jsonValue["CertificateString"] = *certificateString;
Ed Tanous002d39b2022-05-31 08:59:27 -0700343 }
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200344
345 if (keyUsage != nullptr)
346 {
347 asyncResp->res.jsonValue["KeyUsage"] = *keyUsage;
348 }
349
350 if (issuer != nullptr)
351 {
352 updateCertIssuerOrSubject(asyncResp->res.jsonValue["Issuer"],
353 *issuer);
354 }
355
356 if (subject != nullptr)
357 {
358 updateCertIssuerOrSubject(asyncResp->res.jsonValue["Subject"],
359 *subject);
360 }
361
362 if (validNotAfter != nullptr)
363 {
364 asyncResp->res.jsonValue["ValidNotAfter"] =
365 redfish::time_utils::getDateTimeUint(*validNotAfter);
366 }
367
368 if (validNotBefore != nullptr)
369 {
370 asyncResp->res.jsonValue["ValidNotBefore"] =
371 redfish::time_utils::getDateTimeUint(*validNotBefore);
372 }
373
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800374 asyncResp->res.addHeader(
Ed Tanousd9f6c622022-03-17 09:12:17 -0700375 boost::beast::http::field::location,
376 std::string_view(certURL.data(), certURL.size()));
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200377 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600378}
379
Jiaqing Zhao7a3a8f72022-09-29 15:15:58 +0800380static void
381 deleteCertificate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
382 const std::string& service,
383 const sdbusplus::message::object_path& objectPath)
384{
385 crow::connections::systemBus->async_method_call(
386 [asyncResp,
387 id{objectPath.filename()}](const boost::system::error_code ec) {
388 if (ec)
389 {
390 messages::resourceNotFound(asyncResp->res, "Certificate", id);
391 return;
392 }
393 BMCWEB_LOG_INFO << "Certificate deleted";
394 asyncResp->res.result(boost::beast::http::status::no_content);
395 },
396 service, objectPath, certs::objDeleteIntf, "Delete");
397}
398
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800399inline void handleCertificateServiceGet(
400 App& app, const crow::Request& req,
401 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600402{
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800403 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
404 {
405 return;
406 }
407
408 asyncResp->res.jsonValue["@odata.type"] =
409 "#CertificateService.v1_0_0.CertificateService";
410 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/CertificateService";
411 asyncResp->res.jsonValue["Id"] = "CertificateService";
412 asyncResp->res.jsonValue["Name"] = "Certificate Service";
413 asyncResp->res.jsonValue["Description"] =
414 "Actions available to manage certificates";
415 // /redfish/v1/CertificateService/CertificateLocations is something
416 // only ConfigureManager can access then only display when the user
417 // has permissions ConfigureManager
418 Privileges effectiveUserPrivileges =
419 redfish::getUserPrivileges(req.userRole);
420 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
421 effectiveUserPrivileges))
422 {
423 asyncResp->res.jsonValue["CertificateLocations"]["@odata.id"] =
424 "/redfish/v1/CertificateService/CertificateLocations";
425 }
426 nlohmann::json& actions = asyncResp->res.jsonValue["Actions"];
427 nlohmann::json& replace = actions["#CertificateService.ReplaceCertificate"];
428 replace["target"] =
429 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate";
430 nlohmann::json::array_t allowed;
431 allowed.push_back("PEM");
432 replace["CertificateType@Redfish.AllowableValues"] = std::move(allowed);
433 actions["#CertificateService.GenerateCSR"]["target"] =
434 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR";
435}
436
437inline void handleCertificateLocationsGet(
438 App& app, const crow::Request& req,
439 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
440{
441 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
442 {
443 return;
444 }
445 asyncResp->res.jsonValue["@odata.id"] =
446 "/redfish/v1/CertificateService/CertificateLocations";
447 asyncResp->res.jsonValue["@odata.type"] =
448 "#CertificateLocations.v1_0_0.CertificateLocations";
449 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
450 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
451 asyncResp->res.jsonValue["Description"] =
452 "Defines a resource that an administrator can use in order to "
453 "locate all certificates installed on a given service";
454
455 getCertificateList(asyncResp, certs::baseObjectPath,
456 "/Links/Certificates"_json_pointer,
457 "/Links/Certificates@odata.count"_json_pointer);
458}
459
460inline void handleReplaceCertificateAction(
461 App& app, const crow::Request& req,
462 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
463{
464 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
465 {
466 return;
467 }
468 std::string certificate;
469 nlohmann::json certificateUri;
470 std::optional<std::string> certificateType = "PEM";
471
472 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
473 certificate, "CertificateUri",
474 certificateUri, "CertificateType",
475 certificateType))
476 {
477 BMCWEB_LOG_ERROR << "Required parameters are missing";
478 messages::internalError(asyncResp->res);
479 return;
480 }
481
482 if (!certificateType)
483 {
484 // should never happen, but it never hurts to be paranoid.
485 return;
486 }
487 if (certificateType != "PEM")
488 {
489 messages::actionParameterNotSupported(asyncResp->res, "CertificateType",
490 "ReplaceCertificate");
491 return;
492 }
493
494 std::string certURI;
495 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
496 "@odata.id", certURI))
497 {
498 messages::actionParameterMissing(asyncResp->res, "ReplaceCertificate",
499 "CertificateUri");
500 return;
501 }
502 BMCWEB_LOG_INFO << "Certificate URI to replace: " << certURI;
503
504 boost::urls::result<boost::urls::url_view> parsedUrl =
505 boost::urls::parse_relative_ref(certURI);
506 if (!parsedUrl)
507 {
508 messages::actionParameterValueFormatError(
509 asyncResp->res, certURI, "CertificateUri", "ReplaceCertificate");
510 return;
511 }
512
513 std::string id;
514 sdbusplus::message::object_path objectPath;
515 std::string name;
516 std::string service;
517 if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1", "Managers",
518 "bmc", "NetworkProtocol", "HTTPS",
519 "Certificates", std::ref(id)))
520 {
521 objectPath =
522 sdbusplus::message::object_path(certs::httpsObjectPath) / id;
523 name = "HTTPS certificate";
524 service = certs::httpsServiceName;
525 }
526 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
527 "AccountService", "LDAP",
528 "Certificates", std::ref(id)))
529 {
530 objectPath =
531 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
532 name = "LDAP certificate";
533 service = certs::ldapServiceName;
534 }
535 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
536 "Managers", "bmc", "Truststore",
537 "Certificates", std::ref(id)))
538 {
539 objectPath =
540 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
541 name = "TrustStore certificate";
542 service = certs::authorityServiceName;
543 }
544 else
545 {
546 messages::actionParameterNotSupported(asyncResp->res, "CertificateUri",
547 "ReplaceCertificate");
548 return;
549 }
550
551 std::shared_ptr<CertificateFile> certFile =
552 std::make_shared<CertificateFile>(certificate);
553 crow::connections::systemBus->async_method_call(
554 [asyncResp, certFile, objectPath, service, url{*parsedUrl}, id,
555 name](const boost::system::error_code ec) {
556 if (ec)
557 {
558 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
559 if (ec.value() ==
560 boost::system::linux_error::bad_request_descriptor)
561 {
562 messages::resourceNotFound(asyncResp->res, "Certificate", id);
563 return;
564 }
565 messages::internalError(asyncResp->res);
566 return;
567 }
568 getCertificateProperties(asyncResp, objectPath, service, id, url, name);
569 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
570 << certFile->getCertFilePath();
571 },
572 service, objectPath, certs::certReplaceIntf, "Replace",
573 certFile->getCertFilePath());
574}
575
Ed Tanouscf9e4172022-12-21 09:30:16 -0800576// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800577static std::unique_ptr<sdbusplus::bus::match_t> csrMatcher;
578/**
579 * @brief Read data from CSR D-bus object and set to response
580 *
581 * @param[in] asyncResp Shared pointer to the response message
582 * @param[in] certURI Link to certifiate collection URI
583 * @param[in] service D-Bus service name
584 * @param[in] certObjPath certificate D-Bus object path
585 * @param[in] csrObjPath CSR D-Bus object path
586 * @return None
587 */
588static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
589 const std::string& certURI, const std::string& service,
590 const std::string& certObjPath,
591 const std::string& csrObjPath)
592{
593 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
594 << " CSRObjectPath=" << csrObjPath
595 << " service=" << service;
596 crow::connections::systemBus->async_method_call(
597 [asyncResp, certURI](const boost::system::error_code ec,
598 const std::string& csr) {
599 if (ec)
600 {
601 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
602 messages::internalError(asyncResp->res);
603 return;
604 }
605 if (csr.empty())
606 {
607 BMCWEB_LOG_ERROR << "CSR read is empty";
608 messages::internalError(asyncResp->res);
609 return;
610 }
611 asyncResp->res.jsonValue["CSRString"] = csr;
612 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
613 certURI;
614 },
615 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
616}
617
618inline void
619 handleGenerateCSRAction(App& app, const crow::Request& req,
620 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
621{
622 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
623 {
624 return;
625 }
626 static const int rsaKeyBitLength = 2048;
627
628 // Required parameters
629 std::string city;
630 std::string commonName;
631 std::string country;
632 std::string organization;
633 std::string organizationalUnit;
634 std::string state;
635 nlohmann::json certificateCollection;
636
637 // Optional parameters
638 std::optional<std::vector<std::string>> optAlternativeNames =
639 std::vector<std::string>();
640 std::optional<std::string> optContactPerson = "";
641 std::optional<std::string> optChallengePassword = "";
642 std::optional<std::string> optEmail = "";
643 std::optional<std::string> optGivenName = "";
644 std::optional<std::string> optInitials = "";
645 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
646 std::optional<std::string> optKeyCurveId = "secp384r1";
647 std::optional<std::string> optKeyPairAlgorithm = "EC";
648 std::optional<std::vector<std::string>> optKeyUsage =
649 std::vector<std::string>();
650 std::optional<std::string> optSurname = "";
651 std::optional<std::string> optUnstructuredName = "";
652 if (!json_util::readJsonAction(
653 req, asyncResp->res, "City", city, "CommonName", commonName,
654 "ContactPerson", optContactPerson, "Country", country,
655 "Organization", organization, "OrganizationalUnit",
656 organizationalUnit, "State", state, "CertificateCollection",
657 certificateCollection, "AlternativeNames", optAlternativeNames,
658 "ChallengePassword", optChallengePassword, "Email", optEmail,
659 "GivenName", optGivenName, "Initials", optInitials, "KeyBitLength",
660 optKeyBitLength, "KeyCurveId", optKeyCurveId, "KeyPairAlgorithm",
661 optKeyPairAlgorithm, "KeyUsage", optKeyUsage, "Surname", optSurname,
662 "UnstructuredName", optUnstructuredName))
663 {
664 return;
665 }
666
667 // bmcweb has no way to store or decode a private key challenge
668 // password, which will likely cause bmcweb to crash on startup
669 // if this is not set on a post so not allowing the user to set
670 // value
671 if (!optChallengePassword->empty())
672 {
673 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
674 "ChallengePassword");
675 return;
676 }
677
678 std::string certURI;
679 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
680 "@odata.id", certURI))
681 {
682 return;
683 }
684
685 std::string objectPath;
686 std::string service;
687 if (certURI.starts_with(
688 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
689 {
690 objectPath = certs::httpsObjectPath;
691 service = certs::httpsServiceName;
692 }
693 else if (certURI.starts_with(
694 "/redfish/v1/AccountService/LDAP/Certificates"))
695 {
696 objectPath = certs::ldapObjectPath;
697 service = certs::ldapServiceName;
698 }
699 else
700 {
701 messages::actionParameterNotSupported(
702 asyncResp->res, "CertificateCollection", "GenerateCSR");
703 return;
704 }
705
706 // supporting only EC and RSA algorithm
707 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
708 {
709 messages::actionParameterNotSupported(
710 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
711 return;
712 }
713
714 // supporting only 2048 key bit length for RSA algorithm due to
715 // time consumed in generating private key
716 if (*optKeyPairAlgorithm == "RSA" && *optKeyBitLength != rsaKeyBitLength)
717 {
718 messages::propertyValueNotInList(
719 asyncResp->res, std::to_string(*optKeyBitLength), "KeyBitLength");
720 return;
721 }
722
723 // validate KeyUsage supporting only 1 type based on URL
724 if (certURI.starts_with(
725 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
726 {
727 if (optKeyUsage->empty())
728 {
729 optKeyUsage->push_back("ServerAuthentication");
730 }
731 else if (optKeyUsage->size() == 1)
732 {
733 if ((*optKeyUsage)[0] != "ServerAuthentication")
734 {
735 messages::propertyValueNotInList(asyncResp->res,
736 (*optKeyUsage)[0], "KeyUsage");
737 return;
738 }
739 }
740 else
741 {
742 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage",
743 "GenerateCSR");
744 return;
745 }
746 }
747 else if (certURI.starts_with(
748 "/redfish/v1/AccountService/LDAP/Certificates"))
749 {
750 if (optKeyUsage->empty())
751 {
752 optKeyUsage->push_back("ClientAuthentication");
753 }
754 else if (optKeyUsage->size() == 1)
755 {
756 if ((*optKeyUsage)[0] != "ClientAuthentication")
757 {
758 messages::propertyValueNotInList(asyncResp->res,
759 (*optKeyUsage)[0], "KeyUsage");
760 return;
761 }
762 }
763 else
764 {
765 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage",
766 "GenerateCSR");
767 return;
768 }
769 }
770
771 // Only allow one CSR matcher at a time so setting retry
772 // time-out and timer expiry to 10 seconds for now.
773 static const int timeOut = 10;
774 if (csrMatcher)
775 {
776 messages::serviceTemporarilyUnavailable(asyncResp->res,
777 std::to_string(timeOut));
778 return;
779 }
780
781 // Make this static so it survives outside this method
782 static boost::asio::steady_timer timeout(*req.ioService);
783 timeout.expires_after(std::chrono::seconds(timeOut));
784 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
785 csrMatcher = nullptr;
786 if (ec)
787 {
788 // operation_aborted is expected if timer is canceled
789 // before completion.
790 if (ec != boost::asio::error::operation_aborted)
791 {
792 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
793 }
794 return;
795 }
796 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
797 messages::internalError(asyncResp->res);
798 });
799
800 // create a matcher to wait on CSR object
801 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
802 std::string match("type='signal',"
803 "interface='org.freedesktop.DBus.ObjectManager',"
804 "path='" +
805 objectPath +
806 "',"
807 "member='InterfacesAdded'");
808 csrMatcher = std::make_unique<sdbusplus::bus::match_t>(
809 *crow::connections::systemBus, match,
810 [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) {
811 timeout.cancel();
812 if (m.is_method_error())
813 {
814 BMCWEB_LOG_ERROR << "Dbus method error!!!";
815 messages::internalError(asyncResp->res);
816 return;
817 }
818
819 dbus::utility::DBusInteracesMap interfacesProperties;
820
821 sdbusplus::message::object_path csrObjectPath;
822 m.read(csrObjectPath, interfacesProperties);
823 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
824 for (const auto& interface : interfacesProperties)
825 {
826 if (interface.first == "xyz.openbmc_project.Certs.CSR")
827 {
828 getCSR(asyncResp, certURI, service, objectPath,
829 csrObjectPath.str);
830 break;
831 }
832 }
833 });
834 crow::connections::systemBus->async_method_call(
835 [asyncResp](const boost::system::error_code ec, const std::string&) {
836 if (ec)
837 {
838 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
839 messages::internalError(asyncResp->res);
840 return;
841 }
842 },
843 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
844 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
845 commonName, *optContactPerson, country, *optEmail, *optGivenName,
846 *optInitials, *optKeyBitLength, *optKeyCurveId, *optKeyPairAlgorithm,
847 *optKeyUsage, organization, organizationalUnit, state, *optSurname,
848 *optUnstructuredName);
849}
850
851inline void requestRoutesCertificateService(App& app)
852{
853 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
854 .privileges(redfish::privileges::getCertificateService)
855 .methods(boost::beast::http::verb::get)(
856 std::bind_front(handleCertificateServiceGet, std::ref(app)));
857
858 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
859 .privileges(redfish::privileges::getCertificateLocations)
860 .methods(boost::beast::http::verb::get)(
861 std::bind_front(handleCertificateLocationsGet, std::ref(app)));
862
George Liu0fda0f12021-11-16 10:06:17 +0800863 BMCWEB_ROUTE(
864 app,
865 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700866 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700867 .methods(boost::beast::http::verb::post)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800868 std::bind_front(handleReplaceCertificateAction, std::ref(app)));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600869
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800870 BMCWEB_ROUTE(
871 app,
872 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
873 .privileges(redfish::privileges::postCertificateService)
874 .methods(boost::beast::http::verb::post)(
875 std::bind_front(handleGenerateCSRAction, std::ref(app)));
876} // requestRoutesCertificateService
877
878inline void handleHTTPSCertificateCollectionGet(
879 App& app, const crow::Request& req,
880 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
881{
882 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
883 {
884 return;
885 }
886
887 asyncResp->res.jsonValue["@odata.id"] =
888 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
889 asyncResp->res.jsonValue["@odata.type"] =
890 "#CertificateCollection.CertificateCollection";
891 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
892 asyncResp->res.jsonValue["Description"] =
893 "A Collection of HTTPS certificate instances";
894
895 getCertificateList(asyncResp, certs::httpsObjectPath,
896 "/Members"_json_pointer,
897 "/Members@odata.count"_json_pointer);
898}
899
900inline void handleHTTPSCertificateCollectionPost(
901 App& app, const crow::Request& req,
902 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
903{
904 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
905 {
906 return;
907 }
908 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
909
910 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
911 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
912
913 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
914
915 if (certFileBody.empty())
916 {
917 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
918 messages::unrecognizedRequestBody(asyncResp->res);
919 return;
920 }
921
922 std::shared_ptr<CertificateFile> certFile =
923 std::make_shared<CertificateFile>(certFileBody);
924
925 crow::connections::systemBus->async_method_call(
926 [asyncResp, certFile](const boost::system::error_code ec,
927 const std::string& objectPath) {
928 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700929 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800930 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Ed Tanous002d39b2022-05-31 08:59:27 -0700931 messages::internalError(asyncResp->res);
932 return;
933 }
934
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800935 sdbusplus::message::object_path path(objectPath);
936 std::string certId = path.filename();
937 const boost::urls::url certURL = crow::utility::urlFromPieces(
938 "redfish", "v1", "Managers", "bmc", "NetworkProtocol", "HTTPS",
939 "Certificates", certId);
940 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
941 certId, certURL, "HTTPS Certificate");
942 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
943 << certFile->getCertFilePath();
944 },
945 certs::httpsServiceName, certs::httpsObjectPath, certs::certInstallIntf,
946 "Install", certFile->getCertFilePath());
947}
Ed Tanous002d39b2022-05-31 08:59:27 -0700948
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800949inline void handleHTTPSCertificateGet(
950 App& app, const crow::Request& req,
951 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
952{
953 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
954 {
955 return;
956 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700957
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800958 BMCWEB_LOG_DEBUG << "HTTPS Certificate ID=" << id;
959 const boost::urls::url certURL = crow::utility::urlFromPieces(
960 "redfish", "v1", "Managers", "bmc", "NetworkProtocol", "HTTPS",
961 "Certificates", id);
962 std::string objPath =
963 sdbusplus::message::object_path(certs::httpsObjectPath) / id;
964 getCertificateProperties(asyncResp, objPath, certs::httpsServiceName, id,
965 certURL, "HTTPS Certificate");
966}
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700967
968inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600969{
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800970 BMCWEB_ROUTE(app,
971 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
972 .privileges(redfish::privileges::getCertificateCollection)
973 .methods(boost::beast::http::verb::get)(std::bind_front(
974 handleHTTPSCertificateCollectionGet, std::ref(app)));
975
976 BMCWEB_ROUTE(app,
977 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
978 .privileges(redfish::privileges::postCertificateCollection)
979 .methods(boost::beast::http::verb::post)(std::bind_front(
980 handleHTTPSCertificateCollectionPost, std::ref(app)));
981
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700982 BMCWEB_ROUTE(
983 app,
984 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700985 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800986 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800987 std::bind_front(handleHTTPSCertificateGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700988}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600989
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800990inline void handleLDAPCertificateCollectionGet(
991 App& app, const crow::Request& req,
992 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600993{
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800994 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
995 {
996 return;
997 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700998
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800999 asyncResp->res.jsonValue["@odata.id"] =
1000 "/redfish/v1/AccountService/LDAP/Certificates";
1001 asyncResp->res.jsonValue["@odata.type"] =
1002 "#CertificateCollection.CertificateCollection";
1003 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1004 asyncResp->res.jsonValue["Description"] =
1005 "A Collection of LDAP certificate instances";
Ed Tanous002d39b2022-05-31 08:59:27 -07001006
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001007 getCertificateList(asyncResp, certs::ldapObjectPath,
1008 "/Members"_json_pointer,
1009 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001010}
Marri Devender Rao37cce912019-02-20 01:05:22 -06001011
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001012inline void handleLDAPCertificateCollectionPost(
1013 App& app, const crow::Request& req,
1014 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001015{
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001016 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1017 {
1018 return;
1019 }
1020 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1021
1022 if (certFileBody.empty())
1023 {
1024 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1025 messages::unrecognizedRequestBody(asyncResp->res);
1026 return;
1027 }
1028
1029 std::shared_ptr<CertificateFile> certFile =
1030 std::make_shared<CertificateFile>(certFileBody);
1031
1032 crow::connections::systemBus->async_method_call(
1033 [asyncResp, certFile](const boost::system::error_code ec,
1034 const std::string& objectPath) {
1035 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001036 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001037 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1038 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001039 return;
1040 }
1041
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001042 sdbusplus::message::object_path path(objectPath);
1043 std::string certId = path.filename();
1044 const boost::urls::url certURL = crow::utility::urlFromPieces(
1045 "redfish", "v1", "AccountService", "LDAP", "Certificates", certId);
1046 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1047 certId, certURL, "LDAP Certificate");
1048 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1049 << certFile->getCertFilePath();
1050 },
1051 certs::ldapServiceName, certs::ldapObjectPath, certs::certInstallIntf,
1052 "Install", certFile->getCertFilePath());
1053}
Ed Tanous002d39b2022-05-31 08:59:27 -07001054
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001055inline void handleLDAPCertificateGet(
1056 App& app, const crow::Request& req,
1057 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1058{
1059 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1060 {
1061 return;
1062 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001063
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001064 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << id;
1065 const boost::urls::url certURL = crow::utility::urlFromPieces(
1066 "redfish", "v1", "AccountService", "LDAP", "Certificates", id);
1067 std::string objPath =
1068 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1069 getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id,
1070 certURL, "LDAP Certificate");
1071}
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001072
Jiaqing Zhao99612242022-09-29 15:31:09 +08001073inline void handleLDAPCertificateDelete(
1074 App& app, const crow::Request& req,
1075 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1076{
1077 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1078 {
1079 return;
1080 }
1081
1082 BMCWEB_LOG_DEBUG << "Delete LDAP Certificate ID=" << id;
1083 std::string objPath =
1084 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1085
1086 deleteCertificate(asyncResp, certs::ldapServiceName, objPath);
1087}
1088
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001089inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001090{
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001091 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1092 .privileges(redfish::privileges::getCertificateCollection)
1093 .methods(boost::beast::http::verb::get)(
1094 std::bind_front(handleLDAPCertificateCollectionGet, std::ref(app)));
1095
1096 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1097 .privileges(redfish::privileges::postCertificateCollection)
1098 .methods(boost::beast::http::verb::post)(std::bind_front(
1099 handleLDAPCertificateCollectionPost, std::ref(app)));
1100
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001101 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001102 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001103 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001104 std::bind_front(handleLDAPCertificateGet, std::ref(app)));
Jiaqing Zhao99612242022-09-29 15:31:09 +08001105
1106 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
1107 .privileges(redfish::privileges::deleteCertificate)
1108 .methods(boost::beast::http::verb::delete_)(
1109 std::bind_front(handleLDAPCertificateDelete, std::ref(app)));
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001110} // requestRoutesLDAPCertificate
1111
1112inline void handleTrustStoreCertificateCollectionGet(
1113 App& app, const crow::Request& req,
1114 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1115{
1116 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1117 {
1118 return;
1119 }
1120
1121 asyncResp->res.jsonValue["@odata.id"] =
1122 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1123 asyncResp->res.jsonValue["@odata.type"] =
1124 "#CertificateCollection.CertificateCollection";
1125 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1126 asyncResp->res.jsonValue["Description"] =
1127 "A Collection of TrustStore certificate instances";
1128
1129 getCertificateList(asyncResp, certs::authorityObjectPath,
1130 "/Members"_json_pointer,
1131 "/Members@odata.count"_json_pointer);
1132}
1133
1134inline void handleTrustStoreCertificateCollectionPost(
1135 App& app, const crow::Request& req,
1136 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1137{
1138 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1139 {
1140 return;
1141 }
1142 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1143
1144 if (certFileBody.empty())
1145 {
1146 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1147 messages::unrecognizedRequestBody(asyncResp->res);
1148 return;
1149 }
1150
1151 std::shared_ptr<CertificateFile> certFile =
1152 std::make_shared<CertificateFile>(certFileBody);
1153 crow::connections::systemBus->async_method_call(
1154 [asyncResp, certFile](const boost::system::error_code ec,
1155 const std::string& objectPath) {
1156 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001157 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001158 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1159 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001160 return;
1161 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001162
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001163 sdbusplus::message::object_path path(objectPath);
1164 std::string certId = path.filename();
1165 const boost::urls::url certURL =
1166 crow::utility::urlFromPieces("redfish", "v1", "Managers", "bmc",
1167 "Truststore", "Certificates", certId);
1168 getCertificateProperties(asyncResp, objectPath,
1169 certs::authorityServiceName, certId, certURL,
1170 "TrustStore Certificate");
1171 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1172 << certFile->getCertFilePath();
1173 },
1174 certs::authorityServiceName, certs::authorityObjectPath,
1175 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1176}
1177
1178inline void handleTrustStoreCertificateGet(
1179 App& app, const crow::Request& req,
1180 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1181{
1182 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1183 {
1184 return;
1185 }
1186
1187 BMCWEB_LOG_DEBUG << "Truststore Certificate ID=" << id;
1188 const boost::urls::url certURL = crow::utility::urlFromPieces(
1189 "redfish", "v1", "Managers", "bmc", "Truststore", "Certificates", id);
1190 std::string objPath =
1191 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1192 getCertificateProperties(asyncResp, objPath, certs::authorityServiceName,
1193 id, certURL, "TrustStore Certificate");
1194}
1195
1196inline void handleTrustStoreCertificateDelete(
1197 App& app, const crow::Request& req,
1198 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1199{
1200 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1201 {
1202 return;
1203 }
1204
1205 BMCWEB_LOG_DEBUG << "Delete TrustStore Certificate ID=" << id;
1206 std::string objPath =
1207 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1208
Jiaqing Zhao7a3a8f72022-09-29 15:15:58 +08001209 deleteCertificate(asyncResp, certs::authorityServiceName, objPath);
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001210}
1211
1212inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001213{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001214 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001215 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001216 .methods(boost::beast::http::verb::get)(std::bind_front(
1217 handleTrustStoreCertificateCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001218
1219 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001220 .privileges(redfish::privileges::postCertificateCollection)
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001221 .methods(boost::beast::http::verb::post)(std::bind_front(
1222 handleTrustStoreCertificateCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07001223
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001224 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001225 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001226 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001227 std::bind_front(handleTrustStoreCertificateGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001228
1229 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001230 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001231 .methods(boost::beast::http::verb::delete_)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001232 std::bind_front(handleTrustStoreCertificateDelete, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001233} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001234} // namespace redfish