blob: a316cc5080b10fd980e2173596decef384d5e9b6 [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 Tanoused398212021-06-09 17:05:54 -07006#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05007
Marri Devender Rao5968cae2019-01-21 10:27:12 -06008#include <variant>
9namespace 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 }
70 asyncResp->res.jsonValue["Actions"]
71 ["#CertificateService.ReplaceCertificate"] =
72 {{"target", "/redfish/v1/CertificateService/Actions/"
73 "CertificateService.ReplaceCertificate"},
74 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
75 asyncResp->res
76 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
77 {"target", "/redfish/v1/CertificateService/Actions/"
78 "CertificateService.GenerateCSR"}};
79 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070080} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060081
Marri Devender Rao5968cae2019-01-21 10:27:12 -060082/**
83 * @brief Find the ID specified in the URL
84 * Finds the numbers specified after the last "/" in the URL and returns.
85 * @param[in] path URL
86 * @return -1 on failure and number on success
87 */
Ed Tanous23a21a12020-07-25 04:45:05 +000088inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060089{
Ed Tanousf23b7292020-10-15 09:41:17 -070090 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -060091 if (found == std::string::npos)
92 {
93 return -1;
94 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020095
Marri Devender Rao5968cae2019-01-21 10:27:12 -060096 if ((found + 1) < url.length())
97 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -060098 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020099
100 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600101 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200102
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600103 return -1;
104}
105
zhanghch058d1b46d2021-04-01 11:18:24 +0800106inline std::string getCertificateFromReqBody(
107 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
108 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200109{
110 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
111
112 if (reqJson.is_discarded())
113 {
114 // We did not receive JSON request, proceed as it is RAW data
115 return req.body;
116 }
117
118 std::string certificate;
119 std::optional<std::string> certificateType = "PEM";
120
121 if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
122 certificate, "CertificateType", certificateType))
123 {
124 BMCWEB_LOG_ERROR << "Required parameters are missing";
125 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700126 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200127 }
128
129 if (*certificateType != "PEM")
130 {
131 messages::propertyValueNotInList(asyncResp->res, *certificateType,
132 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700133 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200134 }
135
136 return certificate;
137}
138
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600139/**
140 * Class to create a temporary certificate file for uploading to system
141 */
142class CertificateFile
143{
144 public:
145 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500146 CertificateFile(const CertificateFile&) = delete;
147 CertificateFile& operator=(const CertificateFile&) = delete;
148 CertificateFile(CertificateFile&&) = delete;
149 CertificateFile& operator=(CertificateFile&&) = delete;
150 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600151 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700152 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700153 'e', 'r', 't', 's', '.', 'X',
154 'X', 'X', 'X', 'X', 'X', '\0'};
155 char* tempDirectory = mkdtemp(dirTemplate.data());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600156 if (tempDirectory)
157 {
158 certDirectory = tempDirectory;
159 certificateFile = certDirectory / "cert.pem";
160 std::ofstream out(certificateFile, std::ofstream::out |
161 std::ofstream::binary |
162 std::ofstream::trunc);
163 out << certString;
164 out.close();
165 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
166 }
167 }
168 ~CertificateFile()
169 {
170 if (std::filesystem::exists(certDirectory))
171 {
172 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
Ed Tanous23a21a12020-07-25 04:45:05 +0000173 std::error_code ec;
174 std::filesystem::remove_all(certDirectory, ec);
175 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600176 {
177 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
178 << certDirectory;
179 }
180 }
181 }
182 std::string getCertFilePath()
183 {
184 return certificateFile;
185 }
186
187 private:
188 std::filesystem::path certificateFile;
189 std::filesystem::path certDirectory;
190};
191
Marri Devender Rao30215812019-03-18 08:59:21 -0500192static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
193/**
194 * @brief Read data from CSR D-bus object and set to response
195 *
196 * @param[in] asyncResp Shared pointer to the response message
197 * @param[in] certURI Link to certifiate collection URI
198 * @param[in] service D-Bus service name
199 * @param[in] certObjPath certificate D-Bus object path
200 * @param[in] csrObjPath CSR D-Bus object path
201 * @return None
202 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800203static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500204 const std::string& certURI, const std::string& service,
205 const std::string& certObjPath,
206 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500207{
208 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
209 << " CSRObjectPath=" << csrObjPath
210 << " service=" << service;
211 crow::connections::systemBus->async_method_call(
212 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500213 const std::string& csr) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500214 if (ec)
215 {
216 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
217 messages::internalError(asyncResp->res);
218 return;
219 }
220 if (csr.empty())
221 {
222 BMCWEB_LOG_ERROR << "CSR read is empty";
223 messages::internalError(asyncResp->res);
224 return;
225 }
226 asyncResp->res.jsonValue["CSRString"] = csr;
227 asyncResp->res.jsonValue["CertificateCollection"] = {
228 {"@odata.id", certURI}};
229 },
230 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
231}
232
233/**
234 * Action to Generate CSR
235 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700236inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500237{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700238 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/Actions/"
239 "CertificateService.GenerateCSR/")
Ed Tanoused398212021-06-09 17:05:54 -0700240 // Incorrect Privilege; Should be ConfigureManager
241 //.privileges(redfish::privileges::postCertificateService)
Ed Tanous432a8902021-06-14 15:28:56 -0700242 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700243 .methods(boost::beast::http::verb::post)(
244 [](const crow::Request& req,
245 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
246 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500247
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700248 // Required parameters
249 std::string city;
250 std::string commonName;
251 std::string country;
252 std::string organization;
253 std::string organizationalUnit;
254 std::string state;
255 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800256
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700257 // Optional parameters
258 std::optional<std::vector<std::string>> optAlternativeNames =
259 std::vector<std::string>();
260 std::optional<std::string> optContactPerson = "";
261 std::optional<std::string> optChallengePassword = "";
262 std::optional<std::string> optEmail = "";
263 std::optional<std::string> optGivenName = "";
264 std::optional<std::string> optInitials = "";
265 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
266 std::optional<std::string> optKeyCurveId = "secp384r1";
267 std::optional<std::string> optKeyPairAlgorithm = "EC";
268 std::optional<std::vector<std::string>> optKeyUsage =
269 std::vector<std::string>();
270 std::optional<std::string> optSurname = "";
271 std::optional<std::string> optUnstructuredName = "";
272 if (!json_util::readJson(
273 req, asyncResp->res, "City", city, "CommonName",
274 commonName, "ContactPerson", optContactPerson,
275 "Country", country, "Organization", organization,
276 "OrganizationalUnit", organizationalUnit, "State",
277 state, "CertificateCollection", certificateCollection,
278 "AlternativeNames", optAlternativeNames,
279 "ChallengePassword", optChallengePassword, "Email",
280 optEmail, "GivenName", optGivenName, "Initials",
281 optInitials, "KeyBitLength", optKeyBitLength,
282 "KeyCurveId", optKeyCurveId, "KeyPairAlgorithm",
283 optKeyPairAlgorithm, "KeyUsage", optKeyUsage, "Surname",
284 optSurname, "UnstructuredName", optUnstructuredName))
285 {
286 return;
287 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500288
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700289 // bmcweb has no way to store or decode a private key challenge
290 // password, which will likely cause bmcweb to crash on startup
291 // if this is not set on a post so not allowing the user to set
292 // value
293 if (*optChallengePassword != "")
294 {
295 messages::actionParameterNotSupported(
296 asyncResp->res, "GenerateCSR", "ChallengePassword");
297 return;
298 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500299
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700300 std::string certURI;
301 if (!redfish::json_util::readJson(certificateCollection,
302 asyncResp->res, "@odata.id",
303 certURI))
304 {
305 return;
306 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500307
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700308 std::string objectPath;
309 std::string service;
310 if (boost::starts_with(certURI,
311 "/redfish/v1/Managers/bmc/"
312 "NetworkProtocol/HTTPS/Certificates"))
313 {
314 objectPath = certs::httpsObjectPath;
315 service = certs::httpsServiceName;
316 }
317 else if (boost::starts_with(
318 certURI,
319 "/redfish/v1/AccountService/LDAP/Certificates"))
320 {
321 objectPath = certs::ldapObjectPath;
322 service = certs::ldapServiceName;
323 }
324 else
325 {
326 messages::actionParameterNotSupported(
327 asyncResp->res, "CertificateCollection", "GenerateCSR");
328 return;
329 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500330
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700331 // supporting only EC and RSA algorithm
332 if (*optKeyPairAlgorithm != "EC" &&
333 *optKeyPairAlgorithm != "RSA")
334 {
335 messages::actionParameterNotSupported(
336 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
337 return;
338 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500339
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700340 // supporting only 2048 key bit length for RSA algorithm due to
341 // time consumed in generating private key
342 if (*optKeyPairAlgorithm == "RSA" &&
343 *optKeyBitLength != rsaKeyBitLength)
Marri Devender Rao30215812019-03-18 08:59:21 -0500344 {
345 messages::propertyValueNotInList(
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700346 asyncResp->res, std::to_string(*optKeyBitLength),
347 "KeyBitLength");
Marri Devender Rao30215812019-03-18 08:59:21 -0500348 return;
349 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500350
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700351 // validate KeyUsage supporting only 1 type based on URL
352 if (boost::starts_with(certURI,
353 "/redfish/v1/Managers/bmc/"
354 "NetworkProtocol/HTTPS/Certificates"))
Marri Devender Rao30215812019-03-18 08:59:21 -0500355 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700356 if (optKeyUsage->size() == 0)
Marri Devender Rao30215812019-03-18 08:59:21 -0500357 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700358 optKeyUsage->push_back("ServerAuthentication");
359 }
360 else if (optKeyUsage->size() == 1)
361 {
362 if ((*optKeyUsage)[0] != "ServerAuthentication")
363 {
364 messages::propertyValueNotInList(
365 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
366 return;
367 }
368 }
369 else
370 {
371 messages::actionParameterNotSupported(
372 asyncResp->res, "KeyUsage", "GenerateCSR");
373 return;
Marri Devender Rao30215812019-03-18 08:59:21 -0500374 }
375 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700376 else if (boost::starts_with(
377 certURI,
378 "/redfish/v1/AccountService/LDAP/Certificates"))
Marri Devender Rao30215812019-03-18 08:59:21 -0500379 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700380 if (optKeyUsage->size() == 0)
381 {
382 optKeyUsage->push_back("ClientAuthentication");
383 }
384 else if (optKeyUsage->size() == 1)
385 {
386 if ((*optKeyUsage)[0] != "ClientAuthentication")
387 {
388 messages::propertyValueNotInList(
389 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
390 return;
391 }
392 }
393 else
394 {
395 messages::actionParameterNotSupported(
396 asyncResp->res, "KeyUsage", "GenerateCSR");
397 return;
398 }
399 }
400
401 // Only allow one CSR matcher at a time so setting retry
402 // time-out and timer expiry to 10 seconds for now.
403 static const int timeOut = 10;
404 if (csrMatcher)
405 {
406 messages::serviceTemporarilyUnavailable(
407 asyncResp->res, std::to_string(timeOut));
Marri Devender Rao30215812019-03-18 08:59:21 -0500408 return;
409 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700410
411 // Make this static so it survives outside this method
412 static boost::asio::steady_timer timeout(*req.ioService);
413 timeout.expires_after(std::chrono::seconds(timeOut));
414 timeout.async_wait([asyncResp](
415 const boost::system::error_code& ec) {
416 csrMatcher = nullptr;
417 if (ec)
418 {
419 // operation_aborted is expected if timer is canceled
420 // before completion.
421 if (ec != boost::asio::error::operation_aborted)
422 {
423 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
424 }
425 return;
426 }
427 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
428 messages::internalError(asyncResp->res);
429 });
430
431 // create a matcher to wait on CSR object
432 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
433 std::string match(
434 "type='signal',"
435 "interface='org.freedesktop.DBus.ObjectManager',"
436 "path='" +
437 objectPath +
438 "',"
439 "member='InterfacesAdded'");
440 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
441 *crow::connections::systemBus, match,
442 [asyncResp, service, objectPath,
443 certURI](sdbusplus::message::message& m) {
444 timeout.cancel();
445 if (m.is_method_error())
446 {
447 BMCWEB_LOG_ERROR << "Dbus method error!!!";
448 messages::internalError(asyncResp->res);
449 return;
450 }
451 std::vector<std::pair<
452 std::string,
453 std::vector<std::pair<std::string,
454 std::variant<std::string>>>>>
455 interfacesProperties;
456 sdbusplus::message::object_path csrObjectPath;
457 m.read(csrObjectPath, interfacesProperties);
458 BMCWEB_LOG_DEBUG << "CSR object added"
459 << csrObjectPath.str;
460 for (auto& interface : interfacesProperties)
461 {
462 if (interface.first ==
463 "xyz.openbmc_project.Certs.CSR")
464 {
465 getCSR(asyncResp, certURI, service, objectPath,
466 csrObjectPath.str);
467 break;
468 }
469 }
470 });
471 crow::connections::systemBus->async_method_call(
472 [asyncResp](const boost::system::error_code& ec,
473 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,
486 *optKeyCurveId, *optKeyPairAlgorithm, *optKeyUsage,
487 organization, organizationalUnit, state, *optSurname,
488 *optUnstructuredName);
489 });
490} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500491
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600492/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500493 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600494 *
495 * @param[in] asyncResp Shared pointer to the response message
496 * @param[in] str Issuer/Subject value in key=value pairs
497 * @param[in] type Issuer/Subject
498 * @return None
499 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500500static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600501 const std::string_view value)
502{
503 // example: O=openbmc-project.xyz,CN=localhost
504 std::string_view::iterator i = value.begin();
505 while (i != value.end())
506 {
507 std::string_view::iterator tokenBegin = i;
508 while (i != value.end() && *i != '=')
509 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530510 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600511 }
512 if (i == value.end())
513 {
514 break;
515 }
Ed Tanous271584a2019-07-09 16:24:22 -0700516 const std::string_view key(tokenBegin,
517 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530518 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600519 tokenBegin = i;
520 while (i != value.end() && *i != ',')
521 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530522 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600523 }
Ed Tanous271584a2019-07-09 16:24:22 -0700524 const std::string_view val(tokenBegin,
525 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600526 if (key == "L")
527 {
528 out["City"] = val;
529 }
530 else if (key == "CN")
531 {
532 out["CommonName"] = val;
533 }
534 else if (key == "C")
535 {
536 out["Country"] = val;
537 }
538 else if (key == "O")
539 {
540 out["Organization"] = val;
541 }
542 else if (key == "OU")
543 {
544 out["OrganizationalUnit"] = val;
545 }
546 else if (key == "ST")
547 {
548 out["State"] = val;
549 }
550 // skip comma character
551 if (i != value.end())
552 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530553 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600554 }
555 }
556}
557
558/**
559 * @brief Retrieve the certificates properties and append to the response
560 * message
561 *
562 * @param[in] asyncResp Shared pointer to the response message
563 * @param[in] objectPath Path of the D-Bus service object
564 * @param[in] certId Id of the certificate
565 * @param[in] certURL URL of the certificate object
566 * @param[in] name name of the certificate
567 * @return None
568 */
569static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800570 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
571 const std::string& objectPath, const std::string& service, long certId,
572 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600573{
574 using PropertyType =
575 std::variant<std::string, uint64_t, std::vector<std::string>>;
576 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
577 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
578 << " certId=" << certId << " certURl=" << certURL;
579 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600580 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500581 const PropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600582 if (ec)
583 {
584 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500585 messages::resourceNotFound(asyncResp->res, name,
586 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600587 return;
588 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600589 asyncResp->res.jsonValue = {
590 {"@odata.id", certURL},
591 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600592 {"Id", std::to_string(certId)},
593 {"Name", name},
594 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500595 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600596 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600597 if (property.first == "CertificateString")
598 {
599 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500600 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600601 std::get_if<std::string>(&property.second);
602 if (value)
603 {
604 asyncResp->res.jsonValue["CertificateString"] = *value;
605 }
606 }
607 else if (property.first == "KeyUsage")
608 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500609 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600610 asyncResp->res.jsonValue["KeyUsage"];
611 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500612 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600613 std::get_if<std::vector<std::string>>(&property.second);
614 if (value)
615 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500616 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600617 {
618 keyUsage.push_back(usage);
619 }
620 }
621 }
622 else if (property.first == "Issuer")
623 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500624 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600625 std::get_if<std::string>(&property.second);
626 if (value)
627 {
628 updateCertIssuerOrSubject(
629 asyncResp->res.jsonValue["Issuer"], *value);
630 }
631 }
632 else if (property.first == "Subject")
633 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500634 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600635 std::get_if<std::string>(&property.second);
636 if (value)
637 {
638 updateCertIssuerOrSubject(
639 asyncResp->res.jsonValue["Subject"], *value);
640 }
641 }
642 else if (property.first == "ValidNotAfter")
643 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500644 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600645 std::get_if<uint64_t>(&property.second);
646 if (value)
647 {
648 std::time_t time = static_cast<std::time_t>(*value);
649 asyncResp->res.jsonValue["ValidNotAfter"] =
650 crow::utility::getDateTime(time);
651 }
652 }
653 else if (property.first == "ValidNotBefore")
654 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500655 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600656 std::get_if<uint64_t>(&property.second);
657 if (value)
658 {
659 std::time_t time = static_cast<std::time_t>(*value);
660 asyncResp->res.jsonValue["ValidNotBefore"] =
661 crow::utility::getDateTime(time);
662 }
663 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600664 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600665 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600666 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600667 service, objectPath, certs::dbusPropIntf, "GetAll",
668 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600669}
670
671using GetObjectType =
672 std::vector<std::pair<std::string, std::vector<std::string>>>;
673
674/**
675 * Action to replace an existing certificate
676 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700677inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600678{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700679 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/Actions/"
680 "CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700681 .privileges(redfish::privileges::postCertificateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700682 .methods(
683 boost::beast::http::verb::
684 post)([](const crow::Request& req,
685 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
686 std::string certificate;
687 nlohmann::json certificateUri;
688 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600689
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700690 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
691 certificate, "CertificateUri",
692 certificateUri, "CertificateType",
693 certificateType))
694 {
695 BMCWEB_LOG_ERROR << "Required parameters are missing";
696 messages::internalError(asyncResp->res);
697 return;
698 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800699
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700700 if (!certificateType)
701 {
702 // should never happen, but it never hurts to be paranoid.
703 return;
704 }
705 if (certificateType != "PEM")
706 {
707 messages::actionParameterNotSupported(
708 asyncResp->res, "CertificateType", "ReplaceCertificate");
709 return;
710 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600711
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700712 std::string certURI;
713 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
714 "@odata.id", certURI))
715 {
716 messages::actionParameterMissing(
717 asyncResp->res, "ReplaceCertificate", "CertificateUri");
718 return;
719 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600720
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700721 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
722 long id = getIDFromURL(certURI);
723 if (id < 0)
724 {
725 messages::actionParameterValueFormatError(
726 asyncResp->res, certURI, "CertificateUri",
727 "ReplaceCertificate");
728 return;
729 }
730 std::string objectPath;
731 std::string name;
732 std::string service;
733 if (boost::starts_with(certURI,
734 "/redfish/v1/Managers/bmc/NetworkProtocol/"
735 "HTTPS/Certificates/"))
736 {
737 objectPath = std::string(certs::httpsObjectPath) + "/" +
738 std::to_string(id);
739 name = "HTTPS certificate";
740 service = certs::httpsServiceName;
741 }
742 else if (boost::starts_with(
743 certURI,
744 "/redfish/v1/AccountService/LDAP/Certificates/"))
745 {
746 objectPath = std::string(certs::ldapObjectPath) + "/" +
747 std::to_string(id);
748 name = "LDAP certificate";
749 service = certs::ldapServiceName;
750 }
751 else if (boost::starts_with(
752 certURI,
753 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
754 {
755 objectPath = std::string(certs::authorityObjectPath) + "/" +
756 std::to_string(id);
757 name = "TrustStore certificate";
758 service = certs::authorityServiceName;
759 }
760 else
761 {
762 messages::actionParameterNotSupported(
763 asyncResp->res, "CertificateUri", "ReplaceCertificate");
764 return;
765 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600766
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700767 std::shared_ptr<CertificateFile> certFile =
768 std::make_shared<CertificateFile>(certificate);
769 crow::connections::systemBus->async_method_call(
770 [asyncResp, certFile, objectPath, service, certURI, id,
771 name](const boost::system::error_code ec) {
772 if (ec)
773 {
774 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
775 messages::resourceNotFound(asyncResp->res, name,
776 std::to_string(id));
777 return;
778 }
779 getCertificateProperties(asyncResp, objectPath, service, id,
780 certURI, name);
781 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
782 << certFile->getCertFilePath();
783 },
784 service, objectPath, certs::certReplaceIntf, "Replace",
785 certFile->getCertFilePath());
786 });
787} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600788
789/**
790 * Certificate resource describes a certificate used to prove the identity
791 * of a component, account or service.
792 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700793
794inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600795{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700796 BMCWEB_ROUTE(
797 app,
798 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700799 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700800 .methods(
801 boost::beast::http::verb::
802 get)([](const crow::Request& req,
803 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
804 const std::string& param) -> void {
805 if (param.empty())
806 {
807 messages::internalError(asyncResp->res);
808 return;
809 }
810 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600811
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700812 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
813 << std::to_string(id);
814 std::string certURL =
815 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
816 std::to_string(id);
817 std::string objectPath = certs::httpsObjectPath;
818 objectPath += "/";
819 objectPath += std::to_string(id);
820 getCertificateProperties(asyncResp, objectPath,
821 certs::httpsServiceName, id, certURL,
822 "HTTPS Certificate");
823 });
824}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600825
826/**
827 * Collection of HTTPS certificates
828 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700829inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600830{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700831 BMCWEB_ROUTE(app,
832 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700833 .privileges(redfish::privileges::getCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700834 .methods(
835 boost::beast::http::verb::
836 get)([](const crow::Request&,
837 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
838 asyncResp->res.jsonValue = {
839 {"@odata.id",
840 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
841 {"@odata.type", "#CertificateCollection.CertificateCollection"},
842 {"Name", "HTTPS Certificates Collection"},
843 {"Description", "A Collection of HTTPS certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +0800844
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700845 crow::connections::systemBus->async_method_call(
846 [asyncResp](const boost::system::error_code ec,
847 const ManagedObjectType& certs) {
848 if (ec)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600849 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700850 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
851 messages::internalError(asyncResp->res);
852 return;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600853 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700854 nlohmann::json& members =
855 asyncResp->res.jsonValue["Members"];
856 members = nlohmann::json::array();
857 for (const auto& cert : certs)
858 {
859 long id = getIDFromURL(cert.first.str);
860 if (id >= 0)
861 {
862 members.push_back(
863 {{"@odata.id",
864 "/redfish/v1/Managers/bmc/"
865 "NetworkProtocol/HTTPS/Certificates/" +
866 std::to_string(id)}});
867 }
868 }
869 asyncResp->res.jsonValue["Members@odata.count"] =
870 members.size();
871 },
872 certs::httpsServiceName, certs::httpsObjectPath,
873 certs::dbusObjManagerIntf, "GetManagedObjects");
874 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600875
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700876 BMCWEB_ROUTE(app,
877 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700878 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700879 .methods(boost::beast::http::verb::post)(
880 [](const crow::Request& req,
881 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
882 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
zhanghch058d1b46d2021-04-01 11:18:24 +0800883
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700884 asyncResp->res.jsonValue = {
885 {"Name", "HTTPS Certificate"},
886 {"Description", "HTTPS Certificate"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600887
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700888 std::string certFileBody =
889 getCertificateFromReqBody(asyncResp, req);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200890
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700891 if (certFileBody.empty())
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600892 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700893 BMCWEB_LOG_ERROR
894 << "Cannot get certificate from request body.";
895 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600896 return;
897 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700898
899 std::shared_ptr<CertificateFile> certFile =
900 std::make_shared<CertificateFile>(certFileBody);
901
902 crow::connections::systemBus->async_method_call(
903 [asyncResp, certFile](const boost::system::error_code ec,
904 const std::string& objectPath) {
905 if (ec)
906 {
907 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
908 messages::internalError(asyncResp->res);
909 return;
910 }
911 long certId = getIDFromURL(objectPath);
912 if (certId < 0)
913 {
914 BMCWEB_LOG_ERROR << "Invalid objectPath value"
915 << objectPath;
916 messages::internalError(asyncResp->res);
917 return;
918 }
919 std::string certURL =
920 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
921 "Certificates/" +
922 std::to_string(certId);
923 getCertificateProperties(
924 asyncResp, objectPath, certs::httpsServiceName,
925 certId, certURL, "HTTPS Certificate");
926 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
927 << certFile->getCertFilePath();
928 },
929 certs::httpsServiceName, certs::httpsObjectPath,
930 certs::certInstallIntf, "Install",
931 certFile->getCertFilePath());
932 });
933} // requestRoutesHTTPSCertificateCollection
934
935/**
936 * @brief Retrieve the certificates installed list and append to the
937 * response
938 *
939 * @param[in] asyncResp Shared pointer to the response message
940 * @param[in] certURL Path of the certificate object
941 * @param[in] path Path of the D-Bus service object
942 * @return None
943 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700944inline void
945 getCertificateLocations(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
946 const std::string& certURL, const std::string& path,
947 const std::string& service)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700948{
949 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
950 << " Path=" << path << " service= " << service;
951 crow::connections::systemBus->async_method_call(
952 [asyncResp, certURL](const boost::system::error_code ec,
953 const ManagedObjectType& certs) {
954 if (ec)
955 {
956 BMCWEB_LOG_WARNING
957 << "Certificate collection query failed: " << ec
958 << ", skipping " << certURL;
959 return;
960 }
961 nlohmann::json& links =
962 asyncResp->res.jsonValue["Links"]["Certificates"];
963 for (auto& cert : certs)
964 {
965 long id = getIDFromURL(cert.first.str);
966 if (id >= 0)
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200967 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700968 links.push_back(
969 {{"@odata.id", certURL + std::to_string(id)}});
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200970 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700971 }
972 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
973 links.size();
974 },
975 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
976}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600977
978/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600979 * The certificate location schema defines a resource that an administrator
980 * can use in order to locate all certificates installed on a given service.
981 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700982inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600983{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700984 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700985 .privileges(redfish::privileges::getCertificateLocations)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700986 .methods(
987 boost::beast::http::verb::
988 get)([](const crow::Request&,
989 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
990 asyncResp->res.jsonValue = {
991 {"@odata.id",
992 "/redfish/v1/CertificateService/CertificateLocations"},
993 {"@odata.type",
994 "#CertificateLocations.v1_0_0.CertificateLocations"},
995 {"Name", "Certificate Locations"},
996 {"Id", "CertificateLocations"},
997 {"Description",
998 "Defines a resource that an administrator can use in order to "
999 "locate all certificates installed on a given service"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001000
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001001 nlohmann::json& links =
1002 asyncResp->res.jsonValue["Links"]["Certificates"];
1003 links = nlohmann::json::array();
1004 getCertificateLocations(
1005 asyncResp,
1006 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
1007 certs::httpsObjectPath, certs::httpsServiceName);
1008 getCertificateLocations(
1009 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/",
1010 certs::ldapObjectPath, certs::ldapServiceName);
1011 getCertificateLocations(
1012 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1013 certs::authorityObjectPath, certs::authorityServiceName);
1014 });
1015}
1016// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001017
1018/**
1019 * Collection of LDAP certificates
1020 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001021inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001022{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001023 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001024 .privileges(redfish::privileges::getCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001025 .methods(boost::beast::http::verb::get)(
1026 [](const crow::Request&,
1027 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1028 asyncResp->res.jsonValue = {
1029 {"@odata.id",
1030 "/redfish/v1/AccountService/LDAP/Certificates"},
1031 {"@odata.type",
1032 "#CertificateCollection.CertificateCollection"},
1033 {"Name", "LDAP Certificates Collection"},
1034 {"Description",
1035 "A Collection of LDAP certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001036
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001037 crow::connections::systemBus->async_method_call(
1038 [asyncResp](const boost::system::error_code ec,
1039 const ManagedObjectType& certs) {
1040 nlohmann::json& members =
1041 asyncResp->res.jsonValue["Members"];
1042 nlohmann::json& count =
1043 asyncResp->res.jsonValue["Members@odata.count"];
1044 members = nlohmann::json::array();
1045 count = 0;
1046 if (ec)
1047 {
1048 BMCWEB_LOG_WARNING
1049 << "LDAP certificate query failed: " << ec;
1050 return;
1051 }
1052 for (const auto& cert : certs)
1053 {
1054 long id = getIDFromURL(cert.first.str);
1055 if (id >= 0)
1056 {
1057 members.push_back(
1058 {{"@odata.id", "/redfish/v1/AccountService/"
1059 "LDAP/Certificates/" +
1060 std::to_string(id)}});
1061 }
1062 }
1063 count = members.size();
1064 },
1065 certs::ldapServiceName, certs::ldapObjectPath,
1066 certs::dbusObjManagerIntf, "GetManagedObjects");
1067 });
1068
1069 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001070 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001071 .methods(boost::beast::http::verb::post)(
1072 [](const crow::Request& req,
1073 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1074 std::string certFileBody =
1075 getCertificateFromReqBody(asyncResp, req);
1076
1077 if (certFileBody.empty())
Marri Devender Rao37cce912019-02-20 01:05:22 -06001078 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001079 BMCWEB_LOG_ERROR
1080 << "Cannot get certificate from request body.";
1081 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001082 return;
1083 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001084
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001085 std::shared_ptr<CertificateFile> certFile =
1086 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001087
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001088 crow::connections::systemBus->async_method_call(
1089 [asyncResp, certFile](const boost::system::error_code ec,
1090 const std::string& objectPath) {
1091 if (ec)
1092 {
1093 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1094 messages::internalError(asyncResp->res);
1095 return;
1096 }
1097 long certId = getIDFromURL(objectPath);
1098 if (certId < 0)
1099 {
1100 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1101 << objectPath;
1102 messages::internalError(asyncResp->res);
1103 return;
1104 }
1105 std::string certURL =
1106 "/redfish/v1/AccountService/LDAP/Certificates/" +
1107 std::to_string(certId);
1108 getCertificateProperties(asyncResp, objectPath,
1109 certs::ldapServiceName, certId,
1110 certURL, "LDAP Certificate");
1111 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1112 << certFile->getCertFilePath();
1113 },
1114 certs::ldapServiceName, certs::ldapObjectPath,
1115 certs::certInstallIntf, "Install",
1116 certFile->getCertFilePath());
1117 });
1118} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001119
1120/**
1121 * Certificate resource describes a certificate used to prove the identity
1122 * of a component, account or service.
1123 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001124inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001125{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001126 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001127 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001128 .methods(boost::beast::http::verb::get)(
1129 [](const crow::Request& req,
1130 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1131 const std::string&) {
1132 long id = getIDFromURL(req.url);
1133 if (id < 0)
1134 {
1135 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1136 messages::internalError(asyncResp->res);
1137 return;
1138 }
1139 BMCWEB_LOG_DEBUG << "LDAP Certificate ID="
1140 << std::to_string(id);
1141 std::string certURL =
1142 "/redfish/v1/AccountService/LDAP/Certificates/" +
1143 std::to_string(id);
1144 std::string objectPath = certs::ldapObjectPath;
1145 objectPath += "/";
1146 objectPath += std::to_string(id);
1147 getCertificateProperties(asyncResp, objectPath,
1148 certs::ldapServiceName, id, certURL,
1149 "LDAP Certificate");
1150 });
1151} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001152/**
1153 * Collection of TrustStoreCertificate certificates
1154 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001155inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001156{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001157 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001158 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001159 .methods(boost::beast::http::verb::get)(
1160 [](const crow::Request&,
1161 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1162 asyncResp->res.jsonValue = {
1163 {"@odata.id",
1164 "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1165 {"@odata.type",
1166 "#CertificateCollection.CertificateCollection"},
1167 {"Name", "TrustStore Certificates Collection"},
1168 {"Description",
1169 "A Collection of TrustStore certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001170
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001171 crow::connections::systemBus->async_method_call(
1172 [asyncResp](const boost::system::error_code ec,
1173 const ManagedObjectType& certs) {
1174 if (ec)
1175 {
1176 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1177 messages::internalError(asyncResp->res);
1178 return;
1179 }
1180 nlohmann::json& members =
1181 asyncResp->res.jsonValue["Members"];
1182 members = nlohmann::json::array();
1183 for (const auto& cert : certs)
1184 {
1185 long id = getIDFromURL(cert.first.str);
1186 if (id >= 0)
1187 {
1188 members.push_back(
1189 {{"@odata.id", "/redfish/v1/Managers/bmc/"
1190 "Truststore/Certificates/" +
1191 std::to_string(id)}});
1192 }
1193 }
1194 asyncResp->res.jsonValue["Members@odata.count"] =
1195 members.size();
1196 },
1197 certs::authorityServiceName, certs::authorityObjectPath,
1198 certs::dbusObjManagerIntf, "GetManagedObjects");
1199 });
1200
1201 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001202 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001203 .methods(boost::beast::http::verb::post)(
1204 [](const crow::Request& req,
1205 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1206 std::string certFileBody =
1207 getCertificateFromReqBody(asyncResp, req);
1208
1209 if (certFileBody.empty())
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001210 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001211 BMCWEB_LOG_ERROR
1212 << "Cannot get certificate from request body.";
1213 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001214 return;
1215 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001216
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001217 std::shared_ptr<CertificateFile> certFile =
1218 std::make_shared<CertificateFile>(certFileBody);
1219 crow::connections::systemBus->async_method_call(
1220 [asyncResp, certFile](const boost::system::error_code ec,
1221 const std::string& objectPath) {
1222 if (ec)
1223 {
1224 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1225 messages::internalError(asyncResp->res);
1226 return;
1227 }
1228 long certId = getIDFromURL(objectPath);
1229 if (certId < 0)
1230 {
1231 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1232 << objectPath;
1233 messages::internalError(asyncResp->res);
1234 return;
1235 }
1236 std::string certURL = "/redfish/v1/Managers/bmc/"
1237 "Truststore/Certificates/" +
1238 std::to_string(certId);
zhanghch058d1b46d2021-04-01 11:18:24 +08001239
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001240 getCertificateProperties(
1241 asyncResp, objectPath, certs::authorityServiceName,
1242 certId, certURL, "TrustStore Certificate");
1243 BMCWEB_LOG_DEBUG
1244 << "TrustStore certificate install file="
1245 << certFile->getCertFilePath();
1246 },
1247 certs::authorityServiceName, certs::authorityObjectPath,
1248 certs::certInstallIntf, "Install",
1249 certFile->getCertFilePath());
1250 });
1251} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001252
1253/**
1254 * Certificate resource describes a certificate used to prove the identity
1255 * of a component, account or service.
1256 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001257inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001258{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001259 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001260 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001261 .methods(boost::beast::http::verb::get)(
1262 [](const crow::Request& req,
1263 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1264 const std::string&) {
1265 long id = getIDFromURL(req.url);
1266 if (id < 0)
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001267 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001268 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1269 messages::internalError(asyncResp->res);
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001270 return;
1271 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001272 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1273 << std::to_string(id);
1274 std::string certURL =
1275 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1276 std::to_string(id);
1277 std::string objectPath = certs::authorityObjectPath;
1278 objectPath += "/";
1279 objectPath += std::to_string(id);
1280 getCertificateProperties(asyncResp, objectPath,
1281 certs::authorityServiceName, id,
1282 certURL, "TrustStore Certificate");
1283 });
1284
1285 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001286 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001287 .methods(boost::beast::http::verb::delete_)(
1288 [](const crow::Request& req,
1289 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1290 const std::string& param) {
1291 if (param.empty())
1292 {
1293 messages::internalError(asyncResp->res);
1294 return;
1295 }
1296
1297 long id = getIDFromURL(req.url);
1298 if (id < 0)
1299 {
1300 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1301 messages::resourceNotFound(asyncResp->res,
1302 "TrustStore Certificate",
1303 std::string(req.url));
1304 return;
1305 }
1306 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1307 << std::to_string(id);
1308 std::string certPath = certs::authorityObjectPath;
1309 certPath += "/";
1310 certPath += std::to_string(id);
1311
1312 crow::connections::systemBus->async_method_call(
1313 [asyncResp, id](const boost::system::error_code ec) {
1314 if (ec)
1315 {
1316 messages::resourceNotFound(asyncResp->res,
1317 "TrustStore Certificate",
1318 std::to_string(id));
1319 return;
1320 }
1321 BMCWEB_LOG_INFO << "Certificate deleted";
1322 asyncResp->res.result(
1323 boost::beast::http::status::no_content);
1324 },
1325 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1326 "Delete");
1327 });
1328} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001329} // namespace redfish