blob: 9e958cb98c566b85dc5f5f52db4a1784b91e50cd [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,
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800618 const std::string& certId, const boost::urls::url& certURL,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800619 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 }
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800704 asyncResp->res.addHeader(
705 "Location", std::string_view(certURL.data(), certURL.size()));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600706 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600707 service, objectPath, certs::dbusPropIntf, "GetAll",
708 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600709}
710
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600711/**
712 * Action to replace an existing certificate
713 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700714inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600715{
George Liu0fda0f12021-11-16 10:06:17 +0800716 BMCWEB_ROUTE(
717 app,
718 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700719 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700720 .methods(boost::beast::http::verb::post)(
721 [&app](const crow::Request& req,
722 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000723 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700724 {
725 return;
726 }
727 std::string certificate;
728 nlohmann::json certificateUri;
729 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600730
Ed Tanous002d39b2022-05-31 08:59:27 -0700731 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
732 certificate, "CertificateUri",
733 certificateUri, "CertificateType",
734 certificateType))
735 {
736 BMCWEB_LOG_ERROR << "Required parameters are missing";
737 messages::internalError(asyncResp->res);
738 return;
739 }
740
741 if (!certificateType)
742 {
743 // should never happen, but it never hurts to be paranoid.
744 return;
745 }
746 if (certificateType != "PEM")
747 {
748 messages::actionParameterNotSupported(
749 asyncResp->res, "CertificateType", "ReplaceCertificate");
750 return;
751 }
752
753 std::string certURI;
754 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
755 "@odata.id", certURI))
756 {
757 messages::actionParameterMissing(
758 asyncResp->res, "ReplaceCertificate", "CertificateUri");
759 return;
760 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800761 BMCWEB_LOG_INFO << "Certificate URI to replace: " << certURI;
Ed Tanous002d39b2022-05-31 08:59:27 -0700762
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800763 boost::urls::result<boost::urls::url_view> parsedUrl =
764 boost::urls::parse_relative_ref(certURI);
765 if (!parsedUrl)
Ed Tanous002d39b2022-05-31 08:59:27 -0700766 {
767 messages::actionParameterValueFormatError(asyncResp->res, certURI,
768 "CertificateUri",
769 "ReplaceCertificate");
770 return;
771 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800772
773 std::string id;
774 sdbusplus::message::object_path objectPath;
Ed Tanous002d39b2022-05-31 08:59:27 -0700775 std::string name;
776 std::string service;
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800777 if (crow::utility::readUrlSegments(
778 *parsedUrl, "redfish", "v1", "Managers", "bmc",
779 "NetworkProtocol", "HTTPS", "Certificates", std::ref(id)))
Ed Tanous002d39b2022-05-31 08:59:27 -0700780 {
781 objectPath =
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800782 sdbusplus::message::object_path(certs::httpsObjectPath) / id;
Ed Tanous002d39b2022-05-31 08:59:27 -0700783 name = "HTTPS certificate";
784 service = certs::httpsServiceName;
785 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800786 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
787 "AccountService", "LDAP",
788 "Certificates", std::ref(id)))
Ed Tanous002d39b2022-05-31 08:59:27 -0700789 {
790 objectPath =
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800791 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
Ed Tanous002d39b2022-05-31 08:59:27 -0700792 name = "LDAP certificate";
793 service = certs::ldapServiceName;
794 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800795 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
796 "Managers", "bmc", "Truststore",
797 "Certificates", std::ref(id)))
Ed Tanous002d39b2022-05-31 08:59:27 -0700798 {
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800799 objectPath =
800 sdbusplus::message::object_path(certs::authorityObjectPath) /
801 id;
Ed Tanous002d39b2022-05-31 08:59:27 -0700802 name = "TrustStore certificate";
803 service = certs::authorityServiceName;
804 }
805 else
806 {
807 messages::actionParameterNotSupported(
808 asyncResp->res, "CertificateUri", "ReplaceCertificate");
809 return;
810 }
811
812 std::shared_ptr<CertificateFile> certFile =
813 std::make_shared<CertificateFile>(certificate);
814 crow::connections::systemBus->async_method_call(
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800815 [asyncResp, certFile, objectPath, service, url{*parsedUrl}, id,
Ed Tanous002d39b2022-05-31 08:59:27 -0700816 name](const boost::system::error_code ec) {
817 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700818 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700819 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
820 if (ec.value() ==
821 boost::system::linux_error::bad_request_descriptor)
822 {
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800823 messages::resourceNotFound(asyncResp->res, name, 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 {
1208 messages::resourceNotFound(asyncResp->res,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001209 "TrustStore Certificate", id);
Ed Tanous002d39b2022-05-31 08:59:27 -07001210 return;
1211 }
1212 BMCWEB_LOG_INFO << "Certificate deleted";
1213 asyncResp->res.result(boost::beast::http::status::no_content);
1214 },
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001215 certs::authorityServiceName, objPath, certs::objDeleteIntf,
Ed Tanous002d39b2022-05-31 08:59:27 -07001216 "Delete");
1217 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001218} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001219} // namespace redfish