blob: 6da91ff65e7e87c68a0137a1bbfa6daefaea1fe0 [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001#pragma once
2
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003#include <app.hpp>
Jiaqing Zhao90d2d1e2022-04-13 17:01:57 +08004#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -08005#include <dbus_utility.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -07006#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -07007#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05008
Marri Devender Rao5968cae2019-01-21 10:27:12 -06009namespace redfish
10{
11namespace certs
12{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050013constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
14constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
15constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
16constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
17constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
18constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050019constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060020 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050021constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060022 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050023constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050024 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +080025constexpr char const* baseObjectPath = "/xyz/openbmc_project/certs";
26constexpr char const* httpsObjectPath =
27 "/xyz/openbmc_project/certs/server/https";
28constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050029constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050030 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060031} // namespace certs
32
33/**
34 * The Certificate schema defines a Certificate Service which represents the
35 * actions available to manage certificates and links to where certificates
36 * are installed.
37 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060038
John Edward Broadbent7e860f12021-04-08 15:57:16 -070039// TODO: Issue#61 No entries are available for Certificate
40// service at https://www.dmtf.org/standards/redfish
41// "redfish standard registries". Need to modify after DMTF
42// publish Privilege details for certificate service
43
44inline void requestRoutesCertificateService(App& app)
45{
46 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
Ed Tanoused398212021-06-09 17:05:54 -070047 .privileges(redfish::privileges::getCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -070048 .methods(boost::beast::http::verb::get)(
49 [&app](const crow::Request& req,
50 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +000051 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -070052 {
53 return;
54 }
Ed Tanous14766872022-03-15 10:44:42 -070055
Ed Tanous002d39b2022-05-31 08:59:27 -070056 asyncResp->res.jsonValue["@odata.type"] =
57 "#CertificateService.v1_0_0.CertificateService";
58 asyncResp->res.jsonValue["@odata.id"] =
59 "/redfish/v1/CertificateService";
60 asyncResp->res.jsonValue["Id"] = "CertificateService";
61 asyncResp->res.jsonValue["Name"] = "Certificate Service";
62 asyncResp->res.jsonValue["Description"] =
63 "Actions available to manage certificates";
64 // /redfish/v1/CertificateService/CertificateLocations is something
65 // only ConfigureManager can access then only display when the user
66 // has permissions ConfigureManager
67 Privileges effectiveUserPrivileges =
68 redfish::getUserPrivileges(req.userRole);
69 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
70 effectiveUserPrivileges))
71 {
72 asyncResp->res.jsonValue["CertificateLocations"]["@odata.id"] =
73 "/redfish/v1/CertificateService/CertificateLocations";
74 }
75 asyncResp->res
76 .jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = {
77 {"target",
78 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"},
79 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
80 asyncResp->res
81 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
82 {"target",
83 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR"}};
Abhishek Patel72048782021-06-02 09:53:24 -050084 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070085} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060086
zhanghch058d1b46d2021-04-01 11:18:24 +080087inline std::string getCertificateFromReqBody(
88 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
89 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020090{
91 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
92
93 if (reqJson.is_discarded())
94 {
95 // We did not receive JSON request, proceed as it is RAW data
96 return req.body;
97 }
98
99 std::string certificate;
100 std::optional<std::string> certificateType = "PEM";
101
Willy Tu15ed6782021-12-14 11:03:16 -0800102 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
103 certificate, "CertificateType",
104 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200105 {
106 BMCWEB_LOG_ERROR << "Required parameters are missing";
107 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700108 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200109 }
110
111 if (*certificateType != "PEM")
112 {
113 messages::propertyValueNotInList(asyncResp->res, *certificateType,
114 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700115 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200116 }
117
118 return certificate;
119}
120
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600121/**
122 * Class to create a temporary certificate file for uploading to system
123 */
124class CertificateFile
125{
126 public:
127 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500128 CertificateFile(const CertificateFile&) = delete;
129 CertificateFile& operator=(const CertificateFile&) = delete;
130 CertificateFile(CertificateFile&&) = delete;
131 CertificateFile& operator=(CertificateFile&&) = delete;
Ed Tanous4e23a442022-06-06 09:57:26 -0700132 explicit CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600133 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700134 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700135 'e', 'r', 't', 's', '.', 'X',
136 'X', 'X', 'X', 'X', 'X', '\0'};
137 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800138 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600139 {
140 certDirectory = tempDirectory;
141 certificateFile = certDirectory / "cert.pem";
142 std::ofstream out(certificateFile, std::ofstream::out |
143 std::ofstream::binary |
144 std::ofstream::trunc);
145 out << certString;
146 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800147 BMCWEB_LOG_DEBUG << "Creating certificate file"
148 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600149 }
150 }
151 ~CertificateFile()
152 {
153 if (std::filesystem::exists(certDirectory))
154 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800155 BMCWEB_LOG_DEBUG << "Removing certificate file"
156 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000157 std::error_code ec;
158 std::filesystem::remove_all(certDirectory, ec);
159 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600160 {
161 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800162 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600163 }
164 }
165 }
166 std::string getCertFilePath()
167 {
168 return certificateFile;
169 }
170
171 private:
172 std::filesystem::path certificateFile;
173 std::filesystem::path certDirectory;
174};
175
Patrick Williams59d494e2022-07-22 19:26:55 -0500176static std::unique_ptr<sdbusplus::bus::match_t> csrMatcher;
Marri Devender Rao30215812019-03-18 08:59:21 -0500177/**
178 * @brief Read data from CSR D-bus object and set to response
179 *
180 * @param[in] asyncResp Shared pointer to the response message
181 * @param[in] certURI Link to certifiate collection URI
182 * @param[in] service D-Bus service name
183 * @param[in] certObjPath certificate D-Bus object path
184 * @param[in] csrObjPath CSR D-Bus object path
185 * @return None
186 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800187static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500188 const std::string& certURI, const std::string& service,
189 const std::string& certObjPath,
190 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500191{
192 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
193 << " CSRObjectPath=" << csrObjPath
194 << " service=" << service;
195 crow::connections::systemBus->async_method_call(
196 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500197 const std::string& csr) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700198 if (ec)
199 {
200 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
201 messages::internalError(asyncResp->res);
202 return;
203 }
204 if (csr.empty())
205 {
206 BMCWEB_LOG_ERROR << "CSR read is empty";
207 messages::internalError(asyncResp->res);
208 return;
209 }
210 asyncResp->res.jsonValue["CSRString"] = csr;
211 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
212 certURI;
Marri Devender Rao30215812019-03-18 08:59:21 -0500213 },
214 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
215}
216
217/**
218 * Action to Generate CSR
219 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700220inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500221{
George Liu0fda0f12021-11-16 10:06:17 +0800222 BMCWEB_ROUTE(
223 app,
224 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
Abhishek Patel5344ab82021-07-31 17:42:09 -0500225 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700226 .methods(boost::beast::http::verb::post)(
227 [&app](const crow::Request& req,
228 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000229 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700230 {
231 return;
232 }
233 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500234
Ed Tanous002d39b2022-05-31 08:59:27 -0700235 // Required parameters
236 std::string city;
237 std::string commonName;
238 std::string country;
239 std::string organization;
240 std::string organizationalUnit;
241 std::string state;
242 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800243
Ed Tanous002d39b2022-05-31 08:59:27 -0700244 // Optional parameters
245 std::optional<std::vector<std::string>> optAlternativeNames =
246 std::vector<std::string>();
247 std::optional<std::string> optContactPerson = "";
248 std::optional<std::string> optChallengePassword = "";
249 std::optional<std::string> optEmail = "";
250 std::optional<std::string> optGivenName = "";
251 std::optional<std::string> optInitials = "";
252 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
253 std::optional<std::string> optKeyCurveId = "secp384r1";
254 std::optional<std::string> optKeyPairAlgorithm = "EC";
255 std::optional<std::vector<std::string>> optKeyUsage =
256 std::vector<std::string>();
257 std::optional<std::string> optSurname = "";
258 std::optional<std::string> optUnstructuredName = "";
259 if (!json_util::readJsonAction(
260 req, asyncResp->res, "City", city, "CommonName", commonName,
261 "ContactPerson", optContactPerson, "Country", country,
262 "Organization", organization, "OrganizationalUnit",
263 organizationalUnit, "State", state, "CertificateCollection",
264 certificateCollection, "AlternativeNames", optAlternativeNames,
265 "ChallengePassword", optChallengePassword, "Email", optEmail,
266 "GivenName", optGivenName, "Initials", optInitials,
267 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
268 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
269 optKeyUsage, "Surname", optSurname, "UnstructuredName",
270 optUnstructuredName))
271 {
272 return;
273 }
George Liu0fda0f12021-11-16 10:06:17 +0800274
Ed Tanous002d39b2022-05-31 08:59:27 -0700275 // bmcweb has no way to store or decode a private key challenge
276 // password, which will likely cause bmcweb to crash on startup
277 // if this is not set on a post so not allowing the user to set
278 // value
279 if (!optChallengePassword->empty())
280 {
281 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
282 "ChallengePassword");
283 return;
284 }
George Liu0fda0f12021-11-16 10:06:17 +0800285
Ed Tanous002d39b2022-05-31 08:59:27 -0700286 std::string certURI;
287 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
288 "@odata.id", certURI))
289 {
290 return;
291 }
George Liu0fda0f12021-11-16 10:06:17 +0800292
Ed Tanous002d39b2022-05-31 08:59:27 -0700293 std::string objectPath;
294 std::string service;
Ed Tanous11ba3972022-07-11 09:50:41 -0700295 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700296 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
297 {
298 objectPath = certs::httpsObjectPath;
299 service = certs::httpsServiceName;
300 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700301 else if (certURI.starts_with(
302 "/redfish/v1/AccountService/LDAP/Certificates"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700303 {
304 objectPath = certs::ldapObjectPath;
305 service = certs::ldapServiceName;
306 }
307 else
308 {
309 messages::actionParameterNotSupported(
310 asyncResp->res, "CertificateCollection", "GenerateCSR");
311 return;
312 }
313
314 // supporting only EC and RSA algorithm
315 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
316 {
317 messages::actionParameterNotSupported(
318 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
319 return;
320 }
321
322 // supporting only 2048 key bit length for RSA algorithm due to
323 // time consumed in generating private key
324 if (*optKeyPairAlgorithm == "RSA" &&
325 *optKeyBitLength != rsaKeyBitLength)
326 {
327 messages::propertyValueNotInList(asyncResp->res,
328 std::to_string(*optKeyBitLength),
329 "KeyBitLength");
330 return;
331 }
332
333 // validate KeyUsage supporting only 1 type based on URL
Ed Tanous11ba3972022-07-11 09:50:41 -0700334 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700335 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
336 {
337 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800338 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700339 optKeyUsage->push_back("ServerAuthentication");
George Liu0fda0f12021-11-16 10:06:17 +0800340 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700341 else if (optKeyUsage->size() == 1)
George Liu0fda0f12021-11-16 10:06:17 +0800342 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700343 if ((*optKeyUsage)[0] != "ServerAuthentication")
344 {
345 messages::propertyValueNotInList(
346 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
347 return;
348 }
George Liu0fda0f12021-11-16 10:06:17 +0800349 }
350 else
351 {
352 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700353 asyncResp->res, "KeyUsage", "GenerateCSR");
George Liu0fda0f12021-11-16 10:06:17 +0800354 return;
355 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700356 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700357 else if (certURI.starts_with(
358 "/redfish/v1/AccountService/LDAP/Certificates"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700359 {
360 if (optKeyUsage->empty())
361 {
362 optKeyUsage->push_back("ClientAuthentication");
363 }
364 else if (optKeyUsage->size() == 1)
365 {
366 if ((*optKeyUsage)[0] != "ClientAuthentication")
367 {
368 messages::propertyValueNotInList(
369 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
370 return;
371 }
372 }
373 else
George Liu0fda0f12021-11-16 10:06:17 +0800374 {
375 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700376 asyncResp->res, "KeyUsage", "GenerateCSR");
377 return;
378 }
379 }
380
381 // Only allow one CSR matcher at a time so setting retry
382 // time-out and timer expiry to 10 seconds for now.
383 static const int timeOut = 10;
384 if (csrMatcher)
385 {
386 messages::serviceTemporarilyUnavailable(asyncResp->res,
387 std::to_string(timeOut));
388 return;
389 }
390
391 // Make this static so it survives outside this method
392 static boost::asio::steady_timer timeout(*req.ioService);
393 timeout.expires_after(std::chrono::seconds(timeOut));
394 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
395 csrMatcher = nullptr;
396 if (ec)
397 {
398 // operation_aborted is expected if timer is canceled
399 // before completion.
400 if (ec != boost::asio::error::operation_aborted)
401 {
402 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
403 }
404 return;
405 }
406 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
407 messages::internalError(asyncResp->res);
408 });
409
410 // create a matcher to wait on CSR object
411 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
412 std::string match("type='signal',"
413 "interface='org.freedesktop.DBus.ObjectManager',"
414 "path='" +
415 objectPath +
416 "',"
417 "member='InterfacesAdded'");
Patrick Williams59d494e2022-07-22 19:26:55 -0500418 csrMatcher = std::make_unique<sdbusplus::bus::match_t>(
Ed Tanous002d39b2022-05-31 08:59:27 -0700419 *crow::connections::systemBus, match,
Patrick Williams59d494e2022-07-22 19:26:55 -0500420 [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700421 timeout.cancel();
422 if (m.is_method_error())
423 {
424 BMCWEB_LOG_ERROR << "Dbus method error!!!";
425 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800426 return;
427 }
428
Ed Tanous002d39b2022-05-31 08:59:27 -0700429 dbus::utility::DBusInteracesMap interfacesProperties;
430
431 sdbusplus::message::object_path csrObjectPath;
432 m.read(csrObjectPath, interfacesProperties);
433 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
Ed Tanous02cad962022-06-30 16:50:15 -0700434 for (const auto& interface : interfacesProperties)
George Liu0fda0f12021-11-16 10:06:17 +0800435 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700436 if (interface.first == "xyz.openbmc_project.Certs.CSR")
437 {
438 getCSR(asyncResp, certURI, service, objectPath,
439 csrObjectPath.str);
440 break;
441 }
442 }
443 });
444 crow::connections::systemBus->async_method_call(
445 [asyncResp](const boost::system::error_code ec,
446 const std::string&) {
447 if (ec)
448 {
449 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
450 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800451 return;
452 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700453 },
454 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
455 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
456 commonName, *optContactPerson, country, *optEmail, *optGivenName,
457 *optInitials, *optKeyBitLength, *optKeyCurveId,
458 *optKeyPairAlgorithm, *optKeyUsage, organization,
459 organizationalUnit, state, *optSurname, *optUnstructuredName);
George Liu0fda0f12021-11-16 10:06:17 +0800460 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700461} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500462
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600463/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500464 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600465 *
466 * @param[in] asyncResp Shared pointer to the response message
467 * @param[in] str Issuer/Subject value in key=value pairs
468 * @param[in] type Issuer/Subject
469 * @return None
470 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500471static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600472 const std::string_view value)
473{
474 // example: O=openbmc-project.xyz,CN=localhost
475 std::string_view::iterator i = value.begin();
476 while (i != value.end())
477 {
478 std::string_view::iterator tokenBegin = i;
479 while (i != value.end() && *i != '=')
480 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530481 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600482 }
483 if (i == value.end())
484 {
485 break;
486 }
Ed Tanous271584a2019-07-09 16:24:22 -0700487 const std::string_view key(tokenBegin,
488 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530489 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600490 tokenBegin = i;
491 while (i != value.end() && *i != ',')
492 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530493 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600494 }
Ed Tanous271584a2019-07-09 16:24:22 -0700495 const std::string_view val(tokenBegin,
496 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600497 if (key == "L")
498 {
499 out["City"] = val;
500 }
501 else if (key == "CN")
502 {
503 out["CommonName"] = val;
504 }
505 else if (key == "C")
506 {
507 out["Country"] = val;
508 }
509 else if (key == "O")
510 {
511 out["Organization"] = val;
512 }
513 else if (key == "OU")
514 {
515 out["OrganizationalUnit"] = val;
516 }
517 else if (key == "ST")
518 {
519 out["State"] = val;
520 }
521 // skip comma character
522 if (i != value.end())
523 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530524 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600525 }
526 }
527}
528
529/**
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800530 * @brief Retrieve the installed certificate list
531 *
532 * @param[in] asyncResp Shared pointer to the response message
533 * @param[in] basePath DBus object path to search
534 * @param[in] listPtr Json pointer to the list in asyncResp
535 * @param[in] countPtr Json pointer to the count in asyncResp
536 * @return None
537 */
538static void
539 getCertificateList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
540 const std::string& basePath,
541 const nlohmann::json::json_pointer& listPtr,
542 const nlohmann::json::json_pointer& countPtr)
543{
544 crow::connections::systemBus->async_method_call(
545 [asyncResp, listPtr, countPtr](
546 const boost::system::error_code ec,
547 const dbus::utility::MapperGetSubTreePathsResponse& certPaths) {
548 if (ec)
549 {
550 BMCWEB_LOG_ERROR << "Certificate collection query failed: " << ec;
551 messages::internalError(asyncResp->res);
552 return;
553 }
554
555 nlohmann::json& links = asyncResp->res.jsonValue[listPtr];
556 links = nlohmann::json::array();
557 for (const auto& certPath : certPaths)
558 {
559 sdbusplus::message::object_path objPath(certPath);
560 std::string certId = objPath.filename();
561 if (certId.empty())
562 {
563 BMCWEB_LOG_ERROR << "Invalid certificate objPath " << certPath;
564 continue;
565 }
566
567 boost::urls::url certURL;
568 if (objPath.parent_path() == certs::httpsObjectPath)
569 {
570 certURL = crow::utility::urlFromPieces(
571 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
572 "HTTPS", "Certificates", certId);
573 }
574 else if (objPath.parent_path() == certs::ldapObjectPath)
575 {
576 certURL = crow::utility::urlFromPieces("redfish", "v1",
577 "AccountService", "LDAP",
578 "Certificates", certId);
579 }
580 else if (objPath.parent_path() == certs::authorityObjectPath)
581 {
582 certURL = crow::utility::urlFromPieces(
583 "redfish", "v1", "Managers", "bmc", "Truststore",
584 "Certificates", certId);
585 }
586 else
587 {
588 continue;
589 }
590
591 nlohmann::json::object_t link;
592 link["@odata.id"] = certURL;
593 links.emplace_back(std::move(link));
594 }
595
596 asyncResp->res.jsonValue[countPtr] = links.size();
597 },
598 "xyz.openbmc_project.ObjectMapper",
599 "/xyz/openbmc_project/object_mapper",
600 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", basePath, 0,
601 std::array<const char*, 1>{certs::certPropIntf});
602}
603
604/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600605 * @brief Retrieve the certificates properties and append to the response
606 * message
607 *
608 * @param[in] asyncResp Shared pointer to the response message
609 * @param[in] objectPath Path of the D-Bus service object
610 * @param[in] certId Id of the certificate
611 * @param[in] certURL URL of the certificate object
612 * @param[in] name name of the certificate
613 * @return None
614 */
615static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800616 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800617 const std::string& objectPath, const std::string& service,
618 const std::string& certId, const std::string& certURL,
619 const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600620{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600621 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
622 << " certId=" << certId << " certURl=" << certURL;
623 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800624 [asyncResp, certURL, certId,
625 name](const boost::system::error_code ec,
626 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700627 if (ec)
628 {
629 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800630 messages::resourceNotFound(asyncResp->res, name, certId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700631 return;
632 }
633 asyncResp->res.jsonValue["@odata.id"] = certURL;
634 asyncResp->res.jsonValue["@odata.type"] =
635 "#Certificate.v1_0_0.Certificate";
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800636 asyncResp->res.jsonValue["Id"] = certId;
Ed Tanous002d39b2022-05-31 08:59:27 -0700637 asyncResp->res.jsonValue["Name"] = name;
638 asyncResp->res.jsonValue["Description"] = name;
639 for (const auto& property : properties)
640 {
641 if (property.first == "CertificateString")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600642 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700643 asyncResp->res.jsonValue["CertificateString"] = "";
644 const std::string* value =
645 std::get_if<std::string>(&property.second);
646 if (value != nullptr)
647 {
648 asyncResp->res.jsonValue["CertificateString"] = *value;
649 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600650 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700651 else if (property.first == "KeyUsage")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600652 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700653 nlohmann::json& keyUsage = asyncResp->res.jsonValue["KeyUsage"];
654 keyUsage = nlohmann::json::array();
655 const std::vector<std::string>* value =
656 std::get_if<std::vector<std::string>>(&property.second);
657 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600658 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700659 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600660 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700661 keyUsage.push_back(usage);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600662 }
663 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600664 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700665 else if (property.first == "Issuer")
666 {
667 const std::string* value =
668 std::get_if<std::string>(&property.second);
669 if (value != nullptr)
670 {
671 updateCertIssuerOrSubject(
672 asyncResp->res.jsonValue["Issuer"], *value);
673 }
674 }
675 else if (property.first == "Subject")
676 {
677 const std::string* value =
678 std::get_if<std::string>(&property.second);
679 if (value != nullptr)
680 {
681 updateCertIssuerOrSubject(
682 asyncResp->res.jsonValue["Subject"], *value);
683 }
684 }
685 else if (property.first == "ValidNotAfter")
686 {
687 const uint64_t* value = std::get_if<uint64_t>(&property.second);
688 if (value != nullptr)
689 {
690 asyncResp->res.jsonValue["ValidNotAfter"] =
691 crow::utility::getDateTimeUint(*value);
692 }
693 }
694 else if (property.first == "ValidNotBefore")
695 {
696 const uint64_t* value = std::get_if<uint64_t>(&property.second);
697 if (value != nullptr)
698 {
699 asyncResp->res.jsonValue["ValidNotBefore"] =
700 crow::utility::getDateTimeUint(*value);
701 }
702 }
703 }
704 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600705 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600706 service, objectPath, certs::dbusPropIntf, "GetAll",
707 certs::certPropIntf);
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(
814 [asyncResp, certFile, objectPath, service, certURI, id,
815 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 Zhao75b63a22022-06-08 00:02:04 +0800822 messages::resourceNotFound(asyncResp->res, name, id);
Ed Tanous002d39b2022-05-31 08:59:27 -0700823 return;
824 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700825 messages::internalError(asyncResp->res);
826 return;
827 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800828 getCertificateProperties(asyncResp, objectPath, service, id,
829 certURI, name);
Ed Tanous002d39b2022-05-31 08:59:27 -0700830 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
831 << certFile->getCertFilePath();
832 },
833 service, objectPath, certs::certReplaceIntf, "Replace",
834 certFile->getCertFilePath());
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700835 });
836} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600837
838/**
839 * Certificate resource describes a certificate used to prove the identity
840 * of a component, account or service.
841 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700842
843inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600844{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700845 BMCWEB_ROUTE(
846 app,
847 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700848 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700849 .methods(
850 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700851 get)([&app](const crow::Request& req,
852 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800853 const std::string& id) -> void {
Carson Labrado3ba00072022-06-06 19:40:56 +0000854 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous45ca1b82022-03-25 13:07:27 -0700855 {
856 return;
857 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600858
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800859 BMCWEB_LOG_DEBUG << "HTTPS Certificate ID=" << id;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700860 std::string certURL =
861 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800862 id;
863 std::string objPath =
864 sdbusplus::message::object_path(certs::httpsObjectPath) / id;
865 getCertificateProperties(asyncResp, objPath,
866 certs::httpsServiceName, id, certURL,
867 "HTTPS Certificate");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700868 });
869}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600870
871/**
872 * Collection of HTTPS certificates
873 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700874inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600875{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700876 BMCWEB_ROUTE(app,
877 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700878 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700879 .methods(boost::beast::http::verb::get)(
880 [&app](const crow::Request& req,
881 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000882 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700883 {
884 return;
885 }
886
887 asyncResp->res.jsonValue["@odata.id"] =
888 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
889 asyncResp->res.jsonValue["@odata.type"] =
890 "#CertificateCollection.CertificateCollection";
891 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
892 asyncResp->res.jsonValue["Description"] =
893 "A Collection of HTTPS certificate instances";
894
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800895 getCertificateList(asyncResp, certs::httpsObjectPath,
896 "/Members"_json_pointer,
897 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700898 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600899
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700900 BMCWEB_ROUTE(app,
901 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700902 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700903 .methods(boost::beast::http::verb::post)(
904 [&app](const crow::Request& req,
905 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000906 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700907 {
908 return;
909 }
910 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
911
912 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
913 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
914
915 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
916
917 if (certFileBody.empty())
918 {
919 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
920 messages::unrecognizedRequestBody(asyncResp->res);
921 return;
922 }
923
924 std::shared_ptr<CertificateFile> certFile =
925 std::make_shared<CertificateFile>(certFileBody);
926
927 crow::connections::systemBus->async_method_call(
928 [asyncResp, certFile](const boost::system::error_code ec,
929 const std::string& objectPath) {
930 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700931 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700932 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
933 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700934 return;
935 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800936
937 sdbusplus::message::object_path path(objectPath);
938 std::string certId = path.filename();
Ed Tanous002d39b2022-05-31 08:59:27 -0700939 std::string certURL =
940 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800941 certId;
942 getCertificateProperties(asyncResp, objectPath,
943 certs::httpsServiceName, certId, certURL,
944 "HTTPS Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -0700945 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
946 << certFile->getCertFilePath();
947 },
948 certs::httpsServiceName, certs::httpsObjectPath,
949 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +0800950 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700951} // requestRoutesHTTPSCertificateCollection
952
953/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600954 * The certificate location schema defines a resource that an administrator
955 * can use in order to locate all certificates installed on a given service.
956 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700957inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600958{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700959 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700960 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous002d39b2022-05-31 08:59:27 -0700961 .methods(boost::beast::http::verb::get)(
962 [&app](const crow::Request& req,
963 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000964 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700965 {
966 return;
967 }
968 asyncResp->res.jsonValue["@odata.id"] =
969 "/redfish/v1/CertificateService/CertificateLocations";
970 asyncResp->res.jsonValue["@odata.type"] =
971 "#CertificateLocations.v1_0_0.CertificateLocations";
972 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
973 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
974 asyncResp->res.jsonValue["Description"] =
975 "Defines a resource that an administrator can use in order to "
976 "locate all certificates installed on a given service";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600977
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800978 getCertificateList(asyncResp, certs::baseObjectPath,
979 "/Links/Certificates"_json_pointer,
980 "/Links/Certificates@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700981 });
982}
983// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -0600984
985/**
986 * Collection of LDAP certificates
987 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700988inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600989{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700990 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700991 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700992 .methods(boost::beast::http::verb::get)(
993 [&app](const crow::Request& req,
994 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000995 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700996 {
997 return;
998 }
999
1000 asyncResp->res.jsonValue["@odata.id"] =
1001 "/redfish/v1/AccountService/LDAP/Certificates";
1002 asyncResp->res.jsonValue["@odata.type"] =
1003 "#CertificateCollection.CertificateCollection";
1004 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1005 asyncResp->res.jsonValue["Description"] =
1006 "A Collection of LDAP certificate instances";
1007
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001008 getCertificateList(asyncResp, certs::ldapObjectPath,
1009 "/Members"_json_pointer,
1010 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001011 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001012
1013 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001014 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001015 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001016 [&app](const crow::Request& req,
1017 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001018 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001019 {
1020 return;
1021 }
1022 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001023
Ed Tanous002d39b2022-05-31 08:59:27 -07001024 if (certFileBody.empty())
1025 {
1026 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1027 messages::unrecognizedRequestBody(asyncResp->res);
1028 return;
1029 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001030
Ed Tanous002d39b2022-05-31 08:59:27 -07001031 std::shared_ptr<CertificateFile> certFile =
1032 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001033
Ed Tanous002d39b2022-05-31 08:59:27 -07001034 crow::connections::systemBus->async_method_call(
1035 [asyncResp, certFile](const boost::system::error_code ec,
1036 const std::string& objectPath) {
1037 if (ec)
1038 {
1039 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1040 messages::internalError(asyncResp->res);
1041 return;
1042 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001043
1044 sdbusplus::message::object_path path(objectPath);
1045 std::string certId = path.filename();
Ed Tanous002d39b2022-05-31 08:59:27 -07001046 std::string certURL =
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001047 "/redfish/v1/AccountService/LDAP/Certificates/" + certId;
1048 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;
1077 std::string certURL =
1078 "/redfish/v1/AccountService/LDAP/Certificates/" + id;
1079 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();
1145 std::string certURL =
1146 "/redfish/v1/Managers/bmc/Truststore/Certificates/" + certId;
1147 getCertificateProperties(asyncResp, objectPath,
1148 certs::authorityServiceName, certId,
1149 certURL, "TrustStore Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001150 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1151 << certFile->getCertFilePath();
1152 },
1153 certs::authorityServiceName, certs::authorityObjectPath,
1154 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +08001155 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001156} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001157
1158/**
1159 * Certificate resource describes a certificate used to prove the identity
1160 * of a component, account or service.
1161 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001162inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001163{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001164 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001165 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001166 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001167 [&app](const crow::Request& req,
1168 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001169 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001170 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001171 {
1172 return;
1173 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001174
1175 BMCWEB_LOG_DEBUG << "Truststore Certificate ID=" << id;
Ed Tanous002d39b2022-05-31 08:59:27 -07001176 std::string certURL =
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001177 "/redfish/v1/Managers/bmc/Truststore/Certificates/" + id;
1178 std::string objPath =
1179 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1180 getCertificateProperties(asyncResp, objPath,
1181 certs::authorityServiceName, id, certURL,
1182 "TrustStore Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001183 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001184
1185 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001186 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001187 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001188 [&app](const crow::Request& req,
1189 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001190 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001191 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001192 {
1193 return;
1194 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001195
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001196 BMCWEB_LOG_DEBUG << "Delete TrustStore Certificate ID=" << id;
1197 std::string objPath =
1198 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001199
Ed Tanous002d39b2022-05-31 08:59:27 -07001200 crow::connections::systemBus->async_method_call(
1201 [asyncResp, id](const boost::system::error_code ec) {
1202 if (ec)
1203 {
1204 messages::resourceNotFound(asyncResp->res,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001205 "TrustStore Certificate", id);
Ed Tanous002d39b2022-05-31 08:59:27 -07001206 return;
1207 }
1208 BMCWEB_LOG_INFO << "Certificate deleted";
1209 asyncResp->res.result(boost::beast::http::status::no_content);
1210 },
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001211 certs::authorityServiceName, objPath, certs::objDeleteIntf,
Ed Tanous002d39b2022-05-31 08:59:27 -07001212 "Delete");
1213 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001214} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001215} // namespace redfish