blob: c2a3dd672b8813d2f738a3b8e8835572cb435bb7 [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 Zhaoe19e97e2022-06-06 19:43:39 +0800637 messages::resourceNotFound(asyncResp->res, name, 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 Zhao75b63a22022-06-08 00:02:04 +0800821 messages::resourceNotFound(asyncResp->res, name, id);
Ed Tanous002d39b2022-05-31 08:59:27 -0700822 return;
823 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700824 messages::internalError(asyncResp->res);
825 return;
826 }
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800827 getCertificateProperties(asyncResp, objectPath, service, id, url,
828 name);
Ed Tanous002d39b2022-05-31 08:59:27 -0700829 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
830 << certFile->getCertFilePath();
831 },
832 service, objectPath, certs::certReplaceIntf, "Replace",
833 certFile->getCertFilePath());
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700834 });
835} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600836
837/**
838 * Certificate resource describes a certificate used to prove the identity
839 * of a component, account or service.
840 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700841
842inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600843{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700844 BMCWEB_ROUTE(
845 app,
846 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700847 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800848 .methods(boost::beast::http::verb::get)(
849 [&app](const crow::Request& req,
850 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
851 const std::string& id) -> void {
852 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
853 {
854 return;
855 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600856
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800857 BMCWEB_LOG_DEBUG << "HTTPS Certificate ID=" << id;
858 const boost::urls::url certURL = crow::utility::urlFromPieces(
859 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
860 "HTTPS", "Certificates", id);
861 std::string objPath =
862 sdbusplus::message::object_path(certs::httpsObjectPath) /
863 id;
864 getCertificateProperties(asyncResp, objPath,
865 certs::httpsServiceName, id, certURL,
866 "HTTPS Certificate");
867 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700868}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600869
870/**
871 * Collection of HTTPS certificates
872 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700873inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600874{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700875 BMCWEB_ROUTE(app,
876 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700877 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700878 .methods(boost::beast::http::verb::get)(
879 [&app](const crow::Request& req,
880 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000881 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700882 {
883 return;
884 }
885
886 asyncResp->res.jsonValue["@odata.id"] =
887 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
888 asyncResp->res.jsonValue["@odata.type"] =
889 "#CertificateCollection.CertificateCollection";
890 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
891 asyncResp->res.jsonValue["Description"] =
892 "A Collection of HTTPS certificate instances";
893
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800894 getCertificateList(asyncResp, certs::httpsObjectPath,
895 "/Members"_json_pointer,
896 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700897 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600898
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700899 BMCWEB_ROUTE(app,
900 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700901 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700902 .methods(boost::beast::http::verb::post)(
903 [&app](const crow::Request& req,
904 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000905 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700906 {
907 return;
908 }
909 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
910
911 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
912 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
913
914 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
915
916 if (certFileBody.empty())
917 {
918 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
919 messages::unrecognizedRequestBody(asyncResp->res);
920 return;
921 }
922
923 std::shared_ptr<CertificateFile> certFile =
924 std::make_shared<CertificateFile>(certFileBody);
925
926 crow::connections::systemBus->async_method_call(
927 [asyncResp, certFile](const boost::system::error_code ec,
928 const std::string& objectPath) {
929 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700930 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700931 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
932 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700933 return;
934 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800935
936 sdbusplus::message::object_path path(objectPath);
937 std::string certId = path.filename();
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800938 const boost::urls::url certURL = crow::utility::urlFromPieces(
939 "redfish", "v1", "Managers", "bmc", "NetworkProtocol", "HTTPS",
940 "Certificates", certId);
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800941 getCertificateProperties(asyncResp, objectPath,
942 certs::httpsServiceName, certId, certURL,
943 "HTTPS Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -0700944 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
945 << certFile->getCertFilePath();
946 },
947 certs::httpsServiceName, certs::httpsObjectPath,
948 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +0800949 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700950} // requestRoutesHTTPSCertificateCollection
951
952/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600953 * The certificate location schema defines a resource that an administrator
954 * can use in order to locate all certificates installed on a given service.
955 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700956inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600957{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700958 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700959 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous002d39b2022-05-31 08:59:27 -0700960 .methods(boost::beast::http::verb::get)(
961 [&app](const crow::Request& req,
962 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000963 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700964 {
965 return;
966 }
967 asyncResp->res.jsonValue["@odata.id"] =
968 "/redfish/v1/CertificateService/CertificateLocations";
969 asyncResp->res.jsonValue["@odata.type"] =
970 "#CertificateLocations.v1_0_0.CertificateLocations";
971 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
972 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
973 asyncResp->res.jsonValue["Description"] =
974 "Defines a resource that an administrator can use in order to "
975 "locate all certificates installed on a given service";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600976
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800977 getCertificateList(asyncResp, certs::baseObjectPath,
978 "/Links/Certificates"_json_pointer,
979 "/Links/Certificates@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700980 });
981}
982// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -0600983
984/**
985 * Collection of LDAP certificates
986 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700987inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600988{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700989 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700990 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700991 .methods(boost::beast::http::verb::get)(
992 [&app](const crow::Request& req,
993 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000994 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700995 {
996 return;
997 }
998
999 asyncResp->res.jsonValue["@odata.id"] =
1000 "/redfish/v1/AccountService/LDAP/Certificates";
1001 asyncResp->res.jsonValue["@odata.type"] =
1002 "#CertificateCollection.CertificateCollection";
1003 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1004 asyncResp->res.jsonValue["Description"] =
1005 "A Collection of LDAP certificate instances";
1006
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001007 getCertificateList(asyncResp, certs::ldapObjectPath,
1008 "/Members"_json_pointer,
1009 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001010 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001011
1012 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001013 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001014 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001015 [&app](const crow::Request& req,
1016 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001017 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001018 {
1019 return;
1020 }
1021 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001022
Ed Tanous002d39b2022-05-31 08:59:27 -07001023 if (certFileBody.empty())
1024 {
1025 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1026 messages::unrecognizedRequestBody(asyncResp->res);
1027 return;
1028 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001029
Ed Tanous002d39b2022-05-31 08:59:27 -07001030 std::shared_ptr<CertificateFile> certFile =
1031 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001032
Ed Tanous002d39b2022-05-31 08:59:27 -07001033 crow::connections::systemBus->async_method_call(
1034 [asyncResp, certFile](const boost::system::error_code ec,
1035 const std::string& objectPath) {
1036 if (ec)
1037 {
1038 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1039 messages::internalError(asyncResp->res);
1040 return;
1041 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001042
1043 sdbusplus::message::object_path path(objectPath);
1044 std::string certId = path.filename();
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001045 const boost::urls::url certURL =
1046 crow::utility::urlFromPieces("redfish", "v1", "AccountService",
1047 "LDAP", "Certificates", certId);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001048 getCertificateProperties(asyncResp, objectPath,
1049 certs::ldapServiceName, certId, certURL,
1050 "LDAP Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001051 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1052 << certFile->getCertFilePath();
1053 },
1054 certs::ldapServiceName, certs::ldapObjectPath,
1055 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1056 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001057} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001058
1059/**
1060 * Certificate resource describes a certificate used to prove the identity
1061 * of a component, account or service.
1062 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001063inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001064{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001065 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001066 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001067 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001068 [&app](const crow::Request& req,
1069 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001070 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001071 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001072 {
1073 return;
1074 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001075
1076 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << id;
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001077 const boost::urls::url certURL = crow::utility::urlFromPieces(
1078 "redfish", "v1", "AccountService", "LDAP", "Certificates", id);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001079 std::string objPath =
1080 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1081 getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id,
1082 certURL, "LDAP Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001083 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001084} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001085/**
1086 * Collection of TrustStoreCertificate certificates
1087 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001088inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001089{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001090 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001091 .privileges(redfish::privileges::getCertificate)
Ed Tanous002d39b2022-05-31 08:59:27 -07001092 .methods(boost::beast::http::verb::get)(
1093 [&app](const crow::Request& req,
1094 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001095 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001096 {
1097 return;
1098 }
1099
1100 asyncResp->res.jsonValue["@odata.id"] =
1101 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1102 asyncResp->res.jsonValue["@odata.type"] =
1103 "#CertificateCollection.CertificateCollection";
1104 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1105 asyncResp->res.jsonValue["Description"] =
1106 "A Collection of TrustStore certificate instances";
1107
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001108 getCertificateList(asyncResp, certs::authorityObjectPath,
1109 "/Members"_json_pointer,
1110 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001111 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001112
1113 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001114 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001115 .methods(boost::beast::http::verb::post)(
1116 [&app](const crow::Request& req,
1117 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001118 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001119 {
1120 return;
1121 }
1122 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1123
1124 if (certFileBody.empty())
1125 {
1126 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1127 messages::unrecognizedRequestBody(asyncResp->res);
1128 return;
1129 }
1130
1131 std::shared_ptr<CertificateFile> certFile =
1132 std::make_shared<CertificateFile>(certFileBody);
1133 crow::connections::systemBus->async_method_call(
1134 [asyncResp, certFile](const boost::system::error_code ec,
1135 const std::string& objectPath) {
1136 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001137 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001138 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1139 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001140 return;
1141 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001142
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001143 sdbusplus::message::object_path path(objectPath);
1144 std::string certId = path.filename();
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001145 const boost::urls::url certURL = crow::utility::urlFromPieces(
1146 "redfish", "v1", "Managers", "bmc", "Truststore",
1147 "Certificates", certId);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001148 getCertificateProperties(asyncResp, objectPath,
1149 certs::authorityServiceName, certId,
1150 certURL, "TrustStore Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001151 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1152 << certFile->getCertFilePath();
1153 },
1154 certs::authorityServiceName, certs::authorityObjectPath,
1155 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +08001156 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001157} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001158
1159/**
1160 * Certificate resource describes a certificate used to prove the identity
1161 * of a component, account or service.
1162 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001163inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001164{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001165 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001166 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001167 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001168 [&app](const crow::Request& req,
1169 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001170 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001171 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001172 {
1173 return;
1174 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001175
1176 BMCWEB_LOG_DEBUG << "Truststore Certificate ID=" << id;
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001177 const boost::urls::url certURL =
1178 crow::utility::urlFromPieces("redfish", "v1", "Managers", "bmc",
1179 "Truststore", "Certificates", id);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001180 std::string objPath =
1181 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1182 getCertificateProperties(asyncResp, objPath,
1183 certs::authorityServiceName, id, certURL,
1184 "TrustStore Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001185 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001186
1187 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001188 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001189 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001190 [&app](const crow::Request& req,
1191 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001192 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001193 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001194 {
1195 return;
1196 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001197
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001198 BMCWEB_LOG_DEBUG << "Delete TrustStore Certificate ID=" << id;
1199 std::string objPath =
1200 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001201
Ed Tanous002d39b2022-05-31 08:59:27 -07001202 crow::connections::systemBus->async_method_call(
1203 [asyncResp, id](const boost::system::error_code ec) {
1204 if (ec)
1205 {
1206 messages::resourceNotFound(asyncResp->res,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001207 "TrustStore 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