blob: d3aedd00a7c1be84c49569314ada69a86fce7330 [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 }
Ed Tanous613dabe2022-07-09 11:17:36 -070081 nlohmann::json& actions = asyncResp->res.jsonValue["Actions"];
82 nlohmann::json& replace =
83 actions["#CertificateService.ReplaceCertificate"];
84 replace["target"] =
85 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate";
86 nlohmann::json::array_t allowed;
87 allowed.push_back("PEM");
88 replace["CertificateType@Redfish.AllowableValues"] = std::move(allowed);
89 actions["#CertificateService.GenerateCSR"]["target"] =
90 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR";
Abhishek Patel72048782021-06-02 09:53:24 -050091 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070092} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060093
zhanghch058d1b46d2021-04-01 11:18:24 +080094inline std::string getCertificateFromReqBody(
95 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
96 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020097{
98 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
99
100 if (reqJson.is_discarded())
101 {
102 // We did not receive JSON request, proceed as it is RAW data
103 return req.body;
104 }
105
106 std::string certificate;
107 std::optional<std::string> certificateType = "PEM";
108
Willy Tu15ed6782021-12-14 11:03:16 -0800109 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
110 certificate, "CertificateType",
111 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200112 {
113 BMCWEB_LOG_ERROR << "Required parameters are missing";
114 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700115 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200116 }
117
118 if (*certificateType != "PEM")
119 {
120 messages::propertyValueNotInList(asyncResp->res, *certificateType,
121 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700122 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200123 }
124
125 return certificate;
126}
127
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600128/**
129 * Class to create a temporary certificate file for uploading to system
130 */
131class CertificateFile
132{
133 public:
134 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500135 CertificateFile(const CertificateFile&) = delete;
136 CertificateFile& operator=(const CertificateFile&) = delete;
137 CertificateFile(CertificateFile&&) = delete;
138 CertificateFile& operator=(CertificateFile&&) = delete;
Ed Tanous4e23a442022-06-06 09:57:26 -0700139 explicit CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600140 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700141 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700142 'e', 'r', 't', 's', '.', 'X',
143 'X', 'X', 'X', 'X', 'X', '\0'};
144 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800145 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600146 {
147 certDirectory = tempDirectory;
148 certificateFile = certDirectory / "cert.pem";
149 std::ofstream out(certificateFile, std::ofstream::out |
150 std::ofstream::binary |
151 std::ofstream::trunc);
152 out << certString;
153 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800154 BMCWEB_LOG_DEBUG << "Creating certificate file"
155 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600156 }
157 }
158 ~CertificateFile()
159 {
160 if (std::filesystem::exists(certDirectory))
161 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800162 BMCWEB_LOG_DEBUG << "Removing certificate file"
163 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000164 std::error_code ec;
165 std::filesystem::remove_all(certDirectory, ec);
166 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600167 {
168 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800169 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600170 }
171 }
172 }
173 std::string getCertFilePath()
174 {
175 return certificateFile;
176 }
177
178 private:
179 std::filesystem::path certificateFile;
180 std::filesystem::path certDirectory;
181};
182
Patrick Williams59d494e2022-07-22 19:26:55 -0500183static std::unique_ptr<sdbusplus::bus::match_t> csrMatcher;
Marri Devender Rao30215812019-03-18 08:59:21 -0500184/**
185 * @brief Read data from CSR D-bus object and set to response
186 *
187 * @param[in] asyncResp Shared pointer to the response message
188 * @param[in] certURI Link to certifiate collection URI
189 * @param[in] service D-Bus service name
190 * @param[in] certObjPath certificate D-Bus object path
191 * @param[in] csrObjPath CSR D-Bus object path
192 * @return None
193 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800194static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500195 const std::string& certURI, const std::string& service,
196 const std::string& certObjPath,
197 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500198{
199 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
200 << " CSRObjectPath=" << csrObjPath
201 << " service=" << service;
202 crow::connections::systemBus->async_method_call(
203 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500204 const std::string& csr) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700205 if (ec)
206 {
207 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
208 messages::internalError(asyncResp->res);
209 return;
210 }
211 if (csr.empty())
212 {
213 BMCWEB_LOG_ERROR << "CSR read is empty";
214 messages::internalError(asyncResp->res);
215 return;
216 }
217 asyncResp->res.jsonValue["CSRString"] = csr;
218 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
219 certURI;
Marri Devender Rao30215812019-03-18 08:59:21 -0500220 },
221 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
222}
223
224/**
225 * Action to Generate CSR
226 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700227inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500228{
George Liu0fda0f12021-11-16 10:06:17 +0800229 BMCWEB_ROUTE(
230 app,
231 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
Abhishek Patel5344ab82021-07-31 17:42:09 -0500232 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700233 .methods(boost::beast::http::verb::post)(
234 [&app](const crow::Request& req,
235 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000236 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700237 {
238 return;
239 }
240 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500241
Ed Tanous002d39b2022-05-31 08:59:27 -0700242 // Required parameters
243 std::string city;
244 std::string commonName;
245 std::string country;
246 std::string organization;
247 std::string organizationalUnit;
248 std::string state;
249 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800250
Ed Tanous002d39b2022-05-31 08:59:27 -0700251 // Optional parameters
252 std::optional<std::vector<std::string>> optAlternativeNames =
253 std::vector<std::string>();
254 std::optional<std::string> optContactPerson = "";
255 std::optional<std::string> optChallengePassword = "";
256 std::optional<std::string> optEmail = "";
257 std::optional<std::string> optGivenName = "";
258 std::optional<std::string> optInitials = "";
259 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
260 std::optional<std::string> optKeyCurveId = "secp384r1";
261 std::optional<std::string> optKeyPairAlgorithm = "EC";
262 std::optional<std::vector<std::string>> optKeyUsage =
263 std::vector<std::string>();
264 std::optional<std::string> optSurname = "";
265 std::optional<std::string> optUnstructuredName = "";
266 if (!json_util::readJsonAction(
267 req, asyncResp->res, "City", city, "CommonName", commonName,
268 "ContactPerson", optContactPerson, "Country", country,
269 "Organization", organization, "OrganizationalUnit",
270 organizationalUnit, "State", state, "CertificateCollection",
271 certificateCollection, "AlternativeNames", optAlternativeNames,
272 "ChallengePassword", optChallengePassword, "Email", optEmail,
273 "GivenName", optGivenName, "Initials", optInitials,
274 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
275 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
276 optKeyUsage, "Surname", optSurname, "UnstructuredName",
277 optUnstructuredName))
278 {
279 return;
280 }
George Liu0fda0f12021-11-16 10:06:17 +0800281
Ed Tanous002d39b2022-05-31 08:59:27 -0700282 // bmcweb has no way to store or decode a private key challenge
283 // password, which will likely cause bmcweb to crash on startup
284 // if this is not set on a post so not allowing the user to set
285 // value
286 if (!optChallengePassword->empty())
287 {
288 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
289 "ChallengePassword");
290 return;
291 }
George Liu0fda0f12021-11-16 10:06:17 +0800292
Ed Tanous002d39b2022-05-31 08:59:27 -0700293 std::string certURI;
294 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
295 "@odata.id", certURI))
296 {
297 return;
298 }
George Liu0fda0f12021-11-16 10:06:17 +0800299
Ed Tanous002d39b2022-05-31 08:59:27 -0700300 std::string objectPath;
301 std::string service;
Ed Tanous11ba3972022-07-11 09:50:41 -0700302 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700303 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
304 {
305 objectPath = certs::httpsObjectPath;
306 service = certs::httpsServiceName;
307 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700308 else if (certURI.starts_with(
309 "/redfish/v1/AccountService/LDAP/Certificates"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700310 {
311 objectPath = certs::ldapObjectPath;
312 service = certs::ldapServiceName;
313 }
314 else
315 {
316 messages::actionParameterNotSupported(
317 asyncResp->res, "CertificateCollection", "GenerateCSR");
318 return;
319 }
320
321 // supporting only EC and RSA algorithm
322 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
323 {
324 messages::actionParameterNotSupported(
325 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
326 return;
327 }
328
329 // supporting only 2048 key bit length for RSA algorithm due to
330 // time consumed in generating private key
331 if (*optKeyPairAlgorithm == "RSA" &&
332 *optKeyBitLength != rsaKeyBitLength)
333 {
334 messages::propertyValueNotInList(asyncResp->res,
335 std::to_string(*optKeyBitLength),
336 "KeyBitLength");
337 return;
338 }
339
340 // validate KeyUsage supporting only 1 type based on URL
Ed Tanous11ba3972022-07-11 09:50:41 -0700341 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700342 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
343 {
344 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800345 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700346 optKeyUsage->push_back("ServerAuthentication");
George Liu0fda0f12021-11-16 10:06:17 +0800347 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700348 else if (optKeyUsage->size() == 1)
George Liu0fda0f12021-11-16 10:06:17 +0800349 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700350 if ((*optKeyUsage)[0] != "ServerAuthentication")
351 {
352 messages::propertyValueNotInList(
353 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
354 return;
355 }
George Liu0fda0f12021-11-16 10:06:17 +0800356 }
357 else
358 {
359 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700360 asyncResp->res, "KeyUsage", "GenerateCSR");
George Liu0fda0f12021-11-16 10:06:17 +0800361 return;
362 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700363 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700364 else if (certURI.starts_with(
365 "/redfish/v1/AccountService/LDAP/Certificates"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700366 {
367 if (optKeyUsage->empty())
368 {
369 optKeyUsage->push_back("ClientAuthentication");
370 }
371 else if (optKeyUsage->size() == 1)
372 {
373 if ((*optKeyUsage)[0] != "ClientAuthentication")
374 {
375 messages::propertyValueNotInList(
376 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
377 return;
378 }
379 }
380 else
George Liu0fda0f12021-11-16 10:06:17 +0800381 {
382 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700383 asyncResp->res, "KeyUsage", "GenerateCSR");
384 return;
385 }
386 }
387
388 // Only allow one CSR matcher at a time so setting retry
389 // time-out and timer expiry to 10 seconds for now.
390 static const int timeOut = 10;
391 if (csrMatcher)
392 {
393 messages::serviceTemporarilyUnavailable(asyncResp->res,
394 std::to_string(timeOut));
395 return;
396 }
397
398 // Make this static so it survives outside this method
399 static boost::asio::steady_timer timeout(*req.ioService);
400 timeout.expires_after(std::chrono::seconds(timeOut));
401 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
402 csrMatcher = nullptr;
403 if (ec)
404 {
405 // operation_aborted is expected if timer is canceled
406 // before completion.
407 if (ec != boost::asio::error::operation_aborted)
408 {
409 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
410 }
411 return;
412 }
413 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
414 messages::internalError(asyncResp->res);
415 });
416
417 // create a matcher to wait on CSR object
418 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
419 std::string match("type='signal',"
420 "interface='org.freedesktop.DBus.ObjectManager',"
421 "path='" +
422 objectPath +
423 "',"
424 "member='InterfacesAdded'");
Patrick Williams59d494e2022-07-22 19:26:55 -0500425 csrMatcher = std::make_unique<sdbusplus::bus::match_t>(
Ed Tanous002d39b2022-05-31 08:59:27 -0700426 *crow::connections::systemBus, match,
Patrick Williams59d494e2022-07-22 19:26:55 -0500427 [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700428 timeout.cancel();
429 if (m.is_method_error())
430 {
431 BMCWEB_LOG_ERROR << "Dbus method error!!!";
432 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800433 return;
434 }
435
Ed Tanous002d39b2022-05-31 08:59:27 -0700436 dbus::utility::DBusInteracesMap interfacesProperties;
437
438 sdbusplus::message::object_path csrObjectPath;
439 m.read(csrObjectPath, interfacesProperties);
440 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
Ed Tanous02cad962022-06-30 16:50:15 -0700441 for (const auto& interface : interfacesProperties)
George Liu0fda0f12021-11-16 10:06:17 +0800442 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700443 if (interface.first == "xyz.openbmc_project.Certs.CSR")
444 {
445 getCSR(asyncResp, certURI, service, objectPath,
446 csrObjectPath.str);
447 break;
448 }
449 }
450 });
451 crow::connections::systemBus->async_method_call(
452 [asyncResp](const boost::system::error_code ec,
453 const std::string&) {
454 if (ec)
455 {
456 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
457 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800458 return;
459 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700460 },
461 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
462 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
463 commonName, *optContactPerson, country, *optEmail, *optGivenName,
464 *optInitials, *optKeyBitLength, *optKeyCurveId,
465 *optKeyPairAlgorithm, *optKeyUsage, organization,
466 organizationalUnit, state, *optSurname, *optUnstructuredName);
George Liu0fda0f12021-11-16 10:06:17 +0800467 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700468} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500469
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600470/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500471 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600472 *
473 * @param[in] asyncResp Shared pointer to the response message
474 * @param[in] str Issuer/Subject value in key=value pairs
475 * @param[in] type Issuer/Subject
476 * @return None
477 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500478static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600479 const std::string_view value)
480{
481 // example: O=openbmc-project.xyz,CN=localhost
482 std::string_view::iterator i = value.begin();
483 while (i != value.end())
484 {
485 std::string_view::iterator tokenBegin = i;
486 while (i != value.end() && *i != '=')
487 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530488 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600489 }
490 if (i == value.end())
491 {
492 break;
493 }
Ed Tanous271584a2019-07-09 16:24:22 -0700494 const std::string_view key(tokenBegin,
495 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530496 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600497 tokenBegin = i;
498 while (i != value.end() && *i != ',')
499 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530500 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600501 }
Ed Tanous271584a2019-07-09 16:24:22 -0700502 const std::string_view val(tokenBegin,
503 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600504 if (key == "L")
505 {
506 out["City"] = val;
507 }
508 else if (key == "CN")
509 {
510 out["CommonName"] = val;
511 }
512 else if (key == "C")
513 {
514 out["Country"] = val;
515 }
516 else if (key == "O")
517 {
518 out["Organization"] = val;
519 }
520 else if (key == "OU")
521 {
522 out["OrganizationalUnit"] = val;
523 }
524 else if (key == "ST")
525 {
526 out["State"] = val;
527 }
528 // skip comma character
529 if (i != value.end())
530 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530531 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600532 }
533 }
534}
535
536/**
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800537 * @brief Retrieve the installed certificate list
538 *
539 * @param[in] asyncResp Shared pointer to the response message
540 * @param[in] basePath DBus object path to search
541 * @param[in] listPtr Json pointer to the list in asyncResp
542 * @param[in] countPtr Json pointer to the count in asyncResp
543 * @return None
544 */
545static void
546 getCertificateList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
547 const std::string& basePath,
548 const nlohmann::json::json_pointer& listPtr,
549 const nlohmann::json::json_pointer& countPtr)
550{
551 crow::connections::systemBus->async_method_call(
552 [asyncResp, listPtr, countPtr](
553 const boost::system::error_code ec,
554 const dbus::utility::MapperGetSubTreePathsResponse& certPaths) {
555 if (ec)
556 {
557 BMCWEB_LOG_ERROR << "Certificate collection query failed: " << ec;
558 messages::internalError(asyncResp->res);
559 return;
560 }
561
562 nlohmann::json& links = asyncResp->res.jsonValue[listPtr];
563 links = nlohmann::json::array();
564 for (const auto& certPath : certPaths)
565 {
566 sdbusplus::message::object_path objPath(certPath);
567 std::string certId = objPath.filename();
568 if (certId.empty())
569 {
570 BMCWEB_LOG_ERROR << "Invalid certificate objPath " << certPath;
571 continue;
572 }
573
574 boost::urls::url certURL;
575 if (objPath.parent_path() == certs::httpsObjectPath)
576 {
577 certURL = crow::utility::urlFromPieces(
578 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
579 "HTTPS", "Certificates", certId);
580 }
581 else if (objPath.parent_path() == certs::ldapObjectPath)
582 {
583 certURL = crow::utility::urlFromPieces("redfish", "v1",
584 "AccountService", "LDAP",
585 "Certificates", certId);
586 }
587 else if (objPath.parent_path() == certs::authorityObjectPath)
588 {
589 certURL = crow::utility::urlFromPieces(
590 "redfish", "v1", "Managers", "bmc", "Truststore",
591 "Certificates", certId);
592 }
593 else
594 {
595 continue;
596 }
597
598 nlohmann::json::object_t link;
599 link["@odata.id"] = certURL;
600 links.emplace_back(std::move(link));
601 }
602
603 asyncResp->res.jsonValue[countPtr] = links.size();
604 },
605 "xyz.openbmc_project.ObjectMapper",
606 "/xyz/openbmc_project/object_mapper",
607 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", basePath, 0,
608 std::array<const char*, 1>{certs::certPropIntf});
609}
610
611/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600612 * @brief Retrieve the certificates properties and append to the response
613 * message
614 *
615 * @param[in] asyncResp Shared pointer to the response message
616 * @param[in] objectPath Path of the D-Bus service object
617 * @param[in] certId Id of the certificate
618 * @param[in] certURL URL of the certificate object
619 * @param[in] name name of the certificate
620 * @return None
621 */
622static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800623 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800624 const std::string& objectPath, const std::string& service,
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800625 const std::string& certId, const boost::urls::url& certURL,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800626 const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600627{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600628 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
629 << " certId=" << certId << " certURl=" << certURL;
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200630 sdbusplus::asio::getAllProperties(
631 *crow::connections::systemBus, service, objectPath, certs::certPropIntf,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800632 [asyncResp, certURL, certId,
633 name](const boost::system::error_code ec,
634 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700635 if (ec)
636 {
637 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800638 messages::resourceNotFound(asyncResp->res, "Certificate", certId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700639 return;
640 }
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200641
642 const std::string* certificateString = nullptr;
643 const std::vector<std::string>* keyUsage = nullptr;
644 const std::string* issuer = nullptr;
645 const std::string* subject = nullptr;
646 const uint64_t* validNotAfter = nullptr;
647 const uint64_t* validNotBefore = nullptr;
648
649 const bool success = sdbusplus::unpackPropertiesNoThrow(
650 dbus_utils::UnpackErrorPrinter(), properties, "CertificateString",
651 certificateString, "KeyUsage", keyUsage, "Issuer", issuer,
652 "Subject", subject, "ValidNotAfter", validNotAfter,
653 "ValidNotBefore", validNotBefore);
654
655 if (!success)
656 {
657 messages::internalError(asyncResp->res);
658 return;
659 }
660
Ed Tanous002d39b2022-05-31 08:59:27 -0700661 asyncResp->res.jsonValue["@odata.id"] = certURL;
662 asyncResp->res.jsonValue["@odata.type"] =
663 "#Certificate.v1_0_0.Certificate";
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800664 asyncResp->res.jsonValue["Id"] = certId;
Ed Tanous002d39b2022-05-31 08:59:27 -0700665 asyncResp->res.jsonValue["Name"] = name;
666 asyncResp->res.jsonValue["Description"] = name;
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200667 asyncResp->res.jsonValue["CertificateString"] = "";
668 asyncResp->res.jsonValue["KeyUsage"] = nlohmann::json::array();
669
670 if (certificateString != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -0700671 {
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200672 asyncResp->res.jsonValue["CertificateString"] = *certificateString;
Ed Tanous002d39b2022-05-31 08:59:27 -0700673 }
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200674
675 if (keyUsage != nullptr)
676 {
677 asyncResp->res.jsonValue["KeyUsage"] = *keyUsage;
678 }
679
680 if (issuer != nullptr)
681 {
682 updateCertIssuerOrSubject(asyncResp->res.jsonValue["Issuer"],
683 *issuer);
684 }
685
686 if (subject != nullptr)
687 {
688 updateCertIssuerOrSubject(asyncResp->res.jsonValue["Subject"],
689 *subject);
690 }
691
692 if (validNotAfter != nullptr)
693 {
694 asyncResp->res.jsonValue["ValidNotAfter"] =
695 redfish::time_utils::getDateTimeUint(*validNotAfter);
696 }
697
698 if (validNotBefore != nullptr)
699 {
700 asyncResp->res.jsonValue["ValidNotBefore"] =
701 redfish::time_utils::getDateTimeUint(*validNotBefore);
702 }
703
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800704 asyncResp->res.addHeader(
Ed Tanousd9f6c622022-03-17 09:12:17 -0700705 boost::beast::http::field::location,
706 std::string_view(certURL.data(), certURL.size()));
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200707 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600708}
709
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600710/**
711 * Action to replace an existing certificate
712 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700713inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600714{
George Liu0fda0f12021-11-16 10:06:17 +0800715 BMCWEB_ROUTE(
716 app,
717 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700718 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700719 .methods(boost::beast::http::verb::post)(
720 [&app](const crow::Request& req,
721 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000722 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700723 {
724 return;
725 }
726 std::string certificate;
727 nlohmann::json certificateUri;
728 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600729
Ed Tanous002d39b2022-05-31 08:59:27 -0700730 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
731 certificate, "CertificateUri",
732 certificateUri, "CertificateType",
733 certificateType))
734 {
735 BMCWEB_LOG_ERROR << "Required parameters are missing";
736 messages::internalError(asyncResp->res);
737 return;
738 }
739
740 if (!certificateType)
741 {
742 // should never happen, but it never hurts to be paranoid.
743 return;
744 }
745 if (certificateType != "PEM")
746 {
747 messages::actionParameterNotSupported(
748 asyncResp->res, "CertificateType", "ReplaceCertificate");
749 return;
750 }
751
752 std::string certURI;
753 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
754 "@odata.id", certURI))
755 {
756 messages::actionParameterMissing(
757 asyncResp->res, "ReplaceCertificate", "CertificateUri");
758 return;
759 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800760 BMCWEB_LOG_INFO << "Certificate URI to replace: " << certURI;
Ed Tanous002d39b2022-05-31 08:59:27 -0700761
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800762 boost::urls::result<boost::urls::url_view> parsedUrl =
763 boost::urls::parse_relative_ref(certURI);
764 if (!parsedUrl)
Ed Tanous002d39b2022-05-31 08:59:27 -0700765 {
766 messages::actionParameterValueFormatError(asyncResp->res, certURI,
767 "CertificateUri",
768 "ReplaceCertificate");
769 return;
770 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800771
772 std::string id;
773 sdbusplus::message::object_path objectPath;
Ed Tanous002d39b2022-05-31 08:59:27 -0700774 std::string name;
775 std::string service;
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800776 if (crow::utility::readUrlSegments(
777 *parsedUrl, "redfish", "v1", "Managers", "bmc",
778 "NetworkProtocol", "HTTPS", "Certificates", std::ref(id)))
Ed Tanous002d39b2022-05-31 08:59:27 -0700779 {
780 objectPath =
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800781 sdbusplus::message::object_path(certs::httpsObjectPath) / id;
Ed Tanous002d39b2022-05-31 08:59:27 -0700782 name = "HTTPS certificate";
783 service = certs::httpsServiceName;
784 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800785 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
786 "AccountService", "LDAP",
787 "Certificates", std::ref(id)))
Ed Tanous002d39b2022-05-31 08:59:27 -0700788 {
789 objectPath =
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800790 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
Ed Tanous002d39b2022-05-31 08:59:27 -0700791 name = "LDAP certificate";
792 service = certs::ldapServiceName;
793 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800794 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
795 "Managers", "bmc", "Truststore",
796 "Certificates", std::ref(id)))
Ed Tanous002d39b2022-05-31 08:59:27 -0700797 {
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800798 objectPath =
799 sdbusplus::message::object_path(certs::authorityObjectPath) /
800 id;
Ed Tanous002d39b2022-05-31 08:59:27 -0700801 name = "TrustStore certificate";
802 service = certs::authorityServiceName;
803 }
804 else
805 {
806 messages::actionParameterNotSupported(
807 asyncResp->res, "CertificateUri", "ReplaceCertificate");
808 return;
809 }
810
811 std::shared_ptr<CertificateFile> certFile =
812 std::make_shared<CertificateFile>(certificate);
813 crow::connections::systemBus->async_method_call(
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800814 [asyncResp, certFile, objectPath, service, url{*parsedUrl}, id,
Ed Tanous002d39b2022-05-31 08:59:27 -0700815 name](const boost::system::error_code ec) {
816 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700817 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700818 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
819 if (ec.value() ==
820 boost::system::linux_error::bad_request_descriptor)
821 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800822 messages::resourceNotFound(asyncResp->res, "Certificate",
823 id);
Ed Tanous002d39b2022-05-31 08:59:27 -0700824 return;
825 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700826 messages::internalError(asyncResp->res);
827 return;
828 }
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800829 getCertificateProperties(asyncResp, objectPath, service, id, url,
830 name);
Ed Tanous002d39b2022-05-31 08:59:27 -0700831 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
832 << certFile->getCertFilePath();
833 },
834 service, objectPath, certs::certReplaceIntf, "Replace",
835 certFile->getCertFilePath());
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700836 });
837} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600838
839/**
840 * Certificate resource describes a certificate used to prove the identity
841 * of a component, account or service.
842 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700843
844inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600845{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700846 BMCWEB_ROUTE(
847 app,
848 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700849 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800850 .methods(boost::beast::http::verb::get)(
851 [&app](const crow::Request& req,
852 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
853 const std::string& id) -> void {
854 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
855 {
856 return;
857 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600858
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800859 BMCWEB_LOG_DEBUG << "HTTPS Certificate ID=" << id;
860 const boost::urls::url certURL = crow::utility::urlFromPieces(
861 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
862 "HTTPS", "Certificates", id);
863 std::string objPath =
864 sdbusplus::message::object_path(certs::httpsObjectPath) /
865 id;
866 getCertificateProperties(asyncResp, objPath,
867 certs::httpsServiceName, id, certURL,
868 "HTTPS Certificate");
869 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700870}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600871
872/**
873 * Collection of HTTPS certificates
874 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700875inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600876{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700877 BMCWEB_ROUTE(app,
878 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700879 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700880 .methods(boost::beast::http::verb::get)(
881 [&app](const crow::Request& req,
882 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000883 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700884 {
885 return;
886 }
887
888 asyncResp->res.jsonValue["@odata.id"] =
889 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
890 asyncResp->res.jsonValue["@odata.type"] =
891 "#CertificateCollection.CertificateCollection";
892 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
893 asyncResp->res.jsonValue["Description"] =
894 "A Collection of HTTPS certificate instances";
895
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800896 getCertificateList(asyncResp, certs::httpsObjectPath,
897 "/Members"_json_pointer,
898 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700899 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600900
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700901 BMCWEB_ROUTE(app,
902 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700903 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700904 .methods(boost::beast::http::verb::post)(
905 [&app](const crow::Request& req,
906 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000907 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700908 {
909 return;
910 }
911 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
912
913 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
914 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
915
916 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
917
918 if (certFileBody.empty())
919 {
920 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
921 messages::unrecognizedRequestBody(asyncResp->res);
922 return;
923 }
924
925 std::shared_ptr<CertificateFile> certFile =
926 std::make_shared<CertificateFile>(certFileBody);
927
928 crow::connections::systemBus->async_method_call(
929 [asyncResp, certFile](const boost::system::error_code ec,
930 const std::string& objectPath) {
931 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700932 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700933 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
934 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700935 return;
936 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800937
938 sdbusplus::message::object_path path(objectPath);
939 std::string certId = path.filename();
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800940 const boost::urls::url certURL = crow::utility::urlFromPieces(
941 "redfish", "v1", "Managers", "bmc", "NetworkProtocol", "HTTPS",
942 "Certificates", certId);
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800943 getCertificateProperties(asyncResp, objectPath,
944 certs::httpsServiceName, certId, certURL,
945 "HTTPS Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -0700946 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
947 << certFile->getCertFilePath();
948 },
949 certs::httpsServiceName, certs::httpsObjectPath,
950 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +0800951 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700952} // requestRoutesHTTPSCertificateCollection
953
954/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600955 * The certificate location schema defines a resource that an administrator
956 * can use in order to locate all certificates installed on a given service.
957 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700958inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600959{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700960 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700961 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous002d39b2022-05-31 08:59:27 -0700962 .methods(boost::beast::http::verb::get)(
963 [&app](const crow::Request& req,
964 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000965 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700966 {
967 return;
968 }
969 asyncResp->res.jsonValue["@odata.id"] =
970 "/redfish/v1/CertificateService/CertificateLocations";
971 asyncResp->res.jsonValue["@odata.type"] =
972 "#CertificateLocations.v1_0_0.CertificateLocations";
973 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
974 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
975 asyncResp->res.jsonValue["Description"] =
976 "Defines a resource that an administrator can use in order to "
977 "locate all certificates installed on a given service";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600978
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800979 getCertificateList(asyncResp, certs::baseObjectPath,
980 "/Links/Certificates"_json_pointer,
981 "/Links/Certificates@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700982 });
983}
984// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -0600985
986/**
987 * Collection of LDAP certificates
988 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700989inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600990{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700991 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700992 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700993 .methods(boost::beast::http::verb::get)(
994 [&app](const crow::Request& req,
995 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000996 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700997 {
998 return;
999 }
1000
1001 asyncResp->res.jsonValue["@odata.id"] =
1002 "/redfish/v1/AccountService/LDAP/Certificates";
1003 asyncResp->res.jsonValue["@odata.type"] =
1004 "#CertificateCollection.CertificateCollection";
1005 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1006 asyncResp->res.jsonValue["Description"] =
1007 "A Collection of LDAP certificate instances";
1008
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001009 getCertificateList(asyncResp, certs::ldapObjectPath,
1010 "/Members"_json_pointer,
1011 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001012 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001013
1014 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001015 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001016 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001017 [&app](const crow::Request& req,
1018 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001019 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001020 {
1021 return;
1022 }
1023 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001024
Ed Tanous002d39b2022-05-31 08:59:27 -07001025 if (certFileBody.empty())
1026 {
1027 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1028 messages::unrecognizedRequestBody(asyncResp->res);
1029 return;
1030 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001031
Ed Tanous002d39b2022-05-31 08:59:27 -07001032 std::shared_ptr<CertificateFile> certFile =
1033 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001034
Ed Tanous002d39b2022-05-31 08:59:27 -07001035 crow::connections::systemBus->async_method_call(
1036 [asyncResp, certFile](const boost::system::error_code ec,
1037 const std::string& objectPath) {
1038 if (ec)
1039 {
1040 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1041 messages::internalError(asyncResp->res);
1042 return;
1043 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001044
1045 sdbusplus::message::object_path path(objectPath);
1046 std::string certId = path.filename();
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001047 const boost::urls::url certURL =
1048 crow::utility::urlFromPieces("redfish", "v1", "AccountService",
1049 "LDAP", "Certificates", certId);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001050 getCertificateProperties(asyncResp, objectPath,
1051 certs::ldapServiceName, certId, certURL,
1052 "LDAP Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001053 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1054 << certFile->getCertFilePath();
1055 },
1056 certs::ldapServiceName, certs::ldapObjectPath,
1057 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1058 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001059} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001060
1061/**
1062 * Certificate resource describes a certificate used to prove the identity
1063 * of a component, account or service.
1064 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001065inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001066{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001067 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001068 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001069 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001070 [&app](const crow::Request& req,
1071 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001072 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001073 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001074 {
1075 return;
1076 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001077
1078 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << id;
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001079 const boost::urls::url certURL = crow::utility::urlFromPieces(
1080 "redfish", "v1", "AccountService", "LDAP", "Certificates", id);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001081 std::string objPath =
1082 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1083 getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id,
1084 certURL, "LDAP Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001085 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001086} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001087/**
1088 * Collection of TrustStoreCertificate certificates
1089 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001090inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001091{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001092 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001093 .privileges(redfish::privileges::getCertificate)
Ed Tanous002d39b2022-05-31 08:59:27 -07001094 .methods(boost::beast::http::verb::get)(
1095 [&app](const crow::Request& req,
1096 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001097 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001098 {
1099 return;
1100 }
1101
1102 asyncResp->res.jsonValue["@odata.id"] =
1103 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1104 asyncResp->res.jsonValue["@odata.type"] =
1105 "#CertificateCollection.CertificateCollection";
1106 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1107 asyncResp->res.jsonValue["Description"] =
1108 "A Collection of TrustStore certificate instances";
1109
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001110 getCertificateList(asyncResp, certs::authorityObjectPath,
1111 "/Members"_json_pointer,
1112 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001113 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001114
1115 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001116 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001117 .methods(boost::beast::http::verb::post)(
1118 [&app](const crow::Request& req,
1119 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001120 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001121 {
1122 return;
1123 }
1124 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1125
1126 if (certFileBody.empty())
1127 {
1128 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1129 messages::unrecognizedRequestBody(asyncResp->res);
1130 return;
1131 }
1132
1133 std::shared_ptr<CertificateFile> certFile =
1134 std::make_shared<CertificateFile>(certFileBody);
1135 crow::connections::systemBus->async_method_call(
1136 [asyncResp, certFile](const boost::system::error_code ec,
1137 const std::string& objectPath) {
1138 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001139 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001140 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1141 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001142 return;
1143 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001144
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001145 sdbusplus::message::object_path path(objectPath);
1146 std::string certId = path.filename();
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001147 const boost::urls::url certURL = crow::utility::urlFromPieces(
1148 "redfish", "v1", "Managers", "bmc", "Truststore",
1149 "Certificates", certId);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001150 getCertificateProperties(asyncResp, objectPath,
1151 certs::authorityServiceName, certId,
1152 certURL, "TrustStore Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001153 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1154 << certFile->getCertFilePath();
1155 },
1156 certs::authorityServiceName, certs::authorityObjectPath,
1157 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +08001158 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001159} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001160
1161/**
1162 * Certificate resource describes a certificate used to prove the identity
1163 * of a component, account or service.
1164 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001165inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001166{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001167 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001168 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001169 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001170 [&app](const crow::Request& req,
1171 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001172 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001173 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001174 {
1175 return;
1176 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001177
1178 BMCWEB_LOG_DEBUG << "Truststore Certificate ID=" << id;
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001179 const boost::urls::url certURL =
1180 crow::utility::urlFromPieces("redfish", "v1", "Managers", "bmc",
1181 "Truststore", "Certificates", id);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001182 std::string objPath =
1183 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1184 getCertificateProperties(asyncResp, objPath,
1185 certs::authorityServiceName, id, certURL,
1186 "TrustStore Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001187 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001188
1189 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001190 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001191 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001192 [&app](const crow::Request& req,
1193 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001194 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001195 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001196 {
1197 return;
1198 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001199
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001200 BMCWEB_LOG_DEBUG << "Delete TrustStore Certificate ID=" << id;
1201 std::string objPath =
1202 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001203
Ed Tanous002d39b2022-05-31 08:59:27 -07001204 crow::connections::systemBus->async_method_call(
1205 [asyncResp, id](const boost::system::error_code ec) {
1206 if (ec)
1207 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001208 messages::resourceNotFound(asyncResp->res, "Certificate", id);
Ed Tanous002d39b2022-05-31 08:59:27 -07001209 return;
1210 }
1211 BMCWEB_LOG_INFO << "Certificate deleted";
1212 asyncResp->res.result(boost::beast::http::status::no_content);
1213 },
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001214 certs::authorityServiceName, objPath, certs::objDeleteIntf,
Ed Tanous002d39b2022-05-31 08:59:27 -07001215 "Delete");
1216 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001217} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001218} // namespace redfish