blob: ccf5e9ae096f5d65b0a1dd94ae965f4aa2b0c2c3 [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>
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +02004#include <boost/convert.hpp>
5#include <boost/convert/strtol.hpp>
Jiaqing Zhao90d2d1e2022-04-13 17:01:57 +08006#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -08007#include <dbus_utility.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -07008#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -07009#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050010
Marri Devender Rao5968cae2019-01-21 10:27:12 -060011namespace redfish
12{
13namespace certs
14{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050015constexpr char const* httpsObjectPath =
Marri Devender Rao5968cae2019-01-21 10:27:12 -060016 "/xyz/openbmc_project/certs/server/https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050017constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
18constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
19constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
20constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
21constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
22constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
23constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
24constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060025 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060027 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050028constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050029 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050030constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050031 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060032} // namespace certs
33
34/**
35 * The Certificate schema defines a Certificate Service which represents the
36 * actions available to manage certificates and links to where certificates
37 * are installed.
38 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060039
John Edward Broadbent7e860f12021-04-08 15:57:16 -070040// TODO: Issue#61 No entries are available for Certificate
41// service at https://www.dmtf.org/standards/redfish
42// "redfish standard registries". Need to modify after DMTF
43// publish Privilege details for certificate service
44
45inline void requestRoutesCertificateService(App& app)
46{
47 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
Ed Tanoused398212021-06-09 17:05:54 -070048 .privileges(redfish::privileges::getCertificateService)
Ed Tanous45ca1b82022-03-25 13:07:27 -070049 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
50 const std::shared_ptr<
51 bmcweb::AsyncResp>&
52 asyncResp) {
53 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
54 {
55 return;
56 }
Abhishek Patel72048782021-06-02 09:53:24 -050057 asyncResp->res.jsonValue = {
58 {"@odata.type",
59 "#CertificateService.v1_0_0.CertificateService"},
60 {"@odata.id", "/redfish/v1/CertificateService"},
61 {"Id", "CertificateService"},
62 {"Name", "Certificate Service"},
63 {"Description", "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 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -070072 asyncResp->res.jsonValue["CertificateLocations"] = {
73 {"@odata.id",
74 "/redfish/v1/CertificateService/CertificateLocations"}};
Abhishek Patel72048782021-06-02 09:53:24 -050075 }
George Liu0fda0f12021-11-16 10:06:17 +080076 asyncResp->res
77 .jsonValue["Actions"]
78 ["#CertificateService.ReplaceCertificate"] = {
79 {"target",
80 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"},
81 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
Abhishek Patel72048782021-06-02 09:53:24 -050082 asyncResp->res
83 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
George Liu0fda0f12021-11-16 10:06:17 +080084 {"target",
85 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR"}};
Abhishek Patel72048782021-06-02 09:53:24 -050086 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070087} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060088
Marri Devender Rao5968cae2019-01-21 10:27:12 -060089/**
90 * @brief Find the ID specified in the URL
91 * Finds the numbers specified after the last "/" in the URL and returns.
92 * @param[in] path URL
93 * @return -1 on failure and number on success
94 */
Ed Tanous23a21a12020-07-25 04:45:05 +000095inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060096{
Ed Tanousf23b7292020-10-15 09:41:17 -070097 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -060098 if (found == std::string::npos)
99 {
100 return -1;
101 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200102
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600103 if ((found + 1) < url.length())
104 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600105 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200106
107 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600108 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200109
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600110 return -1;
111}
112
zhanghch058d1b46d2021-04-01 11:18:24 +0800113inline std::string getCertificateFromReqBody(
114 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
115 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200116{
117 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
118
119 if (reqJson.is_discarded())
120 {
121 // We did not receive JSON request, proceed as it is RAW data
122 return req.body;
123 }
124
125 std::string certificate;
126 std::optional<std::string> certificateType = "PEM";
127
Willy Tu15ed6782021-12-14 11:03:16 -0800128 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
129 certificate, "CertificateType",
130 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200131 {
132 BMCWEB_LOG_ERROR << "Required parameters are missing";
133 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700134 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200135 }
136
137 if (*certificateType != "PEM")
138 {
139 messages::propertyValueNotInList(asyncResp->res, *certificateType,
140 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700141 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200142 }
143
144 return certificate;
145}
146
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600147/**
148 * Class to create a temporary certificate file for uploading to system
149 */
150class CertificateFile
151{
152 public:
153 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500154 CertificateFile(const CertificateFile&) = delete;
155 CertificateFile& operator=(const CertificateFile&) = delete;
156 CertificateFile(CertificateFile&&) = delete;
157 CertificateFile& operator=(CertificateFile&&) = delete;
158 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600159 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700160 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700161 'e', 'r', 't', 's', '.', 'X',
162 'X', 'X', 'X', 'X', 'X', '\0'};
163 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800164 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600165 {
166 certDirectory = tempDirectory;
167 certificateFile = certDirectory / "cert.pem";
168 std::ofstream out(certificateFile, std::ofstream::out |
169 std::ofstream::binary |
170 std::ofstream::trunc);
171 out << certString;
172 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800173 BMCWEB_LOG_DEBUG << "Creating certificate file"
174 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600175 }
176 }
177 ~CertificateFile()
178 {
179 if (std::filesystem::exists(certDirectory))
180 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800181 BMCWEB_LOG_DEBUG << "Removing certificate file"
182 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000183 std::error_code ec;
184 std::filesystem::remove_all(certDirectory, ec);
185 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600186 {
187 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800188 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600189 }
190 }
191 }
192 std::string getCertFilePath()
193 {
194 return certificateFile;
195 }
196
197 private:
198 std::filesystem::path certificateFile;
199 std::filesystem::path certDirectory;
200};
201
Marri Devender Rao30215812019-03-18 08:59:21 -0500202static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
203/**
204 * @brief Read data from CSR D-bus object and set to response
205 *
206 * @param[in] asyncResp Shared pointer to the response message
207 * @param[in] certURI Link to certifiate collection URI
208 * @param[in] service D-Bus service name
209 * @param[in] certObjPath certificate D-Bus object path
210 * @param[in] csrObjPath CSR D-Bus object path
211 * @return None
212 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800213static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500214 const std::string& certURI, const std::string& service,
215 const std::string& certObjPath,
216 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500217{
218 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
219 << " CSRObjectPath=" << csrObjPath
220 << " service=" << service;
221 crow::connections::systemBus->async_method_call(
222 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500223 const std::string& csr) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500224 if (ec)
225 {
226 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
227 messages::internalError(asyncResp->res);
228 return;
229 }
230 if (csr.empty())
231 {
232 BMCWEB_LOG_ERROR << "CSR read is empty";
233 messages::internalError(asyncResp->res);
234 return;
235 }
236 asyncResp->res.jsonValue["CSRString"] = csr;
237 asyncResp->res.jsonValue["CertificateCollection"] = {
238 {"@odata.id", certURI}};
239 },
240 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
241}
242
243/**
244 * Action to Generate CSR
245 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700246inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500247{
George Liu0fda0f12021-11-16 10:06:17 +0800248 BMCWEB_ROUTE(
249 app,
250 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
Ed Tanoused398212021-06-09 17:05:54 -0700251 // Incorrect Privilege; Should be ConfigureManager
252 //.privileges(redfish::privileges::postCertificateService)
Ed Tanous432a8902021-06-14 15:28:56 -0700253 .privileges({{"ConfigureComponents"}})
George Liu0fda0f12021-11-16 10:06:17 +0800254 .methods(
255 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700256 post)([&app](
257 const crow::Request& req,
258 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
259 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
260 {
261 return;
262 }
George Liu0fda0f12021-11-16 10:06:17 +0800263 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500264
George Liu0fda0f12021-11-16 10:06:17 +0800265 // Required parameters
266 std::string city;
267 std::string commonName;
268 std::string country;
269 std::string organization;
270 std::string organizationalUnit;
271 std::string state;
272 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800273
George Liu0fda0f12021-11-16 10:06:17 +0800274 // Optional parameters
275 std::optional<std::vector<std::string>> optAlternativeNames =
276 std::vector<std::string>();
277 std::optional<std::string> optContactPerson = "";
278 std::optional<std::string> optChallengePassword = "";
279 std::optional<std::string> optEmail = "";
280 std::optional<std::string> optGivenName = "";
281 std::optional<std::string> optInitials = "";
282 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
283 std::optional<std::string> optKeyCurveId = "secp384r1";
284 std::optional<std::string> optKeyPairAlgorithm = "EC";
285 std::optional<std::vector<std::string>> optKeyUsage =
286 std::vector<std::string>();
287 std::optional<std::string> optSurname = "";
288 std::optional<std::string> optUnstructuredName = "";
Willy Tu15ed6782021-12-14 11:03:16 -0800289 if (!json_util::readJsonAction(
George Liu0fda0f12021-11-16 10:06:17 +0800290 req, asyncResp->res, "City", city, "CommonName", commonName,
291 "ContactPerson", optContactPerson, "Country", country,
292 "Organization", organization, "OrganizationalUnit",
293 organizationalUnit, "State", state, "CertificateCollection",
294 certificateCollection, "AlternativeNames",
295 optAlternativeNames, "ChallengePassword",
296 optChallengePassword, "Email", optEmail, "GivenName",
297 optGivenName, "Initials", optInitials, "KeyBitLength",
298 optKeyBitLength, "KeyCurveId", optKeyCurveId,
299 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
300 optKeyUsage, "Surname", optSurname, "UnstructuredName",
301 optUnstructuredName))
302 {
303 return;
304 }
305
306 // bmcweb has no way to store or decode a private key challenge
307 // password, which will likely cause bmcweb to crash on startup
308 // if this is not set on a post so not allowing the user to set
309 // value
Ed Tanous26f69762022-01-25 09:49:11 -0800310 if (!optChallengePassword->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800311 {
312 messages::actionParameterNotSupported(
313 asyncResp->res, "GenerateCSR", "ChallengePassword");
314 return;
315 }
316
317 std::string certURI;
318 if (!redfish::json_util::readJson(certificateCollection,
319 asyncResp->res, "@odata.id",
320 certURI))
321 {
322 return;
323 }
324
325 std::string objectPath;
326 std::string service;
327 if (boost::starts_with(
328 certURI,
329 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
330 {
331 objectPath = certs::httpsObjectPath;
332 service = certs::httpsServiceName;
333 }
334 else if (boost::starts_with(
335 certURI,
336 "/redfish/v1/AccountService/LDAP/Certificates"))
337 {
338 objectPath = certs::ldapObjectPath;
339 service = certs::ldapServiceName;
340 }
341 else
342 {
343 messages::actionParameterNotSupported(
344 asyncResp->res, "CertificateCollection", "GenerateCSR");
345 return;
346 }
347
348 // supporting only EC and RSA algorithm
349 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
350 {
351 messages::actionParameterNotSupported(
352 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
353 return;
354 }
355
356 // supporting only 2048 key bit length for RSA algorithm due to
357 // time consumed in generating private key
358 if (*optKeyPairAlgorithm == "RSA" &&
359 *optKeyBitLength != rsaKeyBitLength)
360 {
361 messages::propertyValueNotInList(
362 asyncResp->res, std::to_string(*optKeyBitLength),
363 "KeyBitLength");
364 return;
365 }
366
367 // validate KeyUsage supporting only 1 type based on URL
368 if (boost::starts_with(
369 certURI,
370 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
371 {
Ed Tanous26f69762022-01-25 09:49:11 -0800372 if (optKeyUsage->empty())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700373 {
George Liu0fda0f12021-11-16 10:06:17 +0800374 optKeyUsage->push_back("ServerAuthentication");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700375 }
George Liu0fda0f12021-11-16 10:06:17 +0800376 else if (optKeyUsage->size() == 1)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700377 {
George Liu0fda0f12021-11-16 10:06:17 +0800378 if ((*optKeyUsage)[0] != "ServerAuthentication")
379 {
380 messages::propertyValueNotInList(
381 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
382 return;
383 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700384 }
385 else
386 {
387 messages::actionParameterNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800388 asyncResp->res, "KeyUsage", "GenerateCSR");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700389 return;
390 }
George Liu0fda0f12021-11-16 10:06:17 +0800391 }
392 else if (boost::starts_with(
393 certURI,
394 "/redfish/v1/AccountService/LDAP/Certificates"))
395 {
Ed Tanous26f69762022-01-25 09:49:11 -0800396 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800397 {
398 optKeyUsage->push_back("ClientAuthentication");
399 }
400 else if (optKeyUsage->size() == 1)
401 {
402 if ((*optKeyUsage)[0] != "ClientAuthentication")
403 {
404 messages::propertyValueNotInList(
405 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
406 return;
407 }
408 }
409 else
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700410 {
411 messages::actionParameterNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800412 asyncResp->res, "KeyUsage", "GenerateCSR");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700413 return;
414 }
George Liu0fda0f12021-11-16 10:06:17 +0800415 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500416
George Liu0fda0f12021-11-16 10:06:17 +0800417 // Only allow one CSR matcher at a time so setting retry
418 // time-out and timer expiry to 10 seconds for now.
419 static const int timeOut = 10;
420 if (csrMatcher)
421 {
422 messages::serviceTemporarilyUnavailable(
423 asyncResp->res, std::to_string(timeOut));
424 return;
425 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500426
George Liu0fda0f12021-11-16 10:06:17 +0800427 // Make this static so it survives outside this method
428 static boost::asio::steady_timer timeout(*req.ioService);
429 timeout.expires_after(std::chrono::seconds(timeOut));
430 timeout.async_wait(
431 [asyncResp](const boost::system::error_code& ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700432 csrMatcher = nullptr;
433 if (ec)
434 {
435 // operation_aborted is expected if timer is canceled
436 // before completion.
437 if (ec != boost::asio::error::operation_aborted)
438 {
439 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
440 }
441 return;
442 }
443 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
444 messages::internalError(asyncResp->res);
445 });
446
George Liu0fda0f12021-11-16 10:06:17 +0800447 // create a matcher to wait on CSR object
448 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
449 std::string match("type='signal',"
450 "interface='org.freedesktop.DBus.ObjectManager',"
451 "path='" +
452 objectPath +
453 "',"
454 "member='InterfacesAdded'");
455 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
456 *crow::connections::systemBus, match,
457 [asyncResp, service, objectPath,
458 certURI](sdbusplus::message::message& m) {
459 timeout.cancel();
460 if (m.is_method_error())
461 {
462 BMCWEB_LOG_ERROR << "Dbus method error!!!";
463 messages::internalError(asyncResp->res);
464 return;
465 }
Ed Tanousb9d36b42022-02-26 21:42:46 -0800466
467 dbus::utility::DBusInteracesMap interfacesProperties;
468
George Liu0fda0f12021-11-16 10:06:17 +0800469 sdbusplus::message::object_path csrObjectPath;
470 m.read(csrObjectPath, interfacesProperties);
471 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
472 for (auto& interface : interfacesProperties)
473 {
474 if (interface.first == "xyz.openbmc_project.Certs.CSR")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700475 {
George Liu0fda0f12021-11-16 10:06:17 +0800476 getCSR(asyncResp, certURI, service, objectPath,
477 csrObjectPath.str);
478 break;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700479 }
George Liu0fda0f12021-11-16 10:06:17 +0800480 }
481 });
482 crow::connections::systemBus->async_method_call(
Ed Tanous914e2d52022-01-07 11:38:34 -0800483 [asyncResp](const boost::system::error_code ec,
George Liu0fda0f12021-11-16 10:06:17 +0800484 const std::string&) {
485 if (ec)
486 {
487 BMCWEB_LOG_ERROR << "DBUS response error: "
488 << ec.message();
489 messages::internalError(asyncResp->res);
490 return;
491 }
492 },
493 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
494 "GenerateCSR", *optAlternativeNames, *optChallengePassword,
495 city, commonName, *optContactPerson, country, *optEmail,
496 *optGivenName, *optInitials, *optKeyBitLength, *optKeyCurveId,
497 *optKeyPairAlgorithm, *optKeyUsage, organization,
498 organizationalUnit, state, *optSurname, *optUnstructuredName);
499 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700500} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500501
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600502/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500503 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600504 *
505 * @param[in] asyncResp Shared pointer to the response message
506 * @param[in] str Issuer/Subject value in key=value pairs
507 * @param[in] type Issuer/Subject
508 * @return None
509 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500510static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600511 const std::string_view value)
512{
513 // example: O=openbmc-project.xyz,CN=localhost
514 std::string_view::iterator i = value.begin();
515 while (i != value.end())
516 {
517 std::string_view::iterator tokenBegin = i;
518 while (i != value.end() && *i != '=')
519 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530520 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600521 }
522 if (i == value.end())
523 {
524 break;
525 }
Ed Tanous271584a2019-07-09 16:24:22 -0700526 const std::string_view key(tokenBegin,
527 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530528 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600529 tokenBegin = i;
530 while (i != value.end() && *i != ',')
531 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530532 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600533 }
Ed Tanous271584a2019-07-09 16:24:22 -0700534 const std::string_view val(tokenBegin,
535 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600536 if (key == "L")
537 {
538 out["City"] = val;
539 }
540 else if (key == "CN")
541 {
542 out["CommonName"] = val;
543 }
544 else if (key == "C")
545 {
546 out["Country"] = val;
547 }
548 else if (key == "O")
549 {
550 out["Organization"] = val;
551 }
552 else if (key == "OU")
553 {
554 out["OrganizationalUnit"] = val;
555 }
556 else if (key == "ST")
557 {
558 out["State"] = val;
559 }
560 // skip comma character
561 if (i != value.end())
562 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530563 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600564 }
565 }
566}
567
568/**
569 * @brief Retrieve the certificates properties and append to the response
570 * message
571 *
572 * @param[in] asyncResp Shared pointer to the response message
573 * @param[in] objectPath Path of the D-Bus service object
574 * @param[in] certId Id of the certificate
575 * @param[in] certURL URL of the certificate object
576 * @param[in] name name of the certificate
577 * @return None
578 */
579static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800580 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
581 const std::string& objectPath, const std::string& service, long certId,
582 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600583{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600584 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
585 << " certId=" << certId << " certURl=" << certURL;
586 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800587 [asyncResp, certURL, certId,
588 name](const boost::system::error_code ec,
589 const dbus::utility::DBusPropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600590 if (ec)
591 {
592 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500593 messages::resourceNotFound(asyncResp->res, name,
594 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600595 return;
596 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600597 asyncResp->res.jsonValue = {
598 {"@odata.id", certURL},
599 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600600 {"Id", std::to_string(certId)},
601 {"Name", name},
602 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500603 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600604 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600605 if (property.first == "CertificateString")
606 {
607 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500608 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600609 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800610 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600611 {
612 asyncResp->res.jsonValue["CertificateString"] = *value;
613 }
614 }
615 else if (property.first == "KeyUsage")
616 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500617 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600618 asyncResp->res.jsonValue["KeyUsage"];
619 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500620 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600621 std::get_if<std::vector<std::string>>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800622 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600623 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500624 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600625 {
626 keyUsage.push_back(usage);
627 }
628 }
629 }
630 else if (property.first == "Issuer")
631 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500632 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600633 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800634 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600635 {
636 updateCertIssuerOrSubject(
637 asyncResp->res.jsonValue["Issuer"], *value);
638 }
639 }
640 else if (property.first == "Subject")
641 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500642 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600643 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800644 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600645 {
646 updateCertIssuerOrSubject(
647 asyncResp->res.jsonValue["Subject"], *value);
648 }
649 }
650 else if (property.first == "ValidNotAfter")
651 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500652 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600653 std::get_if<uint64_t>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800654 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600655 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600656 asyncResp->res.jsonValue["ValidNotAfter"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800657 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600658 }
659 }
660 else if (property.first == "ValidNotBefore")
661 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500662 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600663 std::get_if<uint64_t>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800664 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600665 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600666 asyncResp->res.jsonValue["ValidNotBefore"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800667 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600668 }
669 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600670 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600671 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600672 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600673 service, objectPath, certs::dbusPropIntf, "GetAll",
674 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600675}
676
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600677/**
678 * Action to replace an existing certificate
679 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700680inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600681{
George Liu0fda0f12021-11-16 10:06:17 +0800682 BMCWEB_ROUTE(
683 app,
684 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700685 .privileges(redfish::privileges::postCertificateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700686 .methods(
687 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700688 post)([&app](
689 const crow::Request& req,
690 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
691 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
692 {
693 return;
694 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700695 std::string certificate;
696 nlohmann::json certificateUri;
697 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600698
Willy Tu15ed6782021-12-14 11:03:16 -0800699 if (!json_util::readJsonAction(req, asyncResp->res,
700 "CertificateString", certificate,
701 "CertificateUri", certificateUri,
702 "CertificateType", certificateType))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700703 {
704 BMCWEB_LOG_ERROR << "Required parameters are missing";
705 messages::internalError(asyncResp->res);
706 return;
707 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800708
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700709 if (!certificateType)
710 {
711 // should never happen, but it never hurts to be paranoid.
712 return;
713 }
714 if (certificateType != "PEM")
715 {
716 messages::actionParameterNotSupported(
717 asyncResp->res, "CertificateType", "ReplaceCertificate");
718 return;
719 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600720
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700721 std::string certURI;
722 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
723 "@odata.id", certURI))
724 {
725 messages::actionParameterMissing(
726 asyncResp->res, "ReplaceCertificate", "CertificateUri");
727 return;
728 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600729
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700730 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
731 long id = getIDFromURL(certURI);
732 if (id < 0)
733 {
734 messages::actionParameterValueFormatError(
735 asyncResp->res, certURI, "CertificateUri",
736 "ReplaceCertificate");
737 return;
738 }
739 std::string objectPath;
740 std::string name;
741 std::string service;
George Liu0fda0f12021-11-16 10:06:17 +0800742 if (boost::starts_with(
743 certURI,
744 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700745 {
746 objectPath = std::string(certs::httpsObjectPath) + "/" +
747 std::to_string(id);
748 name = "HTTPS certificate";
749 service = certs::httpsServiceName;
750 }
751 else if (boost::starts_with(
752 certURI,
753 "/redfish/v1/AccountService/LDAP/Certificates/"))
754 {
755 objectPath = std::string(certs::ldapObjectPath) + "/" +
756 std::to_string(id);
757 name = "LDAP certificate";
758 service = certs::ldapServiceName;
759 }
760 else if (boost::starts_with(
761 certURI,
762 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
763 {
764 objectPath = std::string(certs::authorityObjectPath) + "/" +
765 std::to_string(id);
766 name = "TrustStore certificate";
767 service = certs::authorityServiceName;
768 }
769 else
770 {
771 messages::actionParameterNotSupported(
772 asyncResp->res, "CertificateUri", "ReplaceCertificate");
773 return;
774 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600775
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700776 std::shared_ptr<CertificateFile> certFile =
777 std::make_shared<CertificateFile>(certificate);
778 crow::connections::systemBus->async_method_call(
779 [asyncResp, certFile, objectPath, service, certURI, id,
780 name](const boost::system::error_code ec) {
781 if (ec)
782 {
783 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Jiaqing Zhao90d2d1e2022-04-13 17:01:57 +0800784 if (ec.value() ==
785 boost::system::linux_error::bad_request_descriptor)
786 {
787 messages::resourceNotFound(asyncResp->res, name,
788 std::to_string(id));
789 return;
790 }
791 messages::internalError(asyncResp->res);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700792 return;
793 }
794 getCertificateProperties(asyncResp, objectPath, service, id,
795 certURI, name);
796 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
797 << certFile->getCertFilePath();
798 },
799 service, objectPath, certs::certReplaceIntf, "Replace",
800 certFile->getCertFilePath());
801 });
802} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600803
804/**
805 * Certificate resource describes a certificate used to prove the identity
806 * of a component, account or service.
807 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700808
809inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600810{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700811 BMCWEB_ROUTE(
812 app,
813 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700814 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700815 .methods(
816 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700817 get)([&app](const crow::Request& req,
818 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
819 const std::string& param) -> void {
820 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
821 {
822 return;
823 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700824 if (param.empty())
825 {
826 messages::internalError(asyncResp->res);
827 return;
828 }
829 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600830
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700831 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
832 << std::to_string(id);
833 std::string certURL =
834 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
835 std::to_string(id);
836 std::string objectPath = certs::httpsObjectPath;
837 objectPath += "/";
838 objectPath += std::to_string(id);
839 getCertificateProperties(asyncResp, objectPath,
840 certs::httpsServiceName, id, certURL,
841 "HTTPS Certificate");
842 });
843}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600844
845/**
846 * Collection of HTTPS certificates
847 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700848inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600849{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700850 BMCWEB_ROUTE(app,
851 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700852 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700853 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
854 const std::shared_ptr<
855 bmcweb::AsyncResp>&
856 asyncResp) {
857 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
858 {
859 return;
860 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700861 asyncResp->res.jsonValue = {
862 {"@odata.id",
863 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
864 {"@odata.type", "#CertificateCollection.CertificateCollection"},
865 {"Name", "HTTPS Certificates Collection"},
866 {"Description", "A Collection of HTTPS certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +0800867
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700868 crow::connections::systemBus->async_method_call(
869 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800870 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700871 if (ec)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600872 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700873 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
874 messages::internalError(asyncResp->res);
875 return;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600876 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700877 nlohmann::json& members =
878 asyncResp->res.jsonValue["Members"];
879 members = nlohmann::json::array();
880 for (const auto& cert : certs)
881 {
882 long id = getIDFromURL(cert.first.str);
883 if (id >= 0)
884 {
885 members.push_back(
886 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +0800887 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700888 std::to_string(id)}});
889 }
890 }
891 asyncResp->res.jsonValue["Members@odata.count"] =
892 members.size();
893 },
894 certs::httpsServiceName, certs::httpsObjectPath,
895 certs::dbusObjManagerIntf, "GetManagedObjects");
896 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600897
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700898 BMCWEB_ROUTE(app,
899 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700900 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +0800901 .methods(
902 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700903 post)([&app](
904 const crow::Request& req,
905 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
906 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
907 {
908 return;
909 }
George Liu0fda0f12021-11-16 10:06:17 +0800910 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
zhanghch058d1b46d2021-04-01 11:18:24 +0800911
George Liu0fda0f12021-11-16 10:06:17 +0800912 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
913 {"Description", "HTTPS Certificate"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600914
George Liu0fda0f12021-11-16 10:06:17 +0800915 std::string certFileBody =
916 getCertificateFromReqBody(asyncResp, req);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200917
George Liu0fda0f12021-11-16 10:06:17 +0800918 if (certFileBody.empty())
919 {
920 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
921 messages::unrecognizedRequestBody(asyncResp->res);
922 return;
923 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700924
George Liu0fda0f12021-11-16 10:06:17 +0800925 std::shared_ptr<CertificateFile> certFile =
926 std::make_shared<CertificateFile>(certFileBody);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700927
George Liu0fda0f12021-11-16 10:06:17 +0800928 crow::connections::systemBus->async_method_call(
929 [asyncResp, certFile](const boost::system::error_code ec,
930 const std::string& objectPath) {
931 if (ec)
932 {
933 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
934 messages::internalError(asyncResp->res);
935 return;
936 }
937 long certId = getIDFromURL(objectPath);
938 if (certId < 0)
939 {
940 BMCWEB_LOG_ERROR << "Invalid objectPath value"
941 << objectPath;
942 messages::internalError(asyncResp->res);
943 return;
944 }
945 std::string certURL =
946 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
947 std::to_string(certId);
948 getCertificateProperties(asyncResp, objectPath,
949 certs::httpsServiceName, certId,
950 certURL, "HTTPS Certificate");
951 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
952 << certFile->getCertFilePath();
953 },
954 certs::httpsServiceName, certs::httpsObjectPath,
955 certs::certInstallIntf, "Install", certFile->getCertFilePath());
956 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700957} // requestRoutesHTTPSCertificateCollection
958
959/**
960 * @brief Retrieve the certificates installed list and append to the
961 * response
962 *
963 * @param[in] asyncResp Shared pointer to the response message
964 * @param[in] certURL Path of the certificate object
965 * @param[in] path Path of the D-Bus service object
966 * @return None
967 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700968inline void
969 getCertificateLocations(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
970 const std::string& certURL, const std::string& path,
971 const std::string& service)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700972{
973 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
974 << " Path=" << path << " service= " << service;
975 crow::connections::systemBus->async_method_call(
976 [asyncResp, certURL](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800977 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700978 if (ec)
979 {
980 BMCWEB_LOG_WARNING
981 << "Certificate collection query failed: " << ec
982 << ", skipping " << certURL;
983 return;
984 }
985 nlohmann::json& links =
986 asyncResp->res.jsonValue["Links"]["Certificates"];
Ed Tanous9eb808c2022-01-25 10:19:23 -0800987 for (const auto& cert : certs)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700988 {
989 long id = getIDFromURL(cert.first.str);
990 if (id >= 0)
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200991 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700992 links.push_back(
993 {{"@odata.id", certURL + std::to_string(id)}});
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200994 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700995 }
996 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
997 links.size();
998 },
999 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
1000}
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001001
1002/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001003 * The certificate location schema defines a resource that an administrator
1004 * can use in order to locate all certificates installed on a given service.
1005 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001006inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001007{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001008 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -07001009 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001010 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1011 const std::shared_ptr<
1012 bmcweb::AsyncResp>&
1013 asyncResp) {
1014 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1015 {
1016 return;
1017 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001018 asyncResp->res.jsonValue = {
1019 {"@odata.id",
1020 "/redfish/v1/CertificateService/CertificateLocations"},
1021 {"@odata.type",
1022 "#CertificateLocations.v1_0_0.CertificateLocations"},
1023 {"Name", "Certificate Locations"},
1024 {"Id", "CertificateLocations"},
1025 {"Description",
1026 "Defines a resource that an administrator can use in order to "
1027 "locate all certificates installed on a given service"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001028
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001029 nlohmann::json& links =
1030 asyncResp->res.jsonValue["Links"]["Certificates"];
1031 links = nlohmann::json::array();
1032 getCertificateLocations(
1033 asyncResp,
1034 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
1035 certs::httpsObjectPath, certs::httpsServiceName);
1036 getCertificateLocations(
1037 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/",
1038 certs::ldapObjectPath, certs::ldapServiceName);
1039 getCertificateLocations(
1040 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1041 certs::authorityObjectPath, certs::authorityServiceName);
1042 });
1043}
1044// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001045
1046/**
1047 * Collection of LDAP certificates
1048 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001049inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001050{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001051 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001052 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001053 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1054 const std::shared_ptr<
1055 bmcweb::AsyncResp>&
1056 asyncResp) {
1057 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1058 {
1059 return;
1060 }
George Liu0fda0f12021-11-16 10:06:17 +08001061 asyncResp->res.jsonValue = {
1062 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1063 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1064 {"Name", "LDAP Certificates Collection"},
1065 {"Description", "A Collection of LDAP certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001066
George Liu0fda0f12021-11-16 10:06:17 +08001067 crow::connections::systemBus->async_method_call(
1068 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001069 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001070 nlohmann::json& members =
1071 asyncResp->res.jsonValue["Members"];
1072 nlohmann::json& count =
1073 asyncResp->res.jsonValue["Members@odata.count"];
1074 members = nlohmann::json::array();
1075 count = 0;
1076 if (ec)
1077 {
1078 BMCWEB_LOG_WARNING << "LDAP certificate query failed: "
1079 << ec;
1080 return;
1081 }
1082 for (const auto& cert : certs)
1083 {
1084 long id = getIDFromURL(cert.first.str);
1085 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001086 {
George Liu0fda0f12021-11-16 10:06:17 +08001087 members.push_back(
1088 {{"@odata.id",
1089 "/redfish/v1/AccountService/LDAP/Certificates/" +
1090 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001091 }
George Liu0fda0f12021-11-16 10:06:17 +08001092 }
1093 count = members.size();
1094 },
1095 certs::ldapServiceName, certs::ldapObjectPath,
1096 certs::dbusObjManagerIntf, "GetManagedObjects");
1097 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001098
1099 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001100 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001101 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001102 [&app](const crow::Request& req,
1103 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1104 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1105 {
1106 return;
1107 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001108 std::string certFileBody =
1109 getCertificateFromReqBody(asyncResp, req);
1110
1111 if (certFileBody.empty())
Marri Devender Rao37cce912019-02-20 01:05:22 -06001112 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001113 BMCWEB_LOG_ERROR
1114 << "Cannot get certificate from request body.";
1115 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001116 return;
1117 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001118
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001119 std::shared_ptr<CertificateFile> certFile =
1120 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001121
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001122 crow::connections::systemBus->async_method_call(
1123 [asyncResp, certFile](const boost::system::error_code ec,
1124 const std::string& objectPath) {
1125 if (ec)
1126 {
1127 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1128 messages::internalError(asyncResp->res);
1129 return;
1130 }
1131 long certId = getIDFromURL(objectPath);
1132 if (certId < 0)
1133 {
1134 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1135 << objectPath;
1136 messages::internalError(asyncResp->res);
1137 return;
1138 }
1139 std::string certURL =
1140 "/redfish/v1/AccountService/LDAP/Certificates/" +
1141 std::to_string(certId);
1142 getCertificateProperties(asyncResp, objectPath,
1143 certs::ldapServiceName, certId,
1144 certURL, "LDAP Certificate");
1145 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1146 << certFile->getCertFilePath();
1147 },
1148 certs::ldapServiceName, certs::ldapObjectPath,
1149 certs::certInstallIntf, "Install",
1150 certFile->getCertFilePath());
1151 });
1152} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001153
1154/**
1155 * Certificate resource describes a certificate used to prove the identity
1156 * of a component, account or service.
1157 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001158inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001159{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001160 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001161 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001162 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001163 [&app](const crow::Request& req,
1164 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1165 const std::string&) {
1166 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1167 {
1168 return;
1169 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001170 long id = getIDFromURL(req.url);
1171 if (id < 0)
1172 {
1173 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1174 messages::internalError(asyncResp->res);
1175 return;
1176 }
1177 BMCWEB_LOG_DEBUG << "LDAP Certificate ID="
1178 << std::to_string(id);
1179 std::string certURL =
1180 "/redfish/v1/AccountService/LDAP/Certificates/" +
1181 std::to_string(id);
1182 std::string objectPath = certs::ldapObjectPath;
1183 objectPath += "/";
1184 objectPath += std::to_string(id);
1185 getCertificateProperties(asyncResp, objectPath,
1186 certs::ldapServiceName, id, certURL,
1187 "LDAP Certificate");
1188 });
1189} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001190/**
1191 * Collection of TrustStoreCertificate certificates
1192 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001193inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001194{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001195 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001196 .privileges(redfish::privileges::getCertificate)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001197 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1198 const std::shared_ptr<
1199 bmcweb::AsyncResp>&
1200 asyncResp) {
1201 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1202 {
1203 return;
1204 }
George Liu0fda0f12021-11-16 10:06:17 +08001205 asyncResp->res.jsonValue = {
1206 {"@odata.id",
1207 "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1208 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1209 {"Name", "TrustStore Certificates Collection"},
1210 {"Description",
1211 "A Collection of TrustStore certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001212
George Liu0fda0f12021-11-16 10:06:17 +08001213 crow::connections::systemBus->async_method_call(
1214 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001215 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001216 if (ec)
1217 {
1218 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1219 messages::internalError(asyncResp->res);
1220 return;
1221 }
1222 nlohmann::json& members =
1223 asyncResp->res.jsonValue["Members"];
1224 members = nlohmann::json::array();
1225 for (const auto& cert : certs)
1226 {
1227 long id = getIDFromURL(cert.first.str);
1228 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001229 {
George Liu0fda0f12021-11-16 10:06:17 +08001230 members.push_back(
1231 {{"@odata.id",
1232 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1233 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001234 }
George Liu0fda0f12021-11-16 10:06:17 +08001235 }
1236 asyncResp->res.jsonValue["Members@odata.count"] =
1237 members.size();
1238 },
1239 certs::authorityServiceName, certs::authorityObjectPath,
1240 certs::dbusObjManagerIntf, "GetManagedObjects");
1241 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001242
1243 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001244 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +08001245 .methods(
1246 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -07001247 post)([&app](
1248 const crow::Request& req,
1249 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1250 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1251 {
1252 return;
1253 }
George Liu0fda0f12021-11-16 10:06:17 +08001254 std::string certFileBody =
1255 getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001256
George Liu0fda0f12021-11-16 10:06:17 +08001257 if (certFileBody.empty())
1258 {
1259 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1260 messages::unrecognizedRequestBody(asyncResp->res);
1261 return;
1262 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001263
George Liu0fda0f12021-11-16 10:06:17 +08001264 std::shared_ptr<CertificateFile> certFile =
1265 std::make_shared<CertificateFile>(certFileBody);
1266 crow::connections::systemBus->async_method_call(
1267 [asyncResp, certFile](const boost::system::error_code ec,
1268 const std::string& objectPath) {
1269 if (ec)
1270 {
1271 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1272 messages::internalError(asyncResp->res);
1273 return;
1274 }
1275 long certId = getIDFromURL(objectPath);
1276 if (certId < 0)
1277 {
1278 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1279 << objectPath;
1280 messages::internalError(asyncResp->res);
1281 return;
1282 }
1283 std::string certURL =
1284 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1285 std::to_string(certId);
zhanghch058d1b46d2021-04-01 11:18:24 +08001286
George Liu0fda0f12021-11-16 10:06:17 +08001287 getCertificateProperties(
1288 asyncResp, objectPath, certs::authorityServiceName,
1289 certId, certURL, "TrustStore Certificate");
1290 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1291 << certFile->getCertFilePath();
1292 },
1293 certs::authorityServiceName, certs::authorityObjectPath,
1294 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1295 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001296} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001297
1298/**
1299 * Certificate resource describes a certificate used to prove the identity
1300 * of a component, account or service.
1301 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001302inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001303{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001304 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001305 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001306 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001307 [&app](const crow::Request& req,
1308 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1309 const std::string&) {
1310 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1311 {
1312 return;
1313 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001314 long id = getIDFromURL(req.url);
1315 if (id < 0)
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001316 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001317 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1318 messages::internalError(asyncResp->res);
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001319 return;
1320 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001321 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1322 << std::to_string(id);
1323 std::string certURL =
1324 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1325 std::to_string(id);
1326 std::string objectPath = certs::authorityObjectPath;
1327 objectPath += "/";
1328 objectPath += std::to_string(id);
1329 getCertificateProperties(asyncResp, objectPath,
1330 certs::authorityServiceName, id,
1331 certURL, "TrustStore Certificate");
1332 });
1333
1334 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001335 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001336 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001337 [&app](const crow::Request& req,
1338 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1339 const std::string& param) {
1340 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1341 {
1342 return;
1343 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001344 if (param.empty())
1345 {
1346 messages::internalError(asyncResp->res);
1347 return;
1348 }
1349
1350 long id = getIDFromURL(req.url);
1351 if (id < 0)
1352 {
1353 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1354 messages::resourceNotFound(asyncResp->res,
1355 "TrustStore Certificate",
1356 std::string(req.url));
1357 return;
1358 }
1359 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1360 << std::to_string(id);
1361 std::string certPath = certs::authorityObjectPath;
1362 certPath += "/";
1363 certPath += std::to_string(id);
1364
1365 crow::connections::systemBus->async_method_call(
1366 [asyncResp, id](const boost::system::error_code ec) {
1367 if (ec)
1368 {
1369 messages::resourceNotFound(asyncResp->res,
1370 "TrustStore Certificate",
1371 std::to_string(id));
1372 return;
1373 }
1374 BMCWEB_LOG_INFO << "Certificate deleted";
1375 asyncResp->res.result(
1376 boost::beast::http::status::no_content);
1377 },
1378 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1379 "Delete");
1380 });
1381} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001382} // namespace redfish