blob: 889f6064cc2f43b796f49524bc139f4d09df660f [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>
Ed Tanous168e20c2021-12-13 14:39:53 -08006#include <dbus_utility.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* httpsObjectPath =
Marri Devender Rao5968cae2019-01-21 10:27:12 -060014 "/xyz/openbmc_project/certs/server/https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050015constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
16constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
17constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
18constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
19constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
20constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
21constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
22constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060023 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050024constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060025 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050027 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050028constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050029 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060030} // namespace certs
31
32/**
33 * The Certificate schema defines a Certificate Service which represents the
34 * actions available to manage certificates and links to where certificates
35 * are installed.
36 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060037
John Edward Broadbent7e860f12021-04-08 15:57:16 -070038// TODO: Issue#61 No entries are available for Certificate
39// service at https://www.dmtf.org/standards/redfish
40// "redfish standard registries". Need to modify after DMTF
41// publish Privilege details for certificate service
42
43inline void requestRoutesCertificateService(App& app)
44{
45 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
Ed Tanoused398212021-06-09 17:05:54 -070046 .privileges(redfish::privileges::getCertificateService)
Abhishek Patel72048782021-06-02 09:53:24 -050047 .methods(
48 boost::beast::http::verb::
49 get)([](const crow::Request& req,
50 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
51 asyncResp->res.jsonValue = {
52 {"@odata.type",
53 "#CertificateService.v1_0_0.CertificateService"},
54 {"@odata.id", "/redfish/v1/CertificateService"},
55 {"Id", "CertificateService"},
56 {"Name", "Certificate Service"},
57 {"Description", "Actions available to manage certificates"}};
58 // /redfish/v1/CertificateService/CertificateLocations is something
59 // only ConfigureManager can access then only display when the user
60 // has permissions ConfigureManager
61 Privileges effectiveUserPrivileges =
62 redfish::getUserPrivileges(req.userRole);
63 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
64 effectiveUserPrivileges))
65 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -070066 asyncResp->res.jsonValue["CertificateLocations"] = {
67 {"@odata.id",
68 "/redfish/v1/CertificateService/CertificateLocations"}};
Abhishek Patel72048782021-06-02 09:53:24 -050069 }
George Liu0fda0f12021-11-16 10:06:17 +080070 asyncResp->res
71 .jsonValue["Actions"]
72 ["#CertificateService.ReplaceCertificate"] = {
73 {"target",
74 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"},
75 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
Abhishek Patel72048782021-06-02 09:53:24 -050076 asyncResp->res
77 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
George Liu0fda0f12021-11-16 10:06:17 +080078 {"target",
79 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR"}};
Abhishek Patel72048782021-06-02 09:53:24 -050080 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070081} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060082
Marri Devender Rao5968cae2019-01-21 10:27:12 -060083/**
84 * @brief Find the ID specified in the URL
85 * Finds the numbers specified after the last "/" in the URL and returns.
86 * @param[in] path URL
87 * @return -1 on failure and number on success
88 */
Ed Tanous23a21a12020-07-25 04:45:05 +000089inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060090{
Ed Tanousf23b7292020-10-15 09:41:17 -070091 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -060092 if (found == std::string::npos)
93 {
94 return -1;
95 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020096
Marri Devender Rao5968cae2019-01-21 10:27:12 -060097 if ((found + 1) < url.length())
98 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -060099 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200100
101 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600102 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200103
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600104 return -1;
105}
106
zhanghch058d1b46d2021-04-01 11:18:24 +0800107inline std::string getCertificateFromReqBody(
108 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
109 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200110{
111 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
112
113 if (reqJson.is_discarded())
114 {
115 // We did not receive JSON request, proceed as it is RAW data
116 return req.body;
117 }
118
119 std::string certificate;
120 std::optional<std::string> certificateType = "PEM";
121
Willy Tu15ed6782021-12-14 11:03:16 -0800122 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
123 certificate, "CertificateType",
124 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200125 {
126 BMCWEB_LOG_ERROR << "Required parameters are missing";
127 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700128 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200129 }
130
131 if (*certificateType != "PEM")
132 {
133 messages::propertyValueNotInList(asyncResp->res, *certificateType,
134 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700135 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200136 }
137
138 return certificate;
139}
140
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600141/**
142 * Class to create a temporary certificate file for uploading to system
143 */
144class CertificateFile
145{
146 public:
147 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500148 CertificateFile(const CertificateFile&) = delete;
149 CertificateFile& operator=(const CertificateFile&) = delete;
150 CertificateFile(CertificateFile&&) = delete;
151 CertificateFile& operator=(CertificateFile&&) = delete;
152 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600153 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700154 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700155 'e', 'r', 't', 's', '.', 'X',
156 'X', 'X', 'X', 'X', 'X', '\0'};
157 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800158 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600159 {
160 certDirectory = tempDirectory;
161 certificateFile = certDirectory / "cert.pem";
162 std::ofstream out(certificateFile, std::ofstream::out |
163 std::ofstream::binary |
164 std::ofstream::trunc);
165 out << certString;
166 out.close();
167 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
168 }
169 }
170 ~CertificateFile()
171 {
172 if (std::filesystem::exists(certDirectory))
173 {
174 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
Ed Tanous23a21a12020-07-25 04:45:05 +0000175 std::error_code ec;
176 std::filesystem::remove_all(certDirectory, ec);
177 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600178 {
179 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
180 << certDirectory;
181 }
182 }
183 }
184 std::string getCertFilePath()
185 {
186 return certificateFile;
187 }
188
189 private:
190 std::filesystem::path certificateFile;
191 std::filesystem::path certDirectory;
192};
193
Marri Devender Rao30215812019-03-18 08:59:21 -0500194static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
195/**
196 * @brief Read data from CSR D-bus object and set to response
197 *
198 * @param[in] asyncResp Shared pointer to the response message
199 * @param[in] certURI Link to certifiate collection URI
200 * @param[in] service D-Bus service name
201 * @param[in] certObjPath certificate D-Bus object path
202 * @param[in] csrObjPath CSR D-Bus object path
203 * @return None
204 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800205static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500206 const std::string& certURI, const std::string& service,
207 const std::string& certObjPath,
208 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500209{
210 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
211 << " CSRObjectPath=" << csrObjPath
212 << " service=" << service;
213 crow::connections::systemBus->async_method_call(
214 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500215 const std::string& csr) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500216 if (ec)
217 {
218 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
219 messages::internalError(asyncResp->res);
220 return;
221 }
222 if (csr.empty())
223 {
224 BMCWEB_LOG_ERROR << "CSR read is empty";
225 messages::internalError(asyncResp->res);
226 return;
227 }
228 asyncResp->res.jsonValue["CSRString"] = csr;
229 asyncResp->res.jsonValue["CertificateCollection"] = {
230 {"@odata.id", certURI}};
231 },
232 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
233}
234
235/**
236 * Action to Generate CSR
237 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700238inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500239{
George Liu0fda0f12021-11-16 10:06:17 +0800240 BMCWEB_ROUTE(
241 app,
242 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
Ed Tanoused398212021-06-09 17:05:54 -0700243 // Incorrect Privilege; Should be ConfigureManager
244 //.privileges(redfish::privileges::postCertificateService)
Ed Tanous432a8902021-06-14 15:28:56 -0700245 .privileges({{"ConfigureComponents"}})
George Liu0fda0f12021-11-16 10:06:17 +0800246 .methods(
247 boost::beast::http::verb::
248 post)([](const crow::Request& req,
249 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
250 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500251
George Liu0fda0f12021-11-16 10:06:17 +0800252 // Required parameters
253 std::string city;
254 std::string commonName;
255 std::string country;
256 std::string organization;
257 std::string organizationalUnit;
258 std::string state;
259 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800260
George Liu0fda0f12021-11-16 10:06:17 +0800261 // Optional parameters
262 std::optional<std::vector<std::string>> optAlternativeNames =
263 std::vector<std::string>();
264 std::optional<std::string> optContactPerson = "";
265 std::optional<std::string> optChallengePassword = "";
266 std::optional<std::string> optEmail = "";
267 std::optional<std::string> optGivenName = "";
268 std::optional<std::string> optInitials = "";
269 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
270 std::optional<std::string> optKeyCurveId = "secp384r1";
271 std::optional<std::string> optKeyPairAlgorithm = "EC";
272 std::optional<std::vector<std::string>> optKeyUsage =
273 std::vector<std::string>();
274 std::optional<std::string> optSurname = "";
275 std::optional<std::string> optUnstructuredName = "";
Willy Tu15ed6782021-12-14 11:03:16 -0800276 if (!json_util::readJsonAction(
George Liu0fda0f12021-11-16 10:06:17 +0800277 req, asyncResp->res, "City", city, "CommonName", commonName,
278 "ContactPerson", optContactPerson, "Country", country,
279 "Organization", organization, "OrganizationalUnit",
280 organizationalUnit, "State", state, "CertificateCollection",
281 certificateCollection, "AlternativeNames",
282 optAlternativeNames, "ChallengePassword",
283 optChallengePassword, "Email", optEmail, "GivenName",
284 optGivenName, "Initials", optInitials, "KeyBitLength",
285 optKeyBitLength, "KeyCurveId", optKeyCurveId,
286 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
287 optKeyUsage, "Surname", optSurname, "UnstructuredName",
288 optUnstructuredName))
289 {
290 return;
291 }
292
293 // bmcweb has no way to store or decode a private key challenge
294 // password, which will likely cause bmcweb to crash on startup
295 // if this is not set on a post so not allowing the user to set
296 // value
Ed Tanous26f69762022-01-25 09:49:11 -0800297 if (!optChallengePassword->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800298 {
299 messages::actionParameterNotSupported(
300 asyncResp->res, "GenerateCSR", "ChallengePassword");
301 return;
302 }
303
304 std::string certURI;
305 if (!redfish::json_util::readJson(certificateCollection,
306 asyncResp->res, "@odata.id",
307 certURI))
308 {
309 return;
310 }
311
312 std::string objectPath;
313 std::string service;
314 if (boost::starts_with(
315 certURI,
316 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
317 {
318 objectPath = certs::httpsObjectPath;
319 service = certs::httpsServiceName;
320 }
321 else if (boost::starts_with(
322 certURI,
323 "/redfish/v1/AccountService/LDAP/Certificates"))
324 {
325 objectPath = certs::ldapObjectPath;
326 service = certs::ldapServiceName;
327 }
328 else
329 {
330 messages::actionParameterNotSupported(
331 asyncResp->res, "CertificateCollection", "GenerateCSR");
332 return;
333 }
334
335 // supporting only EC and RSA algorithm
336 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
337 {
338 messages::actionParameterNotSupported(
339 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
340 return;
341 }
342
343 // supporting only 2048 key bit length for RSA algorithm due to
344 // time consumed in generating private key
345 if (*optKeyPairAlgorithm == "RSA" &&
346 *optKeyBitLength != rsaKeyBitLength)
347 {
348 messages::propertyValueNotInList(
349 asyncResp->res, std::to_string(*optKeyBitLength),
350 "KeyBitLength");
351 return;
352 }
353
354 // validate KeyUsage supporting only 1 type based on URL
355 if (boost::starts_with(
356 certURI,
357 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
358 {
Ed Tanous26f69762022-01-25 09:49:11 -0800359 if (optKeyUsage->empty())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700360 {
George Liu0fda0f12021-11-16 10:06:17 +0800361 optKeyUsage->push_back("ServerAuthentication");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700362 }
George Liu0fda0f12021-11-16 10:06:17 +0800363 else if (optKeyUsage->size() == 1)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700364 {
George Liu0fda0f12021-11-16 10:06:17 +0800365 if ((*optKeyUsage)[0] != "ServerAuthentication")
366 {
367 messages::propertyValueNotInList(
368 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
369 return;
370 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700371 }
372 else
373 {
374 messages::actionParameterNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800375 asyncResp->res, "KeyUsage", "GenerateCSR");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700376 return;
377 }
George Liu0fda0f12021-11-16 10:06:17 +0800378 }
379 else if (boost::starts_with(
380 certURI,
381 "/redfish/v1/AccountService/LDAP/Certificates"))
382 {
Ed Tanous26f69762022-01-25 09:49:11 -0800383 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800384 {
385 optKeyUsage->push_back("ClientAuthentication");
386 }
387 else if (optKeyUsage->size() == 1)
388 {
389 if ((*optKeyUsage)[0] != "ClientAuthentication")
390 {
391 messages::propertyValueNotInList(
392 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
393 return;
394 }
395 }
396 else
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700397 {
398 messages::actionParameterNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800399 asyncResp->res, "KeyUsage", "GenerateCSR");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700400 return;
401 }
George Liu0fda0f12021-11-16 10:06:17 +0800402 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500403
George Liu0fda0f12021-11-16 10:06:17 +0800404 // Only allow one CSR matcher at a time so setting retry
405 // time-out and timer expiry to 10 seconds for now.
406 static const int timeOut = 10;
407 if (csrMatcher)
408 {
409 messages::serviceTemporarilyUnavailable(
410 asyncResp->res, std::to_string(timeOut));
411 return;
412 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500413
George Liu0fda0f12021-11-16 10:06:17 +0800414 // Make this static so it survives outside this method
415 static boost::asio::steady_timer timeout(*req.ioService);
416 timeout.expires_after(std::chrono::seconds(timeOut));
417 timeout.async_wait(
418 [asyncResp](const boost::system::error_code& ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700419 csrMatcher = nullptr;
420 if (ec)
421 {
422 // operation_aborted is expected if timer is canceled
423 // before completion.
424 if (ec != boost::asio::error::operation_aborted)
425 {
426 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
427 }
428 return;
429 }
430 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
431 messages::internalError(asyncResp->res);
432 });
433
George Liu0fda0f12021-11-16 10:06:17 +0800434 // create a matcher to wait on CSR object
435 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
436 std::string match("type='signal',"
437 "interface='org.freedesktop.DBus.ObjectManager',"
438 "path='" +
439 objectPath +
440 "',"
441 "member='InterfacesAdded'");
442 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
443 *crow::connections::systemBus, match,
444 [asyncResp, service, objectPath,
445 certURI](sdbusplus::message::message& m) {
446 timeout.cancel();
447 if (m.is_method_error())
448 {
449 BMCWEB_LOG_ERROR << "Dbus method error!!!";
450 messages::internalError(asyncResp->res);
451 return;
452 }
Ed Tanous168e20c2021-12-13 14:39:53 -0800453 std::vector<std::pair<
454 std::string,
455 std::vector<std::pair<std::string,
456 dbus::utility::DbusVariantType>>>>
George Liu0fda0f12021-11-16 10:06:17 +0800457 interfacesProperties;
458 sdbusplus::message::object_path csrObjectPath;
459 m.read(csrObjectPath, interfacesProperties);
460 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
461 for (auto& interface : interfacesProperties)
462 {
463 if (interface.first == "xyz.openbmc_project.Certs.CSR")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700464 {
George Liu0fda0f12021-11-16 10:06:17 +0800465 getCSR(asyncResp, certURI, service, objectPath,
466 csrObjectPath.str);
467 break;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700468 }
George Liu0fda0f12021-11-16 10:06:17 +0800469 }
470 });
471 crow::connections::systemBus->async_method_call(
Ed Tanous914e2d52022-01-07 11:38:34 -0800472 [asyncResp](const boost::system::error_code ec,
George Liu0fda0f12021-11-16 10:06:17 +0800473 const std::string&) {
474 if (ec)
475 {
476 BMCWEB_LOG_ERROR << "DBUS response error: "
477 << ec.message();
478 messages::internalError(asyncResp->res);
479 return;
480 }
481 },
482 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
483 "GenerateCSR", *optAlternativeNames, *optChallengePassword,
484 city, commonName, *optContactPerson, country, *optEmail,
485 *optGivenName, *optInitials, *optKeyBitLength, *optKeyCurveId,
486 *optKeyPairAlgorithm, *optKeyUsage, organization,
487 organizationalUnit, state, *optSurname, *optUnstructuredName);
488 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700489} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500490
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600491/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500492 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600493 *
494 * @param[in] asyncResp Shared pointer to the response message
495 * @param[in] str Issuer/Subject value in key=value pairs
496 * @param[in] type Issuer/Subject
497 * @return None
498 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500499static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600500 const std::string_view value)
501{
502 // example: O=openbmc-project.xyz,CN=localhost
503 std::string_view::iterator i = value.begin();
504 while (i != value.end())
505 {
506 std::string_view::iterator tokenBegin = i;
507 while (i != value.end() && *i != '=')
508 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530509 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600510 }
511 if (i == value.end())
512 {
513 break;
514 }
Ed Tanous271584a2019-07-09 16:24:22 -0700515 const std::string_view key(tokenBegin,
516 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530517 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600518 tokenBegin = i;
519 while (i != value.end() && *i != ',')
520 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530521 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600522 }
Ed Tanous271584a2019-07-09 16:24:22 -0700523 const std::string_view val(tokenBegin,
524 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600525 if (key == "L")
526 {
527 out["City"] = val;
528 }
529 else if (key == "CN")
530 {
531 out["CommonName"] = val;
532 }
533 else if (key == "C")
534 {
535 out["Country"] = val;
536 }
537 else if (key == "O")
538 {
539 out["Organization"] = val;
540 }
541 else if (key == "OU")
542 {
543 out["OrganizationalUnit"] = val;
544 }
545 else if (key == "ST")
546 {
547 out["State"] = val;
548 }
549 // skip comma character
550 if (i != value.end())
551 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530552 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600553 }
554 }
555}
556
557/**
558 * @brief Retrieve the certificates properties and append to the response
559 * message
560 *
561 * @param[in] asyncResp Shared pointer to the response message
562 * @param[in] objectPath Path of the D-Bus service object
563 * @param[in] certId Id of the certificate
564 * @param[in] certURL URL of the certificate object
565 * @param[in] name name of the certificate
566 * @return None
567 */
568static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800569 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
570 const std::string& objectPath, const std::string& service, long certId,
571 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600572{
Ed Tanous168e20c2021-12-13 14:39:53 -0800573 using PropertiesMap =
574 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600575 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
576 << " certId=" << certId << " certURl=" << certURL;
577 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600578 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500579 const PropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600580 if (ec)
581 {
582 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500583 messages::resourceNotFound(asyncResp->res, name,
584 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600585 return;
586 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600587 asyncResp->res.jsonValue = {
588 {"@odata.id", certURL},
589 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600590 {"Id", std::to_string(certId)},
591 {"Name", name},
592 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500593 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600594 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600595 if (property.first == "CertificateString")
596 {
597 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500598 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600599 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800600 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600601 {
602 asyncResp->res.jsonValue["CertificateString"] = *value;
603 }
604 }
605 else if (property.first == "KeyUsage")
606 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500607 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600608 asyncResp->res.jsonValue["KeyUsage"];
609 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500610 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600611 std::get_if<std::vector<std::string>>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800612 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600613 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500614 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600615 {
616 keyUsage.push_back(usage);
617 }
618 }
619 }
620 else if (property.first == "Issuer")
621 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500622 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600623 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800624 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600625 {
626 updateCertIssuerOrSubject(
627 asyncResp->res.jsonValue["Issuer"], *value);
628 }
629 }
630 else if (property.first == "Subject")
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["Subject"], *value);
638 }
639 }
640 else if (property.first == "ValidNotAfter")
641 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500642 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600643 std::get_if<uint64_t>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800644 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600645 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600646 asyncResp->res.jsonValue["ValidNotAfter"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800647 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600648 }
649 }
650 else if (property.first == "ValidNotBefore")
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["ValidNotBefore"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800657 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600658 }
659 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600660 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600661 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600662 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600663 service, objectPath, certs::dbusPropIntf, "GetAll",
664 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600665}
666
667using GetObjectType =
668 std::vector<std::pair<std::string, std::vector<std::string>>>;
669
670/**
671 * Action to replace an existing certificate
672 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700673inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600674{
George Liu0fda0f12021-11-16 10:06:17 +0800675 BMCWEB_ROUTE(
676 app,
677 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700678 .privileges(redfish::privileges::postCertificateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700679 .methods(
680 boost::beast::http::verb::
681 post)([](const crow::Request& req,
682 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
683 std::string certificate;
684 nlohmann::json certificateUri;
685 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600686
Willy Tu15ed6782021-12-14 11:03:16 -0800687 if (!json_util::readJsonAction(req, asyncResp->res,
688 "CertificateString", certificate,
689 "CertificateUri", certificateUri,
690 "CertificateType", certificateType))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700691 {
692 BMCWEB_LOG_ERROR << "Required parameters are missing";
693 messages::internalError(asyncResp->res);
694 return;
695 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800696
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700697 if (!certificateType)
698 {
699 // should never happen, but it never hurts to be paranoid.
700 return;
701 }
702 if (certificateType != "PEM")
703 {
704 messages::actionParameterNotSupported(
705 asyncResp->res, "CertificateType", "ReplaceCertificate");
706 return;
707 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600708
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700709 std::string certURI;
710 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
711 "@odata.id", certURI))
712 {
713 messages::actionParameterMissing(
714 asyncResp->res, "ReplaceCertificate", "CertificateUri");
715 return;
716 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600717
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700718 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
719 long id = getIDFromURL(certURI);
720 if (id < 0)
721 {
722 messages::actionParameterValueFormatError(
723 asyncResp->res, certURI, "CertificateUri",
724 "ReplaceCertificate");
725 return;
726 }
727 std::string objectPath;
728 std::string name;
729 std::string service;
George Liu0fda0f12021-11-16 10:06:17 +0800730 if (boost::starts_with(
731 certURI,
732 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700733 {
734 objectPath = std::string(certs::httpsObjectPath) + "/" +
735 std::to_string(id);
736 name = "HTTPS certificate";
737 service = certs::httpsServiceName;
738 }
739 else if (boost::starts_with(
740 certURI,
741 "/redfish/v1/AccountService/LDAP/Certificates/"))
742 {
743 objectPath = std::string(certs::ldapObjectPath) + "/" +
744 std::to_string(id);
745 name = "LDAP certificate";
746 service = certs::ldapServiceName;
747 }
748 else if (boost::starts_with(
749 certURI,
750 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
751 {
752 objectPath = std::string(certs::authorityObjectPath) + "/" +
753 std::to_string(id);
754 name = "TrustStore certificate";
755 service = certs::authorityServiceName;
756 }
757 else
758 {
759 messages::actionParameterNotSupported(
760 asyncResp->res, "CertificateUri", "ReplaceCertificate");
761 return;
762 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600763
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700764 std::shared_ptr<CertificateFile> certFile =
765 std::make_shared<CertificateFile>(certificate);
766 crow::connections::systemBus->async_method_call(
767 [asyncResp, certFile, objectPath, service, certURI, id,
768 name](const boost::system::error_code ec) {
769 if (ec)
770 {
771 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
772 messages::resourceNotFound(asyncResp->res, name,
773 std::to_string(id));
774 return;
775 }
776 getCertificateProperties(asyncResp, objectPath, service, id,
777 certURI, name);
778 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
779 << certFile->getCertFilePath();
780 },
781 service, objectPath, certs::certReplaceIntf, "Replace",
782 certFile->getCertFilePath());
783 });
784} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600785
786/**
787 * Certificate resource describes a certificate used to prove the identity
788 * of a component, account or service.
789 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700790
791inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600792{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700793 BMCWEB_ROUTE(
794 app,
795 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700796 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700797 .methods(
798 boost::beast::http::verb::
799 get)([](const crow::Request& req,
800 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
801 const std::string& param) -> void {
802 if (param.empty())
803 {
804 messages::internalError(asyncResp->res);
805 return;
806 }
807 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600808
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700809 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
810 << std::to_string(id);
811 std::string certURL =
812 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
813 std::to_string(id);
814 std::string objectPath = certs::httpsObjectPath;
815 objectPath += "/";
816 objectPath += std::to_string(id);
817 getCertificateProperties(asyncResp, objectPath,
818 certs::httpsServiceName, id, certURL,
819 "HTTPS Certificate");
820 });
821}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600822
823/**
824 * Collection of HTTPS certificates
825 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700826inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600827{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700828 BMCWEB_ROUTE(app,
829 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700830 .privileges(redfish::privileges::getCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700831 .methods(
832 boost::beast::http::verb::
833 get)([](const crow::Request&,
834 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
835 asyncResp->res.jsonValue = {
836 {"@odata.id",
837 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
838 {"@odata.type", "#CertificateCollection.CertificateCollection"},
839 {"Name", "HTTPS Certificates Collection"},
840 {"Description", "A Collection of HTTPS certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +0800841
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700842 crow::connections::systemBus->async_method_call(
843 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800844 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700845 if (ec)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600846 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700847 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
848 messages::internalError(asyncResp->res);
849 return;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600850 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700851 nlohmann::json& members =
852 asyncResp->res.jsonValue["Members"];
853 members = nlohmann::json::array();
854 for (const auto& cert : certs)
855 {
856 long id = getIDFromURL(cert.first.str);
857 if (id >= 0)
858 {
859 members.push_back(
860 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +0800861 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700862 std::to_string(id)}});
863 }
864 }
865 asyncResp->res.jsonValue["Members@odata.count"] =
866 members.size();
867 },
868 certs::httpsServiceName, certs::httpsObjectPath,
869 certs::dbusObjManagerIntf, "GetManagedObjects");
870 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600871
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700872 BMCWEB_ROUTE(app,
873 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700874 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +0800875 .methods(
876 boost::beast::http::verb::
877 post)([](const crow::Request& req,
878 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
879 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
zhanghch058d1b46d2021-04-01 11:18:24 +0800880
George Liu0fda0f12021-11-16 10:06:17 +0800881 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
882 {"Description", "HTTPS Certificate"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600883
George Liu0fda0f12021-11-16 10:06:17 +0800884 std::string certFileBody =
885 getCertificateFromReqBody(asyncResp, req);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200886
George Liu0fda0f12021-11-16 10:06:17 +0800887 if (certFileBody.empty())
888 {
889 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
890 messages::unrecognizedRequestBody(asyncResp->res);
891 return;
892 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700893
George Liu0fda0f12021-11-16 10:06:17 +0800894 std::shared_ptr<CertificateFile> certFile =
895 std::make_shared<CertificateFile>(certFileBody);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700896
George Liu0fda0f12021-11-16 10:06:17 +0800897 crow::connections::systemBus->async_method_call(
898 [asyncResp, certFile](const boost::system::error_code ec,
899 const std::string& objectPath) {
900 if (ec)
901 {
902 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
903 messages::internalError(asyncResp->res);
904 return;
905 }
906 long certId = getIDFromURL(objectPath);
907 if (certId < 0)
908 {
909 BMCWEB_LOG_ERROR << "Invalid objectPath value"
910 << objectPath;
911 messages::internalError(asyncResp->res);
912 return;
913 }
914 std::string certURL =
915 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
916 std::to_string(certId);
917 getCertificateProperties(asyncResp, objectPath,
918 certs::httpsServiceName, certId,
919 certURL, "HTTPS Certificate");
920 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
921 << certFile->getCertFilePath();
922 },
923 certs::httpsServiceName, certs::httpsObjectPath,
924 certs::certInstallIntf, "Install", certFile->getCertFilePath());
925 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700926} // requestRoutesHTTPSCertificateCollection
927
928/**
929 * @brief Retrieve the certificates installed list and append to the
930 * response
931 *
932 * @param[in] asyncResp Shared pointer to the response message
933 * @param[in] certURL Path of the certificate object
934 * @param[in] path Path of the D-Bus service object
935 * @return None
936 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700937inline void
938 getCertificateLocations(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
939 const std::string& certURL, const std::string& path,
940 const std::string& service)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700941{
942 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
943 << " Path=" << path << " service= " << service;
944 crow::connections::systemBus->async_method_call(
945 [asyncResp, certURL](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800946 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700947 if (ec)
948 {
949 BMCWEB_LOG_WARNING
950 << "Certificate collection query failed: " << ec
951 << ", skipping " << certURL;
952 return;
953 }
954 nlohmann::json& links =
955 asyncResp->res.jsonValue["Links"]["Certificates"];
Ed Tanous9eb808c2022-01-25 10:19:23 -0800956 for (const auto& cert : certs)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700957 {
958 long id = getIDFromURL(cert.first.str);
959 if (id >= 0)
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200960 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700961 links.push_back(
962 {{"@odata.id", certURL + std::to_string(id)}});
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200963 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700964 }
965 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
966 links.size();
967 },
968 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
969}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600970
971/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600972 * The certificate location schema defines a resource that an administrator
973 * can use in order to locate all certificates installed on a given service.
974 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700975inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600976{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700977 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700978 .privileges(redfish::privileges::getCertificateLocations)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700979 .methods(
980 boost::beast::http::verb::
981 get)([](const crow::Request&,
982 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
983 asyncResp->res.jsonValue = {
984 {"@odata.id",
985 "/redfish/v1/CertificateService/CertificateLocations"},
986 {"@odata.type",
987 "#CertificateLocations.v1_0_0.CertificateLocations"},
988 {"Name", "Certificate Locations"},
989 {"Id", "CertificateLocations"},
990 {"Description",
991 "Defines a resource that an administrator can use in order to "
992 "locate all certificates installed on a given service"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600993
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700994 nlohmann::json& links =
995 asyncResp->res.jsonValue["Links"]["Certificates"];
996 links = nlohmann::json::array();
997 getCertificateLocations(
998 asyncResp,
999 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
1000 certs::httpsObjectPath, certs::httpsServiceName);
1001 getCertificateLocations(
1002 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/",
1003 certs::ldapObjectPath, certs::ldapServiceName);
1004 getCertificateLocations(
1005 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1006 certs::authorityObjectPath, certs::authorityServiceName);
1007 });
1008}
1009// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001010
1011/**
1012 * Collection of LDAP certificates
1013 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001014inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001015{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001016 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001017 .privileges(redfish::privileges::getCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +08001018 .methods(
1019 boost::beast::http::verb::
1020 get)([](const crow::Request&,
1021 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1022 asyncResp->res.jsonValue = {
1023 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1024 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1025 {"Name", "LDAP Certificates Collection"},
1026 {"Description", "A Collection of LDAP certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001027
George Liu0fda0f12021-11-16 10:06:17 +08001028 crow::connections::systemBus->async_method_call(
1029 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001030 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001031 nlohmann::json& members =
1032 asyncResp->res.jsonValue["Members"];
1033 nlohmann::json& count =
1034 asyncResp->res.jsonValue["Members@odata.count"];
1035 members = nlohmann::json::array();
1036 count = 0;
1037 if (ec)
1038 {
1039 BMCWEB_LOG_WARNING << "LDAP certificate query failed: "
1040 << ec;
1041 return;
1042 }
1043 for (const auto& cert : certs)
1044 {
1045 long id = getIDFromURL(cert.first.str);
1046 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001047 {
George Liu0fda0f12021-11-16 10:06:17 +08001048 members.push_back(
1049 {{"@odata.id",
1050 "/redfish/v1/AccountService/LDAP/Certificates/" +
1051 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001052 }
George Liu0fda0f12021-11-16 10:06:17 +08001053 }
1054 count = members.size();
1055 },
1056 certs::ldapServiceName, certs::ldapObjectPath,
1057 certs::dbusObjManagerIntf, "GetManagedObjects");
1058 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001059
1060 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001061 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001062 .methods(boost::beast::http::verb::post)(
1063 [](const crow::Request& req,
1064 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1065 std::string certFileBody =
1066 getCertificateFromReqBody(asyncResp, req);
1067
1068 if (certFileBody.empty())
Marri Devender Rao37cce912019-02-20 01:05:22 -06001069 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001070 BMCWEB_LOG_ERROR
1071 << "Cannot get certificate from request body.";
1072 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001073 return;
1074 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001075
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001076 std::shared_ptr<CertificateFile> certFile =
1077 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001078
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001079 crow::connections::systemBus->async_method_call(
1080 [asyncResp, certFile](const boost::system::error_code ec,
1081 const std::string& objectPath) {
1082 if (ec)
1083 {
1084 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1085 messages::internalError(asyncResp->res);
1086 return;
1087 }
1088 long certId = getIDFromURL(objectPath);
1089 if (certId < 0)
1090 {
1091 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1092 << objectPath;
1093 messages::internalError(asyncResp->res);
1094 return;
1095 }
1096 std::string certURL =
1097 "/redfish/v1/AccountService/LDAP/Certificates/" +
1098 std::to_string(certId);
1099 getCertificateProperties(asyncResp, objectPath,
1100 certs::ldapServiceName, certId,
1101 certURL, "LDAP Certificate");
1102 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1103 << certFile->getCertFilePath();
1104 },
1105 certs::ldapServiceName, certs::ldapObjectPath,
1106 certs::certInstallIntf, "Install",
1107 certFile->getCertFilePath());
1108 });
1109} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001110
1111/**
1112 * Certificate resource describes a certificate used to prove the identity
1113 * of a component, account or service.
1114 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001115inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001116{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001117 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001118 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001119 .methods(boost::beast::http::verb::get)(
1120 [](const crow::Request& req,
1121 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1122 const std::string&) {
1123 long id = getIDFromURL(req.url);
1124 if (id < 0)
1125 {
1126 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1127 messages::internalError(asyncResp->res);
1128 return;
1129 }
1130 BMCWEB_LOG_DEBUG << "LDAP Certificate ID="
1131 << std::to_string(id);
1132 std::string certURL =
1133 "/redfish/v1/AccountService/LDAP/Certificates/" +
1134 std::to_string(id);
1135 std::string objectPath = certs::ldapObjectPath;
1136 objectPath += "/";
1137 objectPath += std::to_string(id);
1138 getCertificateProperties(asyncResp, objectPath,
1139 certs::ldapServiceName, id, certURL,
1140 "LDAP Certificate");
1141 });
1142} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001143/**
1144 * Collection of TrustStoreCertificate certificates
1145 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001146inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001147{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001148 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001149 .privileges(redfish::privileges::getCertificate)
George Liu0fda0f12021-11-16 10:06:17 +08001150 .methods(
1151 boost::beast::http::verb::
1152 get)([](const crow::Request&,
1153 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1154 asyncResp->res.jsonValue = {
1155 {"@odata.id",
1156 "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1157 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1158 {"Name", "TrustStore Certificates Collection"},
1159 {"Description",
1160 "A Collection of TrustStore certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001161
George Liu0fda0f12021-11-16 10:06:17 +08001162 crow::connections::systemBus->async_method_call(
1163 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001164 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001165 if (ec)
1166 {
1167 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1168 messages::internalError(asyncResp->res);
1169 return;
1170 }
1171 nlohmann::json& members =
1172 asyncResp->res.jsonValue["Members"];
1173 members = nlohmann::json::array();
1174 for (const auto& cert : certs)
1175 {
1176 long id = getIDFromURL(cert.first.str);
1177 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001178 {
George Liu0fda0f12021-11-16 10:06:17 +08001179 members.push_back(
1180 {{"@odata.id",
1181 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1182 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001183 }
George Liu0fda0f12021-11-16 10:06:17 +08001184 }
1185 asyncResp->res.jsonValue["Members@odata.count"] =
1186 members.size();
1187 },
1188 certs::authorityServiceName, certs::authorityObjectPath,
1189 certs::dbusObjManagerIntf, "GetManagedObjects");
1190 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001191
1192 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001193 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +08001194 .methods(
1195 boost::beast::http::verb::
1196 post)([](const crow::Request& req,
1197 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1198 std::string certFileBody =
1199 getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001200
George Liu0fda0f12021-11-16 10:06:17 +08001201 if (certFileBody.empty())
1202 {
1203 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1204 messages::unrecognizedRequestBody(asyncResp->res);
1205 return;
1206 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001207
George Liu0fda0f12021-11-16 10:06:17 +08001208 std::shared_ptr<CertificateFile> certFile =
1209 std::make_shared<CertificateFile>(certFileBody);
1210 crow::connections::systemBus->async_method_call(
1211 [asyncResp, certFile](const boost::system::error_code ec,
1212 const std::string& objectPath) {
1213 if (ec)
1214 {
1215 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1216 messages::internalError(asyncResp->res);
1217 return;
1218 }
1219 long certId = getIDFromURL(objectPath);
1220 if (certId < 0)
1221 {
1222 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1223 << objectPath;
1224 messages::internalError(asyncResp->res);
1225 return;
1226 }
1227 std::string certURL =
1228 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1229 std::to_string(certId);
zhanghch058d1b46d2021-04-01 11:18:24 +08001230
George Liu0fda0f12021-11-16 10:06:17 +08001231 getCertificateProperties(
1232 asyncResp, objectPath, certs::authorityServiceName,
1233 certId, certURL, "TrustStore Certificate");
1234 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1235 << certFile->getCertFilePath();
1236 },
1237 certs::authorityServiceName, certs::authorityObjectPath,
1238 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1239 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001240} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001241
1242/**
1243 * Certificate resource describes a certificate used to prove the identity
1244 * of a component, account or service.
1245 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001246inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001247{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001248 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001249 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001250 .methods(boost::beast::http::verb::get)(
1251 [](const crow::Request& req,
1252 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1253 const std::string&) {
1254 long id = getIDFromURL(req.url);
1255 if (id < 0)
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001256 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001257 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1258 messages::internalError(asyncResp->res);
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001259 return;
1260 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001261 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1262 << std::to_string(id);
1263 std::string certURL =
1264 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1265 std::to_string(id);
1266 std::string objectPath = certs::authorityObjectPath;
1267 objectPath += "/";
1268 objectPath += std::to_string(id);
1269 getCertificateProperties(asyncResp, objectPath,
1270 certs::authorityServiceName, id,
1271 certURL, "TrustStore Certificate");
1272 });
1273
1274 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001275 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001276 .methods(boost::beast::http::verb::delete_)(
1277 [](const crow::Request& req,
1278 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1279 const std::string& param) {
1280 if (param.empty())
1281 {
1282 messages::internalError(asyncResp->res);
1283 return;
1284 }
1285
1286 long id = getIDFromURL(req.url);
1287 if (id < 0)
1288 {
1289 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1290 messages::resourceNotFound(asyncResp->res,
1291 "TrustStore Certificate",
1292 std::string(req.url));
1293 return;
1294 }
1295 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1296 << std::to_string(id);
1297 std::string certPath = certs::authorityObjectPath;
1298 certPath += "/";
1299 certPath += std::to_string(id);
1300
1301 crow::connections::systemBus->async_method_call(
1302 [asyncResp, id](const boost::system::error_code ec) {
1303 if (ec)
1304 {
1305 messages::resourceNotFound(asyncResp->res,
1306 "TrustStore Certificate",
1307 std::to_string(id));
1308 return;
1309 }
1310 BMCWEB_LOG_INFO << "Certificate deleted";
1311 asyncResp->res.result(
1312 boost::beast::http::status::no_content);
1313 },
1314 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1315 "Delete");
1316 });
1317} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001318} // namespace redfish