blob: ebd9436f9bcd0ad839be927f56ada0a74f4fb913 [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001#pragma once
2
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +02003#include "utils/dbus_utils.hpp"
4
John Edward Broadbent7e860f12021-04-08 15:57:16 -07005#include <app.hpp>
Ed Tanousd9f6c622022-03-17 09:12:17 -07006#include <async_resp.hpp>
Jiaqing Zhao90d2d1e2022-04-13 17:01:57 +08007#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -08008#include <dbus_utility.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
Marri Devender Rao5968cae2019-01-21 10:27:12 -060015namespace redfish
16{
17namespace certs
18{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050019constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
20constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
21constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
22constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
23constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
24constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050025constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060026 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050027constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060028 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050029constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050030 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +080031constexpr char const* baseObjectPath = "/xyz/openbmc_project/certs";
32constexpr char const* httpsObjectPath =
33 "/xyz/openbmc_project/certs/server/https";
34constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050035constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050036 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060037} // namespace certs
38
39/**
40 * The Certificate schema defines a Certificate Service which represents the
41 * actions available to manage certificates and links to where certificates
42 * are installed.
43 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060044
John Edward Broadbent7e860f12021-04-08 15:57:16 -070045// TODO: Issue#61 No entries are available for Certificate
46// service at https://www.dmtf.org/standards/redfish
47// "redfish standard registries". Need to modify after DMTF
48// publish Privilege details for certificate service
49
50inline void requestRoutesCertificateService(App& app)
51{
52 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
Ed Tanoused398212021-06-09 17:05:54 -070053 .privileges(redfish::privileges::getCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -070054 .methods(boost::beast::http::verb::get)(
55 [&app](const crow::Request& req,
56 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +000057 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -070058 {
59 return;
60 }
Ed Tanous14766872022-03-15 10:44:42 -070061
Ed Tanous002d39b2022-05-31 08:59:27 -070062 asyncResp->res.jsonValue["@odata.type"] =
63 "#CertificateService.v1_0_0.CertificateService";
64 asyncResp->res.jsonValue["@odata.id"] =
65 "/redfish/v1/CertificateService";
66 asyncResp->res.jsonValue["Id"] = "CertificateService";
67 asyncResp->res.jsonValue["Name"] = "Certificate Service";
68 asyncResp->res.jsonValue["Description"] =
69 "Actions available to manage certificates";
70 // /redfish/v1/CertificateService/CertificateLocations is something
71 // only ConfigureManager can access then only display when the user
72 // has permissions ConfigureManager
73 Privileges effectiveUserPrivileges =
74 redfish::getUserPrivileges(req.userRole);
75 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
76 effectiveUserPrivileges))
77 {
78 asyncResp->res.jsonValue["CertificateLocations"]["@odata.id"] =
79 "/redfish/v1/CertificateService/CertificateLocations";
80 }
81 asyncResp->res
82 .jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = {
83 {"target",
84 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"},
85 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
86 asyncResp->res
87 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
88 {"target",
89 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR"}};
Abhishek Patel72048782021-06-02 09:53:24 -050090 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070091} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060092
zhanghch058d1b46d2021-04-01 11:18:24 +080093inline std::string getCertificateFromReqBody(
94 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
95 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020096{
97 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
98
99 if (reqJson.is_discarded())
100 {
101 // We did not receive JSON request, proceed as it is RAW data
102 return req.body;
103 }
104
105 std::string certificate;
106 std::optional<std::string> certificateType = "PEM";
107
Willy Tu15ed6782021-12-14 11:03:16 -0800108 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
109 certificate, "CertificateType",
110 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200111 {
112 BMCWEB_LOG_ERROR << "Required parameters are missing";
113 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700114 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200115 }
116
117 if (*certificateType != "PEM")
118 {
119 messages::propertyValueNotInList(asyncResp->res, *certificateType,
120 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700121 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200122 }
123
124 return certificate;
125}
126
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600127/**
128 * Class to create a temporary certificate file for uploading to system
129 */
130class CertificateFile
131{
132 public:
133 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500134 CertificateFile(const CertificateFile&) = delete;
135 CertificateFile& operator=(const CertificateFile&) = delete;
136 CertificateFile(CertificateFile&&) = delete;
137 CertificateFile& operator=(CertificateFile&&) = delete;
Ed Tanous4e23a442022-06-06 09:57:26 -0700138 explicit CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600139 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700140 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700141 'e', 'r', 't', 's', '.', 'X',
142 'X', 'X', 'X', 'X', 'X', '\0'};
143 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800144 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600145 {
146 certDirectory = tempDirectory;
147 certificateFile = certDirectory / "cert.pem";
148 std::ofstream out(certificateFile, std::ofstream::out |
149 std::ofstream::binary |
150 std::ofstream::trunc);
151 out << certString;
152 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800153 BMCWEB_LOG_DEBUG << "Creating certificate file"
154 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600155 }
156 }
157 ~CertificateFile()
158 {
159 if (std::filesystem::exists(certDirectory))
160 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800161 BMCWEB_LOG_DEBUG << "Removing certificate file"
162 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000163 std::error_code ec;
164 std::filesystem::remove_all(certDirectory, ec);
165 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600166 {
167 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800168 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600169 }
170 }
171 }
172 std::string getCertFilePath()
173 {
174 return certificateFile;
175 }
176
177 private:
178 std::filesystem::path certificateFile;
179 std::filesystem::path certDirectory;
180};
181
Patrick Williams59d494e2022-07-22 19:26:55 -0500182static std::unique_ptr<sdbusplus::bus::match_t> csrMatcher;
Marri Devender Rao30215812019-03-18 08:59:21 -0500183/**
184 * @brief Read data from CSR D-bus object and set to response
185 *
186 * @param[in] asyncResp Shared pointer to the response message
187 * @param[in] certURI Link to certifiate collection URI
188 * @param[in] service D-Bus service name
189 * @param[in] certObjPath certificate D-Bus object path
190 * @param[in] csrObjPath CSR D-Bus object path
191 * @return None
192 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800193static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500194 const std::string& certURI, const std::string& service,
195 const std::string& certObjPath,
196 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500197{
198 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
199 << " CSRObjectPath=" << csrObjPath
200 << " service=" << service;
201 crow::connections::systemBus->async_method_call(
202 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500203 const std::string& csr) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700204 if (ec)
205 {
206 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
207 messages::internalError(asyncResp->res);
208 return;
209 }
210 if (csr.empty())
211 {
212 BMCWEB_LOG_ERROR << "CSR read is empty";
213 messages::internalError(asyncResp->res);
214 return;
215 }
216 asyncResp->res.jsonValue["CSRString"] = csr;
217 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
218 certURI;
Marri Devender Rao30215812019-03-18 08:59:21 -0500219 },
220 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
221}
222
223/**
224 * Action to Generate CSR
225 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700226inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500227{
George Liu0fda0f12021-11-16 10:06:17 +0800228 BMCWEB_ROUTE(
229 app,
230 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
Abhishek Patel5344ab82021-07-31 17:42:09 -0500231 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700232 .methods(boost::beast::http::verb::post)(
233 [&app](const crow::Request& req,
234 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000235 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700236 {
237 return;
238 }
239 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500240
Ed Tanous002d39b2022-05-31 08:59:27 -0700241 // Required parameters
242 std::string city;
243 std::string commonName;
244 std::string country;
245 std::string organization;
246 std::string organizationalUnit;
247 std::string state;
248 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800249
Ed Tanous002d39b2022-05-31 08:59:27 -0700250 // Optional parameters
251 std::optional<std::vector<std::string>> optAlternativeNames =
252 std::vector<std::string>();
253 std::optional<std::string> optContactPerson = "";
254 std::optional<std::string> optChallengePassword = "";
255 std::optional<std::string> optEmail = "";
256 std::optional<std::string> optGivenName = "";
257 std::optional<std::string> optInitials = "";
258 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
259 std::optional<std::string> optKeyCurveId = "secp384r1";
260 std::optional<std::string> optKeyPairAlgorithm = "EC";
261 std::optional<std::vector<std::string>> optKeyUsage =
262 std::vector<std::string>();
263 std::optional<std::string> optSurname = "";
264 std::optional<std::string> optUnstructuredName = "";
265 if (!json_util::readJsonAction(
266 req, asyncResp->res, "City", city, "CommonName", commonName,
267 "ContactPerson", optContactPerson, "Country", country,
268 "Organization", organization, "OrganizationalUnit",
269 organizationalUnit, "State", state, "CertificateCollection",
270 certificateCollection, "AlternativeNames", optAlternativeNames,
271 "ChallengePassword", optChallengePassword, "Email", optEmail,
272 "GivenName", optGivenName, "Initials", optInitials,
273 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
274 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
275 optKeyUsage, "Surname", optSurname, "UnstructuredName",
276 optUnstructuredName))
277 {
278 return;
279 }
George Liu0fda0f12021-11-16 10:06:17 +0800280
Ed Tanous002d39b2022-05-31 08:59:27 -0700281 // bmcweb has no way to store or decode a private key challenge
282 // password, which will likely cause bmcweb to crash on startup
283 // if this is not set on a post so not allowing the user to set
284 // value
285 if (!optChallengePassword->empty())
286 {
287 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
288 "ChallengePassword");
289 return;
290 }
George Liu0fda0f12021-11-16 10:06:17 +0800291
Ed Tanous002d39b2022-05-31 08:59:27 -0700292 std::string certURI;
293 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
294 "@odata.id", certURI))
295 {
296 return;
297 }
George Liu0fda0f12021-11-16 10:06:17 +0800298
Ed Tanous002d39b2022-05-31 08:59:27 -0700299 std::string objectPath;
300 std::string service;
Ed Tanous11ba3972022-07-11 09:50:41 -0700301 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700302 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
303 {
304 objectPath = certs::httpsObjectPath;
305 service = certs::httpsServiceName;
306 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700307 else if (certURI.starts_with(
308 "/redfish/v1/AccountService/LDAP/Certificates"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700309 {
310 objectPath = certs::ldapObjectPath;
311 service = certs::ldapServiceName;
312 }
313 else
314 {
315 messages::actionParameterNotSupported(
316 asyncResp->res, "CertificateCollection", "GenerateCSR");
317 return;
318 }
319
320 // supporting only EC and RSA algorithm
321 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
322 {
323 messages::actionParameterNotSupported(
324 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
325 return;
326 }
327
328 // supporting only 2048 key bit length for RSA algorithm due to
329 // time consumed in generating private key
330 if (*optKeyPairAlgorithm == "RSA" &&
331 *optKeyBitLength != rsaKeyBitLength)
332 {
333 messages::propertyValueNotInList(asyncResp->res,
334 std::to_string(*optKeyBitLength),
335 "KeyBitLength");
336 return;
337 }
338
339 // validate KeyUsage supporting only 1 type based on URL
Ed Tanous11ba3972022-07-11 09:50:41 -0700340 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700341 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
342 {
343 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800344 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700345 optKeyUsage->push_back("ServerAuthentication");
George Liu0fda0f12021-11-16 10:06:17 +0800346 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700347 else if (optKeyUsage->size() == 1)
George Liu0fda0f12021-11-16 10:06:17 +0800348 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700349 if ((*optKeyUsage)[0] != "ServerAuthentication")
350 {
351 messages::propertyValueNotInList(
352 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
353 return;
354 }
George Liu0fda0f12021-11-16 10:06:17 +0800355 }
356 else
357 {
358 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700359 asyncResp->res, "KeyUsage", "GenerateCSR");
George Liu0fda0f12021-11-16 10:06:17 +0800360 return;
361 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700362 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700363 else if (certURI.starts_with(
364 "/redfish/v1/AccountService/LDAP/Certificates"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700365 {
366 if (optKeyUsage->empty())
367 {
368 optKeyUsage->push_back("ClientAuthentication");
369 }
370 else if (optKeyUsage->size() == 1)
371 {
372 if ((*optKeyUsage)[0] != "ClientAuthentication")
373 {
374 messages::propertyValueNotInList(
375 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
376 return;
377 }
378 }
379 else
George Liu0fda0f12021-11-16 10:06:17 +0800380 {
381 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700382 asyncResp->res, "KeyUsage", "GenerateCSR");
383 return;
384 }
385 }
386
387 // Only allow one CSR matcher at a time so setting retry
388 // time-out and timer expiry to 10 seconds for now.
389 static const int timeOut = 10;
390 if (csrMatcher)
391 {
392 messages::serviceTemporarilyUnavailable(asyncResp->res,
393 std::to_string(timeOut));
394 return;
395 }
396
397 // Make this static so it survives outside this method
398 static boost::asio::steady_timer timeout(*req.ioService);
399 timeout.expires_after(std::chrono::seconds(timeOut));
400 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
401 csrMatcher = nullptr;
402 if (ec)
403 {
404 // operation_aborted is expected if timer is canceled
405 // before completion.
406 if (ec != boost::asio::error::operation_aborted)
407 {
408 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
409 }
410 return;
411 }
412 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
413 messages::internalError(asyncResp->res);
414 });
415
416 // create a matcher to wait on CSR object
417 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
418 std::string match("type='signal',"
419 "interface='org.freedesktop.DBus.ObjectManager',"
420 "path='" +
421 objectPath +
422 "',"
423 "member='InterfacesAdded'");
Patrick Williams59d494e2022-07-22 19:26:55 -0500424 csrMatcher = std::make_unique<sdbusplus::bus::match_t>(
Ed Tanous002d39b2022-05-31 08:59:27 -0700425 *crow::connections::systemBus, match,
Patrick Williams59d494e2022-07-22 19:26:55 -0500426 [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700427 timeout.cancel();
428 if (m.is_method_error())
429 {
430 BMCWEB_LOG_ERROR << "Dbus method error!!!";
431 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800432 return;
433 }
434
Ed Tanous002d39b2022-05-31 08:59:27 -0700435 dbus::utility::DBusInteracesMap interfacesProperties;
436
437 sdbusplus::message::object_path csrObjectPath;
438 m.read(csrObjectPath, interfacesProperties);
439 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
Ed Tanous02cad962022-06-30 16:50:15 -0700440 for (const auto& interface : interfacesProperties)
George Liu0fda0f12021-11-16 10:06:17 +0800441 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700442 if (interface.first == "xyz.openbmc_project.Certs.CSR")
443 {
444 getCSR(asyncResp, certURI, service, objectPath,
445 csrObjectPath.str);
446 break;
447 }
448 }
449 });
450 crow::connections::systemBus->async_method_call(
451 [asyncResp](const boost::system::error_code ec,
452 const std::string&) {
453 if (ec)
454 {
455 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
456 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800457 return;
458 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700459 },
460 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
461 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
462 commonName, *optContactPerson, country, *optEmail, *optGivenName,
463 *optInitials, *optKeyBitLength, *optKeyCurveId,
464 *optKeyPairAlgorithm, *optKeyUsage, organization,
465 organizationalUnit, state, *optSurname, *optUnstructuredName);
George Liu0fda0f12021-11-16 10:06:17 +0800466 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700467} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500468
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600469/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500470 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600471 *
472 * @param[in] asyncResp Shared pointer to the response message
473 * @param[in] str Issuer/Subject value in key=value pairs
474 * @param[in] type Issuer/Subject
475 * @return None
476 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500477static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600478 const std::string_view value)
479{
480 // example: O=openbmc-project.xyz,CN=localhost
481 std::string_view::iterator i = value.begin();
482 while (i != value.end())
483 {
484 std::string_view::iterator tokenBegin = i;
485 while (i != value.end() && *i != '=')
486 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530487 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600488 }
489 if (i == value.end())
490 {
491 break;
492 }
Ed Tanous271584a2019-07-09 16:24:22 -0700493 const std::string_view key(tokenBegin,
494 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530495 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600496 tokenBegin = i;
497 while (i != value.end() && *i != ',')
498 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530499 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600500 }
Ed Tanous271584a2019-07-09 16:24:22 -0700501 const std::string_view val(tokenBegin,
502 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600503 if (key == "L")
504 {
505 out["City"] = val;
506 }
507 else if (key == "CN")
508 {
509 out["CommonName"] = val;
510 }
511 else if (key == "C")
512 {
513 out["Country"] = val;
514 }
515 else if (key == "O")
516 {
517 out["Organization"] = val;
518 }
519 else if (key == "OU")
520 {
521 out["OrganizationalUnit"] = val;
522 }
523 else if (key == "ST")
524 {
525 out["State"] = val;
526 }
527 // skip comma character
528 if (i != value.end())
529 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530530 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600531 }
532 }
533}
534
535/**
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800536 * @brief Retrieve the installed certificate list
537 *
538 * @param[in] asyncResp Shared pointer to the response message
539 * @param[in] basePath DBus object path to search
540 * @param[in] listPtr Json pointer to the list in asyncResp
541 * @param[in] countPtr Json pointer to the count in asyncResp
542 * @return None
543 */
544static void
545 getCertificateList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
546 const std::string& basePath,
547 const nlohmann::json::json_pointer& listPtr,
548 const nlohmann::json::json_pointer& countPtr)
549{
550 crow::connections::systemBus->async_method_call(
551 [asyncResp, listPtr, countPtr](
552 const boost::system::error_code ec,
553 const dbus::utility::MapperGetSubTreePathsResponse& certPaths) {
554 if (ec)
555 {
556 BMCWEB_LOG_ERROR << "Certificate collection query failed: " << ec;
557 messages::internalError(asyncResp->res);
558 return;
559 }
560
561 nlohmann::json& links = asyncResp->res.jsonValue[listPtr];
562 links = nlohmann::json::array();
563 for (const auto& certPath : certPaths)
564 {
565 sdbusplus::message::object_path objPath(certPath);
566 std::string certId = objPath.filename();
567 if (certId.empty())
568 {
569 BMCWEB_LOG_ERROR << "Invalid certificate objPath " << certPath;
570 continue;
571 }
572
573 boost::urls::url certURL;
574 if (objPath.parent_path() == certs::httpsObjectPath)
575 {
576 certURL = crow::utility::urlFromPieces(
577 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
578 "HTTPS", "Certificates", certId);
579 }
580 else if (objPath.parent_path() == certs::ldapObjectPath)
581 {
582 certURL = crow::utility::urlFromPieces("redfish", "v1",
583 "AccountService", "LDAP",
584 "Certificates", certId);
585 }
586 else if (objPath.parent_path() == certs::authorityObjectPath)
587 {
588 certURL = crow::utility::urlFromPieces(
589 "redfish", "v1", "Managers", "bmc", "Truststore",
590 "Certificates", certId);
591 }
592 else
593 {
594 continue;
595 }
596
597 nlohmann::json::object_t link;
598 link["@odata.id"] = certURL;
599 links.emplace_back(std::move(link));
600 }
601
602 asyncResp->res.jsonValue[countPtr] = links.size();
603 },
604 "xyz.openbmc_project.ObjectMapper",
605 "/xyz/openbmc_project/object_mapper",
606 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", basePath, 0,
607 std::array<const char*, 1>{certs::certPropIntf});
608}
609
610/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600611 * @brief Retrieve the certificates properties and append to the response
612 * message
613 *
614 * @param[in] asyncResp Shared pointer to the response message
615 * @param[in] objectPath Path of the D-Bus service object
616 * @param[in] certId Id of the certificate
617 * @param[in] certURL URL of the certificate object
618 * @param[in] name name of the certificate
619 * @return None
620 */
621static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800622 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800623 const std::string& objectPath, const std::string& service,
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800624 const std::string& certId, const boost::urls::url& certURL,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800625 const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600626{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600627 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
628 << " certId=" << certId << " certURl=" << certURL;
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200629 sdbusplus::asio::getAllProperties(
630 *crow::connections::systemBus, service, objectPath, certs::certPropIntf,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800631 [asyncResp, certURL, certId,
632 name](const boost::system::error_code ec,
633 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700634 if (ec)
635 {
636 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800637 messages::resourceNotFound(asyncResp->res, "Certificate", certId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700638 return;
639 }
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200640
641 const std::string* certificateString = nullptr;
642 const std::vector<std::string>* keyUsage = nullptr;
643 const std::string* issuer = nullptr;
644 const std::string* subject = nullptr;
645 const uint64_t* validNotAfter = nullptr;
646 const uint64_t* validNotBefore = nullptr;
647
648 const bool success = sdbusplus::unpackPropertiesNoThrow(
649 dbus_utils::UnpackErrorPrinter(), properties, "CertificateString",
650 certificateString, "KeyUsage", keyUsage, "Issuer", issuer,
651 "Subject", subject, "ValidNotAfter", validNotAfter,
652 "ValidNotBefore", validNotBefore);
653
654 if (!success)
655 {
656 messages::internalError(asyncResp->res);
657 return;
658 }
659
Ed Tanous002d39b2022-05-31 08:59:27 -0700660 asyncResp->res.jsonValue["@odata.id"] = certURL;
661 asyncResp->res.jsonValue["@odata.type"] =
662 "#Certificate.v1_0_0.Certificate";
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800663 asyncResp->res.jsonValue["Id"] = certId;
Ed Tanous002d39b2022-05-31 08:59:27 -0700664 asyncResp->res.jsonValue["Name"] = name;
665 asyncResp->res.jsonValue["Description"] = name;
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200666 asyncResp->res.jsonValue["CertificateString"] = "";
667 asyncResp->res.jsonValue["KeyUsage"] = nlohmann::json::array();
668
669 if (certificateString != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -0700670 {
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200671 asyncResp->res.jsonValue["CertificateString"] = *certificateString;
Ed Tanous002d39b2022-05-31 08:59:27 -0700672 }
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200673
674 if (keyUsage != nullptr)
675 {
676 asyncResp->res.jsonValue["KeyUsage"] = *keyUsage;
677 }
678
679 if (issuer != nullptr)
680 {
681 updateCertIssuerOrSubject(asyncResp->res.jsonValue["Issuer"],
682 *issuer);
683 }
684
685 if (subject != nullptr)
686 {
687 updateCertIssuerOrSubject(asyncResp->res.jsonValue["Subject"],
688 *subject);
689 }
690
691 if (validNotAfter != nullptr)
692 {
693 asyncResp->res.jsonValue["ValidNotAfter"] =
694 redfish::time_utils::getDateTimeUint(*validNotAfter);
695 }
696
697 if (validNotBefore != nullptr)
698 {
699 asyncResp->res.jsonValue["ValidNotBefore"] =
700 redfish::time_utils::getDateTimeUint(*validNotBefore);
701 }
702
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800703 asyncResp->res.addHeader(
Ed Tanousd9f6c622022-03-17 09:12:17 -0700704 boost::beast::http::field::location,
705 std::string_view(certURL.data(), certURL.size()));
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200706 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600707}
708
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600709/**
710 * Action to replace an existing certificate
711 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700712inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600713{
George Liu0fda0f12021-11-16 10:06:17 +0800714 BMCWEB_ROUTE(
715 app,
716 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700717 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700718 .methods(boost::beast::http::verb::post)(
719 [&app](const crow::Request& req,
720 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000721 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700722 {
723 return;
724 }
725 std::string certificate;
726 nlohmann::json certificateUri;
727 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600728
Ed Tanous002d39b2022-05-31 08:59:27 -0700729 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
730 certificate, "CertificateUri",
731 certificateUri, "CertificateType",
732 certificateType))
733 {
734 BMCWEB_LOG_ERROR << "Required parameters are missing";
735 messages::internalError(asyncResp->res);
736 return;
737 }
738
739 if (!certificateType)
740 {
741 // should never happen, but it never hurts to be paranoid.
742 return;
743 }
744 if (certificateType != "PEM")
745 {
746 messages::actionParameterNotSupported(
747 asyncResp->res, "CertificateType", "ReplaceCertificate");
748 return;
749 }
750
751 std::string certURI;
752 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
753 "@odata.id", certURI))
754 {
755 messages::actionParameterMissing(
756 asyncResp->res, "ReplaceCertificate", "CertificateUri");
757 return;
758 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800759 BMCWEB_LOG_INFO << "Certificate URI to replace: " << certURI;
Ed Tanous002d39b2022-05-31 08:59:27 -0700760
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800761 boost::urls::result<boost::urls::url_view> parsedUrl =
762 boost::urls::parse_relative_ref(certURI);
763 if (!parsedUrl)
Ed Tanous002d39b2022-05-31 08:59:27 -0700764 {
765 messages::actionParameterValueFormatError(asyncResp->res, certURI,
766 "CertificateUri",
767 "ReplaceCertificate");
768 return;
769 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800770
771 std::string id;
772 sdbusplus::message::object_path objectPath;
Ed Tanous002d39b2022-05-31 08:59:27 -0700773 std::string name;
774 std::string service;
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800775 if (crow::utility::readUrlSegments(
776 *parsedUrl, "redfish", "v1", "Managers", "bmc",
777 "NetworkProtocol", "HTTPS", "Certificates", std::ref(id)))
Ed Tanous002d39b2022-05-31 08:59:27 -0700778 {
779 objectPath =
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800780 sdbusplus::message::object_path(certs::httpsObjectPath) / id;
Ed Tanous002d39b2022-05-31 08:59:27 -0700781 name = "HTTPS certificate";
782 service = certs::httpsServiceName;
783 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800784 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
785 "AccountService", "LDAP",
786 "Certificates", std::ref(id)))
Ed Tanous002d39b2022-05-31 08:59:27 -0700787 {
788 objectPath =
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800789 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
Ed Tanous002d39b2022-05-31 08:59:27 -0700790 name = "LDAP certificate";
791 service = certs::ldapServiceName;
792 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800793 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
794 "Managers", "bmc", "Truststore",
795 "Certificates", std::ref(id)))
Ed Tanous002d39b2022-05-31 08:59:27 -0700796 {
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800797 objectPath =
798 sdbusplus::message::object_path(certs::authorityObjectPath) /
799 id;
Ed Tanous002d39b2022-05-31 08:59:27 -0700800 name = "TrustStore certificate";
801 service = certs::authorityServiceName;
802 }
803 else
804 {
805 messages::actionParameterNotSupported(
806 asyncResp->res, "CertificateUri", "ReplaceCertificate");
807 return;
808 }
809
810 std::shared_ptr<CertificateFile> certFile =
811 std::make_shared<CertificateFile>(certificate);
812 crow::connections::systemBus->async_method_call(
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800813 [asyncResp, certFile, objectPath, service, url{*parsedUrl}, id,
Ed Tanous002d39b2022-05-31 08:59:27 -0700814 name](const boost::system::error_code ec) {
815 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700816 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700817 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
818 if (ec.value() ==
819 boost::system::linux_error::bad_request_descriptor)
820 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800821 messages::resourceNotFound(asyncResp->res, "Certificate",
822 id);
Ed Tanous002d39b2022-05-31 08:59:27 -0700823 return;
824 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700825 messages::internalError(asyncResp->res);
826 return;
827 }
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800828 getCertificateProperties(asyncResp, objectPath, service, id, url,
829 name);
Ed Tanous002d39b2022-05-31 08:59:27 -0700830 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
831 << certFile->getCertFilePath();
832 },
833 service, objectPath, certs::certReplaceIntf, "Replace",
834 certFile->getCertFilePath());
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700835 });
836} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600837
838/**
839 * Certificate resource describes a certificate used to prove the identity
840 * of a component, account or service.
841 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700842
843inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600844{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700845 BMCWEB_ROUTE(
846 app,
847 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700848 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800849 .methods(boost::beast::http::verb::get)(
850 [&app](const crow::Request& req,
851 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
852 const std::string& id) -> void {
853 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
854 {
855 return;
856 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600857
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800858 BMCWEB_LOG_DEBUG << "HTTPS Certificate ID=" << id;
859 const boost::urls::url certURL = crow::utility::urlFromPieces(
860 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
861 "HTTPS", "Certificates", id);
862 std::string objPath =
863 sdbusplus::message::object_path(certs::httpsObjectPath) /
864 id;
865 getCertificateProperties(asyncResp, objPath,
866 certs::httpsServiceName, id, certURL,
867 "HTTPS Certificate");
868 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700869}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600870
871/**
872 * Collection of HTTPS certificates
873 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700874inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600875{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700876 BMCWEB_ROUTE(app,
877 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700878 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700879 .methods(boost::beast::http::verb::get)(
880 [&app](const crow::Request& req,
881 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000882 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700883 {
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
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800895 getCertificateList(asyncResp, certs::httpsObjectPath,
896 "/Members"_json_pointer,
897 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700898 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600899
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700900 BMCWEB_ROUTE(app,
901 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700902 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700903 .methods(boost::beast::http::verb::post)(
904 [&app](const crow::Request& req,
905 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000906 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700907 {
908 return;
909 }
910 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
911
912 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
913 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
914
915 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
916
917 if (certFileBody.empty())
918 {
919 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
920 messages::unrecognizedRequestBody(asyncResp->res);
921 return;
922 }
923
924 std::shared_ptr<CertificateFile> certFile =
925 std::make_shared<CertificateFile>(certFileBody);
926
927 crow::connections::systemBus->async_method_call(
928 [asyncResp, certFile](const boost::system::error_code ec,
929 const std::string& objectPath) {
930 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700931 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700932 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
933 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700934 return;
935 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800936
937 sdbusplus::message::object_path path(objectPath);
938 std::string certId = path.filename();
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800939 const boost::urls::url certURL = crow::utility::urlFromPieces(
940 "redfish", "v1", "Managers", "bmc", "NetworkProtocol", "HTTPS",
941 "Certificates", certId);
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800942 getCertificateProperties(asyncResp, objectPath,
943 certs::httpsServiceName, certId, certURL,
944 "HTTPS Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -0700945 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
946 << certFile->getCertFilePath();
947 },
948 certs::httpsServiceName, certs::httpsObjectPath,
949 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +0800950 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700951} // requestRoutesHTTPSCertificateCollection
952
953/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600954 * The certificate location schema defines a resource that an administrator
955 * can use in order to locate all certificates installed on a given service.
956 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700957inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600958{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700959 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700960 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous002d39b2022-05-31 08:59:27 -0700961 .methods(boost::beast::http::verb::get)(
962 [&app](const crow::Request& req,
963 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000964 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700965 {
966 return;
967 }
968 asyncResp->res.jsonValue["@odata.id"] =
969 "/redfish/v1/CertificateService/CertificateLocations";
970 asyncResp->res.jsonValue["@odata.type"] =
971 "#CertificateLocations.v1_0_0.CertificateLocations";
972 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
973 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
974 asyncResp->res.jsonValue["Description"] =
975 "Defines a resource that an administrator can use in order to "
976 "locate all certificates installed on a given service";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600977
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800978 getCertificateList(asyncResp, certs::baseObjectPath,
979 "/Links/Certificates"_json_pointer,
980 "/Links/Certificates@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700981 });
982}
983// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -0600984
985/**
986 * Collection of LDAP certificates
987 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700988inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600989{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700990 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700991 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700992 .methods(boost::beast::http::verb::get)(
993 [&app](const crow::Request& req,
994 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000995 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700996 {
997 return;
998 }
999
1000 asyncResp->res.jsonValue["@odata.id"] =
1001 "/redfish/v1/AccountService/LDAP/Certificates";
1002 asyncResp->res.jsonValue["@odata.type"] =
1003 "#CertificateCollection.CertificateCollection";
1004 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1005 asyncResp->res.jsonValue["Description"] =
1006 "A Collection of LDAP certificate instances";
1007
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001008 getCertificateList(asyncResp, certs::ldapObjectPath,
1009 "/Members"_json_pointer,
1010 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001011 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001012
1013 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001014 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001015 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001016 [&app](const crow::Request& req,
1017 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001018 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001019 {
1020 return;
1021 }
1022 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001023
Ed Tanous002d39b2022-05-31 08:59:27 -07001024 if (certFileBody.empty())
1025 {
1026 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1027 messages::unrecognizedRequestBody(asyncResp->res);
1028 return;
1029 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001030
Ed Tanous002d39b2022-05-31 08:59:27 -07001031 std::shared_ptr<CertificateFile> certFile =
1032 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001033
Ed Tanous002d39b2022-05-31 08:59:27 -07001034 crow::connections::systemBus->async_method_call(
1035 [asyncResp, certFile](const boost::system::error_code ec,
1036 const std::string& objectPath) {
1037 if (ec)
1038 {
1039 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1040 messages::internalError(asyncResp->res);
1041 return;
1042 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001043
1044 sdbusplus::message::object_path path(objectPath);
1045 std::string certId = path.filename();
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001046 const boost::urls::url certURL =
1047 crow::utility::urlFromPieces("redfish", "v1", "AccountService",
1048 "LDAP", "Certificates", certId);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001049 getCertificateProperties(asyncResp, objectPath,
1050 certs::ldapServiceName, certId, certURL,
1051 "LDAP Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001052 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1053 << certFile->getCertFilePath();
1054 },
1055 certs::ldapServiceName, certs::ldapObjectPath,
1056 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1057 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001058} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001059
1060/**
1061 * Certificate resource describes a certificate used to prove the identity
1062 * of a component, account or service.
1063 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001064inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001065{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001066 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001067 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001068 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001069 [&app](const crow::Request& req,
1070 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001071 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001072 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001073 {
1074 return;
1075 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001076
1077 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << id;
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001078 const boost::urls::url certURL = crow::utility::urlFromPieces(
1079 "redfish", "v1", "AccountService", "LDAP", "Certificates", id);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001080 std::string objPath =
1081 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1082 getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id,
1083 certURL, "LDAP Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001084 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001085} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001086/**
1087 * Collection of TrustStoreCertificate certificates
1088 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001089inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001090{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001091 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001092 .privileges(redfish::privileges::getCertificate)
Ed Tanous002d39b2022-05-31 08:59:27 -07001093 .methods(boost::beast::http::verb::get)(
1094 [&app](const crow::Request& req,
1095 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001096 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001097 {
1098 return;
1099 }
1100
1101 asyncResp->res.jsonValue["@odata.id"] =
1102 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1103 asyncResp->res.jsonValue["@odata.type"] =
1104 "#CertificateCollection.CertificateCollection";
1105 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1106 asyncResp->res.jsonValue["Description"] =
1107 "A Collection of TrustStore certificate instances";
1108
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001109 getCertificateList(asyncResp, certs::authorityObjectPath,
1110 "/Members"_json_pointer,
1111 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001112 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001113
1114 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001115 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001116 .methods(boost::beast::http::verb::post)(
1117 [&app](const crow::Request& req,
1118 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001119 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001120 {
1121 return;
1122 }
1123 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1124
1125 if (certFileBody.empty())
1126 {
1127 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1128 messages::unrecognizedRequestBody(asyncResp->res);
1129 return;
1130 }
1131
1132 std::shared_ptr<CertificateFile> certFile =
1133 std::make_shared<CertificateFile>(certFileBody);
1134 crow::connections::systemBus->async_method_call(
1135 [asyncResp, certFile](const boost::system::error_code ec,
1136 const std::string& objectPath) {
1137 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001138 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001139 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1140 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001141 return;
1142 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001143
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001144 sdbusplus::message::object_path path(objectPath);
1145 std::string certId = path.filename();
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001146 const boost::urls::url certURL = crow::utility::urlFromPieces(
1147 "redfish", "v1", "Managers", "bmc", "Truststore",
1148 "Certificates", certId);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001149 getCertificateProperties(asyncResp, objectPath,
1150 certs::authorityServiceName, certId,
1151 certURL, "TrustStore Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001152 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1153 << certFile->getCertFilePath();
1154 },
1155 certs::authorityServiceName, certs::authorityObjectPath,
1156 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +08001157 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001158} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001159
1160/**
1161 * Certificate resource describes a certificate used to prove the identity
1162 * of a component, account or service.
1163 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001164inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001165{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001166 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001167 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001168 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001169 [&app](const crow::Request& req,
1170 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001171 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001172 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001173 {
1174 return;
1175 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001176
1177 BMCWEB_LOG_DEBUG << "Truststore Certificate ID=" << id;
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001178 const boost::urls::url certURL =
1179 crow::utility::urlFromPieces("redfish", "v1", "Managers", "bmc",
1180 "Truststore", "Certificates", id);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001181 std::string objPath =
1182 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1183 getCertificateProperties(asyncResp, objPath,
1184 certs::authorityServiceName, id, certURL,
1185 "TrustStore Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001186 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001187
1188 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001189 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001190 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001191 [&app](const crow::Request& req,
1192 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001193 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001194 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001195 {
1196 return;
1197 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001198
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001199 BMCWEB_LOG_DEBUG << "Delete TrustStore Certificate ID=" << id;
1200 std::string objPath =
1201 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001202
Ed Tanous002d39b2022-05-31 08:59:27 -07001203 crow::connections::systemBus->async_method_call(
1204 [asyncResp, id](const boost::system::error_code ec) {
1205 if (ec)
1206 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001207 messages::resourceNotFound(asyncResp->res, "Certificate", id);
Ed Tanous002d39b2022-05-31 08:59:27 -07001208 return;
1209 }
1210 BMCWEB_LOG_INFO << "Certificate deleted";
1211 asyncResp->res.result(boost::beast::http::status::no_content);
1212 },
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001213 certs::authorityServiceName, objPath, certs::objDeleteIntf,
Ed Tanous002d39b2022-05-31 08:59:27 -07001214 "Delete");
1215 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001216} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001217} // namespace redfish