blob: f5633793b1b12ee61b747924408e52d70be3063d [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001#pragma once
2
George Liu7a1dbc42022-12-07 16:03:22 +08003#include "dbus_utility.hpp"
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +02004#include "utils/dbus_utils.hpp"
5
John Edward Broadbent7e860f12021-04-08 15:57:16 -07006#include <app.hpp>
Ed Tanousd9f6c622022-03-17 09:12:17 -07007#include <async_resp.hpp>
Jiaqing Zhao90d2d1e2022-04-13 17:01:57 +08008#include <boost/system/linux_error.hpp>
Ed Tanousd9f6c622022-03-17 09:12:17 -07009#include <http_response.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -070010#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070011#include <registries/privilege_registry.hpp>
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +020012#include <sdbusplus/asio/property.hpp>
13#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050014
George Liu7a1dbc42022-12-07 16:03:22 +080015#include <array>
16#include <string_view>
17
Marri Devender Rao5968cae2019-01-21 10:27:12 -060018namespace redfish
19{
20namespace certs
21{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050022constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
23constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
24constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
25constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
26constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
27constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050028constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060029 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050030constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060031 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050032constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050033 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +080034constexpr char const* baseObjectPath = "/xyz/openbmc_project/certs";
35constexpr char const* httpsObjectPath =
36 "/xyz/openbmc_project/certs/server/https";
37constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050038constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050039 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060040} // namespace certs
41
42/**
43 * The Certificate schema defines a Certificate Service which represents the
44 * actions available to manage certificates and links to where certificates
45 * are installed.
46 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060047
zhanghch058d1b46d2021-04-01 11:18:24 +080048inline std::string getCertificateFromReqBody(
49 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
50 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020051{
52 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
53
54 if (reqJson.is_discarded())
55 {
56 // We did not receive JSON request, proceed as it is RAW data
57 return req.body;
58 }
59
60 std::string certificate;
61 std::optional<std::string> certificateType = "PEM";
62
Willy Tu15ed6782021-12-14 11:03:16 -080063 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
64 certificate, "CertificateType",
65 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020066 {
67 BMCWEB_LOG_ERROR << "Required parameters are missing";
68 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -070069 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020070 }
71
72 if (*certificateType != "PEM")
73 {
74 messages::propertyValueNotInList(asyncResp->res, *certificateType,
75 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -070076 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020077 }
78
79 return certificate;
80}
81
Marri Devender Rao5968cae2019-01-21 10:27:12 -060082/**
83 * Class to create a temporary certificate file for uploading to system
84 */
85class CertificateFile
86{
87 public:
88 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050089 CertificateFile(const CertificateFile&) = delete;
90 CertificateFile& operator=(const CertificateFile&) = delete;
91 CertificateFile(CertificateFile&&) = delete;
92 CertificateFile& operator=(CertificateFile&&) = delete;
Ed Tanous4e23a442022-06-06 09:57:26 -070093 explicit CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060094 {
Ed Tanous72d52d22020-10-12 07:46:27 -070095 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -070096 'e', 'r', 't', 's', '.', 'X',
97 'X', 'X', 'X', 'X', 'X', '\0'};
98 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -080099 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600100 {
101 certDirectory = tempDirectory;
102 certificateFile = certDirectory / "cert.pem";
103 std::ofstream out(certificateFile, std::ofstream::out |
104 std::ofstream::binary |
105 std::ofstream::trunc);
106 out << certString;
107 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800108 BMCWEB_LOG_DEBUG << "Creating certificate file"
109 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600110 }
111 }
112 ~CertificateFile()
113 {
114 if (std::filesystem::exists(certDirectory))
115 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800116 BMCWEB_LOG_DEBUG << "Removing certificate file"
117 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000118 std::error_code ec;
119 std::filesystem::remove_all(certDirectory, ec);
120 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600121 {
122 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800123 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600124 }
125 }
126 }
127 std::string getCertFilePath()
128 {
129 return certificateFile;
130 }
131
132 private:
133 std::filesystem::path certificateFile;
134 std::filesystem::path certDirectory;
135};
136
137/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500138 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600139 *
140 * @param[in] asyncResp Shared pointer to the response message
141 * @param[in] str Issuer/Subject value in key=value pairs
142 * @param[in] type Issuer/Subject
143 * @return None
144 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500145static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600146 const std::string_view value)
147{
148 // example: O=openbmc-project.xyz,CN=localhost
149 std::string_view::iterator i = value.begin();
150 while (i != value.end())
151 {
152 std::string_view::iterator tokenBegin = i;
153 while (i != value.end() && *i != '=')
154 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530155 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600156 }
157 if (i == value.end())
158 {
159 break;
160 }
Ed Tanous271584a2019-07-09 16:24:22 -0700161 const std::string_view key(tokenBegin,
162 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530163 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600164 tokenBegin = i;
165 while (i != value.end() && *i != ',')
166 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530167 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600168 }
Ed Tanous271584a2019-07-09 16:24:22 -0700169 const std::string_view val(tokenBegin,
170 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600171 if (key == "L")
172 {
173 out["City"] = val;
174 }
175 else if (key == "CN")
176 {
177 out["CommonName"] = val;
178 }
179 else if (key == "C")
180 {
181 out["Country"] = val;
182 }
183 else if (key == "O")
184 {
185 out["Organization"] = val;
186 }
187 else if (key == "OU")
188 {
189 out["OrganizationalUnit"] = val;
190 }
191 else if (key == "ST")
192 {
193 out["State"] = val;
194 }
195 // skip comma character
196 if (i != value.end())
197 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530198 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600199 }
200 }
201}
202
203/**
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800204 * @brief Retrieve the installed certificate list
205 *
206 * @param[in] asyncResp Shared pointer to the response message
207 * @param[in] basePath DBus object path to search
208 * @param[in] listPtr Json pointer to the list in asyncResp
209 * @param[in] countPtr Json pointer to the count in asyncResp
210 * @return None
211 */
212static void
213 getCertificateList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
214 const std::string& basePath,
215 const nlohmann::json::json_pointer& listPtr,
216 const nlohmann::json::json_pointer& countPtr)
217{
George Liu7a1dbc42022-12-07 16:03:22 +0800218 constexpr std::array<std::string_view, 1> interfaces = {
219 certs::certPropIntf};
220 dbus::utility::getSubTreePaths(
221 basePath, 0, interfaces,
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800222 [asyncResp, listPtr, countPtr](
George Liu7a1dbc42022-12-07 16:03:22 +0800223 const boost::system::error_code& ec,
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800224 const dbus::utility::MapperGetSubTreePathsResponse& certPaths) {
225 if (ec)
226 {
227 BMCWEB_LOG_ERROR << "Certificate collection query failed: " << ec;
228 messages::internalError(asyncResp->res);
229 return;
230 }
231
232 nlohmann::json& links = asyncResp->res.jsonValue[listPtr];
233 links = nlohmann::json::array();
234 for (const auto& certPath : certPaths)
235 {
236 sdbusplus::message::object_path objPath(certPath);
237 std::string certId = objPath.filename();
238 if (certId.empty())
239 {
240 BMCWEB_LOG_ERROR << "Invalid certificate objPath " << certPath;
241 continue;
242 }
243
244 boost::urls::url certURL;
245 if (objPath.parent_path() == certs::httpsObjectPath)
246 {
247 certURL = crow::utility::urlFromPieces(
248 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
249 "HTTPS", "Certificates", certId);
250 }
251 else if (objPath.parent_path() == certs::ldapObjectPath)
252 {
253 certURL = crow::utility::urlFromPieces("redfish", "v1",
254 "AccountService", "LDAP",
255 "Certificates", certId);
256 }
257 else if (objPath.parent_path() == certs::authorityObjectPath)
258 {
259 certURL = crow::utility::urlFromPieces(
260 "redfish", "v1", "Managers", "bmc", "Truststore",
261 "Certificates", certId);
262 }
263 else
264 {
265 continue;
266 }
267
268 nlohmann::json::object_t link;
269 link["@odata.id"] = certURL;
270 links.emplace_back(std::move(link));
271 }
272
273 asyncResp->res.jsonValue[countPtr] = links.size();
George Liu7a1dbc42022-12-07 16:03:22 +0800274 });
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800275}
276
277/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600278 * @brief Retrieve the certificates properties and append to the response
279 * message
280 *
281 * @param[in] asyncResp Shared pointer to the response message
282 * @param[in] objectPath Path of the D-Bus service object
283 * @param[in] certId Id of the certificate
284 * @param[in] certURL URL of the certificate object
285 * @param[in] name name of the certificate
286 * @return None
287 */
288static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800289 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800290 const std::string& objectPath, const std::string& service,
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800291 const std::string& certId, const boost::urls::url& certURL,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800292 const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600293{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600294 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
295 << " certId=" << certId << " certURl=" << certURL;
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200296 sdbusplus::asio::getAllProperties(
297 *crow::connections::systemBus, service, objectPath, certs::certPropIntf,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800298 [asyncResp, certURL, certId,
299 name](const boost::system::error_code ec,
300 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700301 if (ec)
302 {
303 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800304 messages::resourceNotFound(asyncResp->res, "Certificate", certId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700305 return;
306 }
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200307
308 const std::string* certificateString = nullptr;
309 const std::vector<std::string>* keyUsage = nullptr;
310 const std::string* issuer = nullptr;
311 const std::string* subject = nullptr;
312 const uint64_t* validNotAfter = nullptr;
313 const uint64_t* validNotBefore = nullptr;
314
315 const bool success = sdbusplus::unpackPropertiesNoThrow(
316 dbus_utils::UnpackErrorPrinter(), properties, "CertificateString",
317 certificateString, "KeyUsage", keyUsage, "Issuer", issuer,
318 "Subject", subject, "ValidNotAfter", validNotAfter,
319 "ValidNotBefore", validNotBefore);
320
321 if (!success)
322 {
323 messages::internalError(asyncResp->res);
324 return;
325 }
326
Ed Tanous002d39b2022-05-31 08:59:27 -0700327 asyncResp->res.jsonValue["@odata.id"] = certURL;
328 asyncResp->res.jsonValue["@odata.type"] =
329 "#Certificate.v1_0_0.Certificate";
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800330 asyncResp->res.jsonValue["Id"] = certId;
Ed Tanous002d39b2022-05-31 08:59:27 -0700331 asyncResp->res.jsonValue["Name"] = name;
332 asyncResp->res.jsonValue["Description"] = name;
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200333 asyncResp->res.jsonValue["CertificateString"] = "";
334 asyncResp->res.jsonValue["KeyUsage"] = nlohmann::json::array();
335
336 if (certificateString != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -0700337 {
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200338 asyncResp->res.jsonValue["CertificateString"] = *certificateString;
Ed Tanous002d39b2022-05-31 08:59:27 -0700339 }
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200340
341 if (keyUsage != nullptr)
342 {
343 asyncResp->res.jsonValue["KeyUsage"] = *keyUsage;
344 }
345
346 if (issuer != nullptr)
347 {
348 updateCertIssuerOrSubject(asyncResp->res.jsonValue["Issuer"],
349 *issuer);
350 }
351
352 if (subject != nullptr)
353 {
354 updateCertIssuerOrSubject(asyncResp->res.jsonValue["Subject"],
355 *subject);
356 }
357
358 if (validNotAfter != nullptr)
359 {
360 asyncResp->res.jsonValue["ValidNotAfter"] =
361 redfish::time_utils::getDateTimeUint(*validNotAfter);
362 }
363
364 if (validNotBefore != nullptr)
365 {
366 asyncResp->res.jsonValue["ValidNotBefore"] =
367 redfish::time_utils::getDateTimeUint(*validNotBefore);
368 }
369
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800370 asyncResp->res.addHeader(
Ed Tanousd9f6c622022-03-17 09:12:17 -0700371 boost::beast::http::field::location,
372 std::string_view(certURL.data(), certURL.size()));
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200373 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600374}
375
Jiaqing Zhao7a3a8f72022-09-29 15:15:58 +0800376static void
377 deleteCertificate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
378 const std::string& service,
379 const sdbusplus::message::object_path& objectPath)
380{
381 crow::connections::systemBus->async_method_call(
382 [asyncResp,
383 id{objectPath.filename()}](const boost::system::error_code ec) {
384 if (ec)
385 {
386 messages::resourceNotFound(asyncResp->res, "Certificate", id);
387 return;
388 }
389 BMCWEB_LOG_INFO << "Certificate deleted";
390 asyncResp->res.result(boost::beast::http::status::no_content);
391 },
392 service, objectPath, certs::objDeleteIntf, "Delete");
393}
394
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800395inline void handleCertificateServiceGet(
396 App& app, const crow::Request& req,
397 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600398{
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800399 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
400 {
401 return;
402 }
403
404 asyncResp->res.jsonValue["@odata.type"] =
405 "#CertificateService.v1_0_0.CertificateService";
406 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/CertificateService";
407 asyncResp->res.jsonValue["Id"] = "CertificateService";
408 asyncResp->res.jsonValue["Name"] = "Certificate Service";
409 asyncResp->res.jsonValue["Description"] =
410 "Actions available to manage certificates";
411 // /redfish/v1/CertificateService/CertificateLocations is something
412 // only ConfigureManager can access then only display when the user
413 // has permissions ConfigureManager
414 Privileges effectiveUserPrivileges =
415 redfish::getUserPrivileges(req.userRole);
416 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
417 effectiveUserPrivileges))
418 {
419 asyncResp->res.jsonValue["CertificateLocations"]["@odata.id"] =
420 "/redfish/v1/CertificateService/CertificateLocations";
421 }
422 nlohmann::json& actions = asyncResp->res.jsonValue["Actions"];
423 nlohmann::json& replace = actions["#CertificateService.ReplaceCertificate"];
424 replace["target"] =
425 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate";
426 nlohmann::json::array_t allowed;
427 allowed.push_back("PEM");
428 replace["CertificateType@Redfish.AllowableValues"] = std::move(allowed);
429 actions["#CertificateService.GenerateCSR"]["target"] =
430 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR";
431}
432
433inline void handleCertificateLocationsGet(
434 App& app, const crow::Request& req,
435 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
436{
437 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
438 {
439 return;
440 }
441 asyncResp->res.jsonValue["@odata.id"] =
442 "/redfish/v1/CertificateService/CertificateLocations";
443 asyncResp->res.jsonValue["@odata.type"] =
444 "#CertificateLocations.v1_0_0.CertificateLocations";
445 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
446 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
447 asyncResp->res.jsonValue["Description"] =
448 "Defines a resource that an administrator can use in order to "
449 "locate all certificates installed on a given service";
450
451 getCertificateList(asyncResp, certs::baseObjectPath,
452 "/Links/Certificates"_json_pointer,
453 "/Links/Certificates@odata.count"_json_pointer);
454}
455
456inline void handleReplaceCertificateAction(
457 App& app, const crow::Request& req,
458 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
459{
460 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
461 {
462 return;
463 }
464 std::string certificate;
465 nlohmann::json certificateUri;
466 std::optional<std::string> certificateType = "PEM";
467
468 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
469 certificate, "CertificateUri",
470 certificateUri, "CertificateType",
471 certificateType))
472 {
473 BMCWEB_LOG_ERROR << "Required parameters are missing";
474 messages::internalError(asyncResp->res);
475 return;
476 }
477
478 if (!certificateType)
479 {
480 // should never happen, but it never hurts to be paranoid.
481 return;
482 }
483 if (certificateType != "PEM")
484 {
485 messages::actionParameterNotSupported(asyncResp->res, "CertificateType",
486 "ReplaceCertificate");
487 return;
488 }
489
490 std::string certURI;
491 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
492 "@odata.id", certURI))
493 {
494 messages::actionParameterMissing(asyncResp->res, "ReplaceCertificate",
495 "CertificateUri");
496 return;
497 }
498 BMCWEB_LOG_INFO << "Certificate URI to replace: " << certURI;
499
500 boost::urls::result<boost::urls::url_view> parsedUrl =
501 boost::urls::parse_relative_ref(certURI);
502 if (!parsedUrl)
503 {
504 messages::actionParameterValueFormatError(
505 asyncResp->res, certURI, "CertificateUri", "ReplaceCertificate");
506 return;
507 }
508
509 std::string id;
510 sdbusplus::message::object_path objectPath;
511 std::string name;
512 std::string service;
513 if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1", "Managers",
514 "bmc", "NetworkProtocol", "HTTPS",
515 "Certificates", std::ref(id)))
516 {
517 objectPath =
518 sdbusplus::message::object_path(certs::httpsObjectPath) / id;
519 name = "HTTPS certificate";
520 service = certs::httpsServiceName;
521 }
522 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
523 "AccountService", "LDAP",
524 "Certificates", std::ref(id)))
525 {
526 objectPath =
527 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
528 name = "LDAP certificate";
529 service = certs::ldapServiceName;
530 }
531 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
532 "Managers", "bmc", "Truststore",
533 "Certificates", std::ref(id)))
534 {
535 objectPath =
536 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
537 name = "TrustStore certificate";
538 service = certs::authorityServiceName;
539 }
540 else
541 {
542 messages::actionParameterNotSupported(asyncResp->res, "CertificateUri",
543 "ReplaceCertificate");
544 return;
545 }
546
547 std::shared_ptr<CertificateFile> certFile =
548 std::make_shared<CertificateFile>(certificate);
549 crow::connections::systemBus->async_method_call(
550 [asyncResp, certFile, objectPath, service, url{*parsedUrl}, id,
551 name](const boost::system::error_code ec) {
552 if (ec)
553 {
554 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
555 if (ec.value() ==
556 boost::system::linux_error::bad_request_descriptor)
557 {
558 messages::resourceNotFound(asyncResp->res, "Certificate", id);
559 return;
560 }
561 messages::internalError(asyncResp->res);
562 return;
563 }
564 getCertificateProperties(asyncResp, objectPath, service, id, url, name);
565 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
566 << certFile->getCertFilePath();
567 },
568 service, objectPath, certs::certReplaceIntf, "Replace",
569 certFile->getCertFilePath());
570}
571
Ed Tanouscf9e4172022-12-21 09:30:16 -0800572// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800573static std::unique_ptr<sdbusplus::bus::match_t> csrMatcher;
574/**
575 * @brief Read data from CSR D-bus object and set to response
576 *
577 * @param[in] asyncResp Shared pointer to the response message
578 * @param[in] certURI Link to certifiate collection URI
579 * @param[in] service D-Bus service name
580 * @param[in] certObjPath certificate D-Bus object path
581 * @param[in] csrObjPath CSR D-Bus object path
582 * @return None
583 */
584static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
585 const std::string& certURI, const std::string& service,
586 const std::string& certObjPath,
587 const std::string& csrObjPath)
588{
589 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
590 << " CSRObjectPath=" << csrObjPath
591 << " service=" << service;
592 crow::connections::systemBus->async_method_call(
593 [asyncResp, certURI](const boost::system::error_code ec,
594 const std::string& csr) {
595 if (ec)
596 {
597 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
598 messages::internalError(asyncResp->res);
599 return;
600 }
601 if (csr.empty())
602 {
603 BMCWEB_LOG_ERROR << "CSR read is empty";
604 messages::internalError(asyncResp->res);
605 return;
606 }
607 asyncResp->res.jsonValue["CSRString"] = csr;
608 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
609 certURI;
610 },
611 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
612}
613
614inline void
615 handleGenerateCSRAction(App& app, const crow::Request& req,
616 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
617{
618 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
619 {
620 return;
621 }
622 static const int rsaKeyBitLength = 2048;
623
624 // Required parameters
625 std::string city;
626 std::string commonName;
627 std::string country;
628 std::string organization;
629 std::string organizationalUnit;
630 std::string state;
631 nlohmann::json certificateCollection;
632
633 // Optional parameters
634 std::optional<std::vector<std::string>> optAlternativeNames =
635 std::vector<std::string>();
636 std::optional<std::string> optContactPerson = "";
637 std::optional<std::string> optChallengePassword = "";
638 std::optional<std::string> optEmail = "";
639 std::optional<std::string> optGivenName = "";
640 std::optional<std::string> optInitials = "";
641 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
642 std::optional<std::string> optKeyCurveId = "secp384r1";
643 std::optional<std::string> optKeyPairAlgorithm = "EC";
644 std::optional<std::vector<std::string>> optKeyUsage =
645 std::vector<std::string>();
646 std::optional<std::string> optSurname = "";
647 std::optional<std::string> optUnstructuredName = "";
648 if (!json_util::readJsonAction(
649 req, asyncResp->res, "City", city, "CommonName", commonName,
650 "ContactPerson", optContactPerson, "Country", country,
651 "Organization", organization, "OrganizationalUnit",
652 organizationalUnit, "State", state, "CertificateCollection",
653 certificateCollection, "AlternativeNames", optAlternativeNames,
654 "ChallengePassword", optChallengePassword, "Email", optEmail,
655 "GivenName", optGivenName, "Initials", optInitials, "KeyBitLength",
656 optKeyBitLength, "KeyCurveId", optKeyCurveId, "KeyPairAlgorithm",
657 optKeyPairAlgorithm, "KeyUsage", optKeyUsage, "Surname", optSurname,
658 "UnstructuredName", optUnstructuredName))
659 {
660 return;
661 }
662
663 // bmcweb has no way to store or decode a private key challenge
664 // password, which will likely cause bmcweb to crash on startup
665 // if this is not set on a post so not allowing the user to set
666 // value
667 if (!optChallengePassword->empty())
668 {
669 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
670 "ChallengePassword");
671 return;
672 }
673
674 std::string certURI;
675 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
676 "@odata.id", certURI))
677 {
678 return;
679 }
680
681 std::string objectPath;
682 std::string service;
683 if (certURI.starts_with(
684 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
685 {
686 objectPath = certs::httpsObjectPath;
687 service = certs::httpsServiceName;
688 }
689 else if (certURI.starts_with(
690 "/redfish/v1/AccountService/LDAP/Certificates"))
691 {
692 objectPath = certs::ldapObjectPath;
693 service = certs::ldapServiceName;
694 }
695 else
696 {
697 messages::actionParameterNotSupported(
698 asyncResp->res, "CertificateCollection", "GenerateCSR");
699 return;
700 }
701
702 // supporting only EC and RSA algorithm
703 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
704 {
705 messages::actionParameterNotSupported(
706 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
707 return;
708 }
709
710 // supporting only 2048 key bit length for RSA algorithm due to
711 // time consumed in generating private key
712 if (*optKeyPairAlgorithm == "RSA" && *optKeyBitLength != rsaKeyBitLength)
713 {
714 messages::propertyValueNotInList(
715 asyncResp->res, std::to_string(*optKeyBitLength), "KeyBitLength");
716 return;
717 }
718
719 // validate KeyUsage supporting only 1 type based on URL
720 if (certURI.starts_with(
721 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
722 {
723 if (optKeyUsage->empty())
724 {
725 optKeyUsage->push_back("ServerAuthentication");
726 }
727 else if (optKeyUsage->size() == 1)
728 {
729 if ((*optKeyUsage)[0] != "ServerAuthentication")
730 {
731 messages::propertyValueNotInList(asyncResp->res,
732 (*optKeyUsage)[0], "KeyUsage");
733 return;
734 }
735 }
736 else
737 {
738 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage",
739 "GenerateCSR");
740 return;
741 }
742 }
743 else if (certURI.starts_with(
744 "/redfish/v1/AccountService/LDAP/Certificates"))
745 {
746 if (optKeyUsage->empty())
747 {
748 optKeyUsage->push_back("ClientAuthentication");
749 }
750 else if (optKeyUsage->size() == 1)
751 {
752 if ((*optKeyUsage)[0] != "ClientAuthentication")
753 {
754 messages::propertyValueNotInList(asyncResp->res,
755 (*optKeyUsage)[0], "KeyUsage");
756 return;
757 }
758 }
759 else
760 {
761 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage",
762 "GenerateCSR");
763 return;
764 }
765 }
766
767 // Only allow one CSR matcher at a time so setting retry
768 // time-out and timer expiry to 10 seconds for now.
769 static const int timeOut = 10;
770 if (csrMatcher)
771 {
772 messages::serviceTemporarilyUnavailable(asyncResp->res,
773 std::to_string(timeOut));
774 return;
775 }
776
777 // Make this static so it survives outside this method
778 static boost::asio::steady_timer timeout(*req.ioService);
779 timeout.expires_after(std::chrono::seconds(timeOut));
780 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
781 csrMatcher = nullptr;
782 if (ec)
783 {
784 // operation_aborted is expected if timer is canceled
785 // before completion.
786 if (ec != boost::asio::error::operation_aborted)
787 {
788 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
789 }
790 return;
791 }
792 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
793 messages::internalError(asyncResp->res);
794 });
795
796 // create a matcher to wait on CSR object
797 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
798 std::string match("type='signal',"
799 "interface='org.freedesktop.DBus.ObjectManager',"
800 "path='" +
801 objectPath +
802 "',"
803 "member='InterfacesAdded'");
804 csrMatcher = std::make_unique<sdbusplus::bus::match_t>(
805 *crow::connections::systemBus, match,
806 [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) {
807 timeout.cancel();
808 if (m.is_method_error())
809 {
810 BMCWEB_LOG_ERROR << "Dbus method error!!!";
811 messages::internalError(asyncResp->res);
812 return;
813 }
814
815 dbus::utility::DBusInteracesMap interfacesProperties;
816
817 sdbusplus::message::object_path csrObjectPath;
818 m.read(csrObjectPath, interfacesProperties);
819 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
820 for (const auto& interface : interfacesProperties)
821 {
822 if (interface.first == "xyz.openbmc_project.Certs.CSR")
823 {
824 getCSR(asyncResp, certURI, service, objectPath,
825 csrObjectPath.str);
826 break;
827 }
828 }
829 });
830 crow::connections::systemBus->async_method_call(
831 [asyncResp](const boost::system::error_code ec, const std::string&) {
832 if (ec)
833 {
834 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
835 messages::internalError(asyncResp->res);
836 return;
837 }
838 },
839 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
840 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
841 commonName, *optContactPerson, country, *optEmail, *optGivenName,
842 *optInitials, *optKeyBitLength, *optKeyCurveId, *optKeyPairAlgorithm,
843 *optKeyUsage, organization, organizationalUnit, state, *optSurname,
844 *optUnstructuredName);
845}
846
847inline void requestRoutesCertificateService(App& app)
848{
849 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
850 .privileges(redfish::privileges::getCertificateService)
851 .methods(boost::beast::http::verb::get)(
852 std::bind_front(handleCertificateServiceGet, std::ref(app)));
853
854 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
855 .privileges(redfish::privileges::getCertificateLocations)
856 .methods(boost::beast::http::verb::get)(
857 std::bind_front(handleCertificateLocationsGet, std::ref(app)));
858
George Liu0fda0f12021-11-16 10:06:17 +0800859 BMCWEB_ROUTE(
860 app,
861 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700862 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700863 .methods(boost::beast::http::verb::post)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800864 std::bind_front(handleReplaceCertificateAction, std::ref(app)));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600865
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800866 BMCWEB_ROUTE(
867 app,
868 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
869 .privileges(redfish::privileges::postCertificateService)
870 .methods(boost::beast::http::verb::post)(
871 std::bind_front(handleGenerateCSRAction, std::ref(app)));
872} // requestRoutesCertificateService
873
874inline void handleHTTPSCertificateCollectionGet(
875 App& app, const crow::Request& req,
876 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
877{
878 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
879 {
880 return;
881 }
882
883 asyncResp->res.jsonValue["@odata.id"] =
884 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
885 asyncResp->res.jsonValue["@odata.type"] =
886 "#CertificateCollection.CertificateCollection";
887 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
888 asyncResp->res.jsonValue["Description"] =
889 "A Collection of HTTPS certificate instances";
890
891 getCertificateList(asyncResp, certs::httpsObjectPath,
892 "/Members"_json_pointer,
893 "/Members@odata.count"_json_pointer);
894}
895
896inline void handleHTTPSCertificateCollectionPost(
897 App& app, const crow::Request& req,
898 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
899{
900 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
901 {
902 return;
903 }
904 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
905
906 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
907 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
908
909 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
910
911 if (certFileBody.empty())
912 {
913 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
914 messages::unrecognizedRequestBody(asyncResp->res);
915 return;
916 }
917
918 std::shared_ptr<CertificateFile> certFile =
919 std::make_shared<CertificateFile>(certFileBody);
920
921 crow::connections::systemBus->async_method_call(
922 [asyncResp, certFile](const boost::system::error_code ec,
923 const std::string& objectPath) {
924 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700925 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800926 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Ed Tanous002d39b2022-05-31 08:59:27 -0700927 messages::internalError(asyncResp->res);
928 return;
929 }
930
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800931 sdbusplus::message::object_path path(objectPath);
932 std::string certId = path.filename();
933 const boost::urls::url certURL = crow::utility::urlFromPieces(
934 "redfish", "v1", "Managers", "bmc", "NetworkProtocol", "HTTPS",
935 "Certificates", certId);
936 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
937 certId, certURL, "HTTPS Certificate");
938 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
939 << certFile->getCertFilePath();
940 },
941 certs::httpsServiceName, certs::httpsObjectPath, certs::certInstallIntf,
942 "Install", certFile->getCertFilePath());
943}
Ed Tanous002d39b2022-05-31 08:59:27 -0700944
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800945inline void handleHTTPSCertificateGet(
946 App& app, const crow::Request& req,
947 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
948{
949 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
950 {
951 return;
952 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700953
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800954 BMCWEB_LOG_DEBUG << "HTTPS Certificate ID=" << id;
955 const boost::urls::url certURL = crow::utility::urlFromPieces(
956 "redfish", "v1", "Managers", "bmc", "NetworkProtocol", "HTTPS",
957 "Certificates", id);
958 std::string objPath =
959 sdbusplus::message::object_path(certs::httpsObjectPath) / id;
960 getCertificateProperties(asyncResp, objPath, certs::httpsServiceName, id,
961 certURL, "HTTPS Certificate");
962}
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700963
964inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600965{
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800966 BMCWEB_ROUTE(app,
967 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
968 .privileges(redfish::privileges::getCertificateCollection)
969 .methods(boost::beast::http::verb::get)(std::bind_front(
970 handleHTTPSCertificateCollectionGet, std::ref(app)));
971
972 BMCWEB_ROUTE(app,
973 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
974 .privileges(redfish::privileges::postCertificateCollection)
975 .methods(boost::beast::http::verb::post)(std::bind_front(
976 handleHTTPSCertificateCollectionPost, std::ref(app)));
977
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700978 BMCWEB_ROUTE(
979 app,
980 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700981 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800982 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800983 std::bind_front(handleHTTPSCertificateGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700984}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600985
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800986inline void handleLDAPCertificateCollectionGet(
987 App& app, const crow::Request& req,
988 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600989{
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800990 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
991 {
992 return;
993 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700994
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800995 asyncResp->res.jsonValue["@odata.id"] =
996 "/redfish/v1/AccountService/LDAP/Certificates";
997 asyncResp->res.jsonValue["@odata.type"] =
998 "#CertificateCollection.CertificateCollection";
999 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1000 asyncResp->res.jsonValue["Description"] =
1001 "A Collection of LDAP certificate instances";
Ed Tanous002d39b2022-05-31 08:59:27 -07001002
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001003 getCertificateList(asyncResp, certs::ldapObjectPath,
1004 "/Members"_json_pointer,
1005 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001006}
Marri Devender Rao37cce912019-02-20 01:05:22 -06001007
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001008inline void handleLDAPCertificateCollectionPost(
1009 App& app, const crow::Request& req,
1010 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001011{
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001012 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1013 {
1014 return;
1015 }
1016 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1017
1018 if (certFileBody.empty())
1019 {
1020 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1021 messages::unrecognizedRequestBody(asyncResp->res);
1022 return;
1023 }
1024
1025 std::shared_ptr<CertificateFile> certFile =
1026 std::make_shared<CertificateFile>(certFileBody);
1027
1028 crow::connections::systemBus->async_method_call(
1029 [asyncResp, certFile](const boost::system::error_code ec,
1030 const std::string& objectPath) {
1031 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001032 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001033 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1034 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001035 return;
1036 }
1037
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001038 sdbusplus::message::object_path path(objectPath);
1039 std::string certId = path.filename();
1040 const boost::urls::url certURL = crow::utility::urlFromPieces(
1041 "redfish", "v1", "AccountService", "LDAP", "Certificates", certId);
1042 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1043 certId, certURL, "LDAP Certificate");
1044 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1045 << certFile->getCertFilePath();
1046 },
1047 certs::ldapServiceName, certs::ldapObjectPath, certs::certInstallIntf,
1048 "Install", certFile->getCertFilePath());
1049}
Ed Tanous002d39b2022-05-31 08:59:27 -07001050
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001051inline void handleLDAPCertificateGet(
1052 App& app, const crow::Request& req,
1053 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1054{
1055 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1056 {
1057 return;
1058 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001059
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001060 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << id;
1061 const boost::urls::url certURL = crow::utility::urlFromPieces(
1062 "redfish", "v1", "AccountService", "LDAP", "Certificates", id);
1063 std::string objPath =
1064 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1065 getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id,
1066 certURL, "LDAP Certificate");
1067}
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001068
Jiaqing Zhao99612242022-09-29 15:31:09 +08001069inline void handleLDAPCertificateDelete(
1070 App& app, const crow::Request& req,
1071 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1072{
1073 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1074 {
1075 return;
1076 }
1077
1078 BMCWEB_LOG_DEBUG << "Delete LDAP Certificate ID=" << id;
1079 std::string objPath =
1080 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1081
1082 deleteCertificate(asyncResp, certs::ldapServiceName, objPath);
1083}
1084
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001085inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001086{
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001087 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1088 .privileges(redfish::privileges::getCertificateCollection)
1089 .methods(boost::beast::http::verb::get)(
1090 std::bind_front(handleLDAPCertificateCollectionGet, std::ref(app)));
1091
1092 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1093 .privileges(redfish::privileges::postCertificateCollection)
1094 .methods(boost::beast::http::verb::post)(std::bind_front(
1095 handleLDAPCertificateCollectionPost, std::ref(app)));
1096
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001097 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001098 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001099 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001100 std::bind_front(handleLDAPCertificateGet, std::ref(app)));
Jiaqing Zhao99612242022-09-29 15:31:09 +08001101
1102 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
1103 .privileges(redfish::privileges::deleteCertificate)
1104 .methods(boost::beast::http::verb::delete_)(
1105 std::bind_front(handleLDAPCertificateDelete, std::ref(app)));
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001106} // requestRoutesLDAPCertificate
1107
1108inline void handleTrustStoreCertificateCollectionGet(
1109 App& app, const crow::Request& req,
1110 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1111{
1112 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1113 {
1114 return;
1115 }
1116
1117 asyncResp->res.jsonValue["@odata.id"] =
1118 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1119 asyncResp->res.jsonValue["@odata.type"] =
1120 "#CertificateCollection.CertificateCollection";
1121 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1122 asyncResp->res.jsonValue["Description"] =
1123 "A Collection of TrustStore certificate instances";
1124
1125 getCertificateList(asyncResp, certs::authorityObjectPath,
1126 "/Members"_json_pointer,
1127 "/Members@odata.count"_json_pointer);
1128}
1129
1130inline void handleTrustStoreCertificateCollectionPost(
1131 App& app, const crow::Request& req,
1132 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1133{
1134 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1135 {
1136 return;
1137 }
1138 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1139
1140 if (certFileBody.empty())
1141 {
1142 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1143 messages::unrecognizedRequestBody(asyncResp->res);
1144 return;
1145 }
1146
1147 std::shared_ptr<CertificateFile> certFile =
1148 std::make_shared<CertificateFile>(certFileBody);
1149 crow::connections::systemBus->async_method_call(
1150 [asyncResp, certFile](const boost::system::error_code ec,
1151 const std::string& objectPath) {
1152 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001153 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001154 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1155 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001156 return;
1157 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001158
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001159 sdbusplus::message::object_path path(objectPath);
1160 std::string certId = path.filename();
1161 const boost::urls::url certURL =
1162 crow::utility::urlFromPieces("redfish", "v1", "Managers", "bmc",
1163 "Truststore", "Certificates", certId);
1164 getCertificateProperties(asyncResp, objectPath,
1165 certs::authorityServiceName, certId, certURL,
1166 "TrustStore Certificate");
1167 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1168 << certFile->getCertFilePath();
1169 },
1170 certs::authorityServiceName, certs::authorityObjectPath,
1171 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1172}
1173
1174inline void handleTrustStoreCertificateGet(
1175 App& app, const crow::Request& req,
1176 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1177{
1178 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1179 {
1180 return;
1181 }
1182
1183 BMCWEB_LOG_DEBUG << "Truststore Certificate ID=" << id;
1184 const boost::urls::url certURL = crow::utility::urlFromPieces(
1185 "redfish", "v1", "Managers", "bmc", "Truststore", "Certificates", id);
1186 std::string objPath =
1187 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1188 getCertificateProperties(asyncResp, objPath, certs::authorityServiceName,
1189 id, certURL, "TrustStore Certificate");
1190}
1191
1192inline void handleTrustStoreCertificateDelete(
1193 App& app, const crow::Request& req,
1194 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1195{
1196 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1197 {
1198 return;
1199 }
1200
1201 BMCWEB_LOG_DEBUG << "Delete TrustStore Certificate ID=" << id;
1202 std::string objPath =
1203 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1204
Jiaqing Zhao7a3a8f72022-09-29 15:15:58 +08001205 deleteCertificate(asyncResp, certs::authorityServiceName, objPath);
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001206}
1207
1208inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001209{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001210 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001211 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001212 .methods(boost::beast::http::verb::get)(std::bind_front(
1213 handleTrustStoreCertificateCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001214
1215 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001216 .privileges(redfish::privileges::postCertificateCollection)
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001217 .methods(boost::beast::http::verb::post)(std::bind_front(
1218 handleTrustStoreCertificateCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07001219
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001220 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001221 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001222 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001223 std::bind_front(handleTrustStoreCertificateGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001224
1225 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001226 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001227 .methods(boost::beast::http::verb::delete_)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001228 std::bind_front(handleTrustStoreCertificateDelete, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001229} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001230} // namespace redfish