blob: 0e26a86bdbf39e34a583d3b55d3910d863ae4785 [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>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05006
Marri Devender Rao5968cae2019-01-21 10:27:12 -06007#include <variant>
8namespace redfish
9{
10namespace certs
11{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050012constexpr char const* httpsObjectPath =
Marri Devender Rao5968cae2019-01-21 10:27:12 -060013 "/xyz/openbmc_project/certs/server/https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050014constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
15constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
16constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
17constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
18constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
19constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
20constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
21constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060022 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050023constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060024 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050025constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050026 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050027constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050028 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060029} // namespace certs
30
31/**
32 * The Certificate schema defines a Certificate Service which represents the
33 * actions available to manage certificates and links to where certificates
34 * are installed.
35 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060036
John Edward Broadbent7e860f12021-04-08 15:57:16 -070037// TODO: Issue#61 No entries are available for Certificate
38// service at https://www.dmtf.org/standards/redfish
39// "redfish standard registries". Need to modify after DMTF
40// publish Privilege details for certificate service
41
42inline void requestRoutesCertificateService(App& app)
43{
44 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
45 .privileges({"Login"})
46 .methods(boost::beast::http::verb::get)(
47 [](const crow::Request&,
48 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
49 asyncResp->res.jsonValue = {
50 {"@odata.type",
51 "#CertificateService.v1_0_0.CertificateService"},
52 {"@odata.id", "/redfish/v1/CertificateService"},
53 {"Id", "CertificateService"},
54 {"Name", "Certificate Service"},
55 {"Description",
56 "Actions available to manage certificates"}};
57 asyncResp->res.jsonValue["CertificateLocations"] = {
58 {"@odata.id",
59 "/redfish/v1/CertificateService/CertificateLocations"}};
60 asyncResp->res
61 .jsonValue["Actions"]
62 ["#CertificateService.ReplaceCertificate"] = {
63 {"target", "/redfish/v1/CertificateService/Actions/"
64 "CertificateService.ReplaceCertificate"},
65 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
66 asyncResp->res
67 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
68 {"target", "/redfish/v1/CertificateService/Actions/"
69 "CertificateService.GenerateCSR"}};
70 });
71} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060072
Marri Devender Rao5968cae2019-01-21 10:27:12 -060073/**
74 * @brief Find the ID specified in the URL
75 * Finds the numbers specified after the last "/" in the URL and returns.
76 * @param[in] path URL
77 * @return -1 on failure and number on success
78 */
Ed Tanous23a21a12020-07-25 04:45:05 +000079inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060080{
Ed Tanousf23b7292020-10-15 09:41:17 -070081 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -060082 if (found == std::string::npos)
83 {
84 return -1;
85 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020086
Marri Devender Rao5968cae2019-01-21 10:27:12 -060087 if ((found + 1) < url.length())
88 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -060089 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020090
91 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -060092 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020093
Marri Devender Rao5968cae2019-01-21 10:27:12 -060094 return -1;
95}
96
zhanghch058d1b46d2021-04-01 11:18:24 +080097inline std::string getCertificateFromReqBody(
98 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
99 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200100{
101 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
102
103 if (reqJson.is_discarded())
104 {
105 // We did not receive JSON request, proceed as it is RAW data
106 return req.body;
107 }
108
109 std::string certificate;
110 std::optional<std::string> certificateType = "PEM";
111
112 if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
113 certificate, "CertificateType", certificateType))
114 {
115 BMCWEB_LOG_ERROR << "Required parameters are missing";
116 messages::internalError(asyncResp->res);
117 return std::string();
118 }
119
120 if (*certificateType != "PEM")
121 {
122 messages::propertyValueNotInList(asyncResp->res, *certificateType,
123 "CertificateType");
124 return std::string();
125 }
126
127 return certificate;
128}
129
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600130/**
131 * Class to create a temporary certificate file for uploading to system
132 */
133class CertificateFile
134{
135 public:
136 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500137 CertificateFile(const CertificateFile&) = delete;
138 CertificateFile& operator=(const CertificateFile&) = delete;
139 CertificateFile(CertificateFile&&) = delete;
140 CertificateFile& operator=(CertificateFile&&) = delete;
141 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600142 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700143 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700144 'e', 'r', 't', 's', '.', 'X',
145 'X', 'X', 'X', 'X', 'X', '\0'};
146 char* tempDirectory = mkdtemp(dirTemplate.data());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600147 if (tempDirectory)
148 {
149 certDirectory = tempDirectory;
150 certificateFile = certDirectory / "cert.pem";
151 std::ofstream out(certificateFile, std::ofstream::out |
152 std::ofstream::binary |
153 std::ofstream::trunc);
154 out << certString;
155 out.close();
156 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
157 }
158 }
159 ~CertificateFile()
160 {
161 if (std::filesystem::exists(certDirectory))
162 {
163 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
Ed Tanous23a21a12020-07-25 04:45:05 +0000164 std::error_code ec;
165 std::filesystem::remove_all(certDirectory, ec);
166 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600167 {
168 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
169 << certDirectory;
170 }
171 }
172 }
173 std::string getCertFilePath()
174 {
175 return certificateFile;
176 }
177
178 private:
179 std::filesystem::path certificateFile;
180 std::filesystem::path certDirectory;
181};
182
Marri Devender Rao30215812019-03-18 08:59:21 -0500183static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
184/**
185 * @brief Read data from CSR D-bus object and set to response
186 *
187 * @param[in] asyncResp Shared pointer to the response message
188 * @param[in] certURI Link to certifiate collection URI
189 * @param[in] service D-Bus service name
190 * @param[in] certObjPath certificate D-Bus object path
191 * @param[in] csrObjPath CSR D-Bus object path
192 * @return None
193 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800194static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500195 const std::string& certURI, const std::string& service,
196 const std::string& certObjPath,
197 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500198{
199 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
200 << " CSRObjectPath=" << csrObjPath
201 << " service=" << service;
202 crow::connections::systemBus->async_method_call(
203 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500204 const std::string& csr) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500205 if (ec)
206 {
207 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
208 messages::internalError(asyncResp->res);
209 return;
210 }
211 if (csr.empty())
212 {
213 BMCWEB_LOG_ERROR << "CSR read is empty";
214 messages::internalError(asyncResp->res);
215 return;
216 }
217 asyncResp->res.jsonValue["CSRString"] = csr;
218 asyncResp->res.jsonValue["CertificateCollection"] = {
219 {"@odata.id", certURI}};
220 },
221 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
222}
223
224/**
225 * Action to Generate CSR
226 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700227inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500228{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700229 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/Actions/"
230 "CertificateService.GenerateCSR/")
231 .privileges({"ConfigureComponents"})
232 .methods(boost::beast::http::verb::post)(
233 [](const crow::Request& req,
234 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
235 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500236
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700237 // Required parameters
238 std::string city;
239 std::string commonName;
240 std::string country;
241 std::string organization;
242 std::string organizationalUnit;
243 std::string state;
244 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800245
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700246 // Optional parameters
247 std::optional<std::vector<std::string>> optAlternativeNames =
248 std::vector<std::string>();
249 std::optional<std::string> optContactPerson = "";
250 std::optional<std::string> optChallengePassword = "";
251 std::optional<std::string> optEmail = "";
252 std::optional<std::string> optGivenName = "";
253 std::optional<std::string> optInitials = "";
254 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
255 std::optional<std::string> optKeyCurveId = "secp384r1";
256 std::optional<std::string> optKeyPairAlgorithm = "EC";
257 std::optional<std::vector<std::string>> optKeyUsage =
258 std::vector<std::string>();
259 std::optional<std::string> optSurname = "";
260 std::optional<std::string> optUnstructuredName = "";
261 if (!json_util::readJson(
262 req, asyncResp->res, "City", city, "CommonName",
263 commonName, "ContactPerson", optContactPerson,
264 "Country", country, "Organization", organization,
265 "OrganizationalUnit", organizationalUnit, "State",
266 state, "CertificateCollection", certificateCollection,
267 "AlternativeNames", optAlternativeNames,
268 "ChallengePassword", optChallengePassword, "Email",
269 optEmail, "GivenName", optGivenName, "Initials",
270 optInitials, "KeyBitLength", optKeyBitLength,
271 "KeyCurveId", optKeyCurveId, "KeyPairAlgorithm",
272 optKeyPairAlgorithm, "KeyUsage", optKeyUsage, "Surname",
273 optSurname, "UnstructuredName", optUnstructuredName))
274 {
275 return;
276 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500277
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700278 // bmcweb has no way to store or decode a private key challenge
279 // password, which will likely cause bmcweb to crash on startup
280 // if this is not set on a post so not allowing the user to set
281 // value
282 if (*optChallengePassword != "")
283 {
284 messages::actionParameterNotSupported(
285 asyncResp->res, "GenerateCSR", "ChallengePassword");
286 return;
287 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500288
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700289 std::string certURI;
290 if (!redfish::json_util::readJson(certificateCollection,
291 asyncResp->res, "@odata.id",
292 certURI))
293 {
294 return;
295 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500296
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700297 std::string objectPath;
298 std::string service;
299 if (boost::starts_with(certURI,
300 "/redfish/v1/Managers/bmc/"
301 "NetworkProtocol/HTTPS/Certificates"))
302 {
303 objectPath = certs::httpsObjectPath;
304 service = certs::httpsServiceName;
305 }
306 else if (boost::starts_with(
307 certURI,
308 "/redfish/v1/AccountService/LDAP/Certificates"))
309 {
310 objectPath = certs::ldapObjectPath;
311 service = certs::ldapServiceName;
312 }
313 else
314 {
315 messages::actionParameterNotSupported(
316 asyncResp->res, "CertificateCollection", "GenerateCSR");
317 return;
318 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500319
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700320 // supporting only EC and RSA algorithm
321 if (*optKeyPairAlgorithm != "EC" &&
322 *optKeyPairAlgorithm != "RSA")
323 {
324 messages::actionParameterNotSupported(
325 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
326 return;
327 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500328
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700329 // supporting only 2048 key bit length for RSA algorithm due to
330 // time consumed in generating private key
331 if (*optKeyPairAlgorithm == "RSA" &&
332 *optKeyBitLength != rsaKeyBitLength)
Marri Devender Rao30215812019-03-18 08:59:21 -0500333 {
334 messages::propertyValueNotInList(
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700335 asyncResp->res, std::to_string(*optKeyBitLength),
336 "KeyBitLength");
Marri Devender Rao30215812019-03-18 08:59:21 -0500337 return;
338 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500339
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700340 // validate KeyUsage supporting only 1 type based on URL
341 if (boost::starts_with(certURI,
342 "/redfish/v1/Managers/bmc/"
343 "NetworkProtocol/HTTPS/Certificates"))
Marri Devender Rao30215812019-03-18 08:59:21 -0500344 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700345 if (optKeyUsage->size() == 0)
Marri Devender Rao30215812019-03-18 08:59:21 -0500346 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700347 optKeyUsage->push_back("ServerAuthentication");
348 }
349 else if (optKeyUsage->size() == 1)
350 {
351 if ((*optKeyUsage)[0] != "ServerAuthentication")
352 {
353 messages::propertyValueNotInList(
354 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
355 return;
356 }
357 }
358 else
359 {
360 messages::actionParameterNotSupported(
361 asyncResp->res, "KeyUsage", "GenerateCSR");
362 return;
Marri Devender Rao30215812019-03-18 08:59:21 -0500363 }
364 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700365 else if (boost::starts_with(
366 certURI,
367 "/redfish/v1/AccountService/LDAP/Certificates"))
Marri Devender Rao30215812019-03-18 08:59:21 -0500368 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700369 if (optKeyUsage->size() == 0)
370 {
371 optKeyUsage->push_back("ClientAuthentication");
372 }
373 else if (optKeyUsage->size() == 1)
374 {
375 if ((*optKeyUsage)[0] != "ClientAuthentication")
376 {
377 messages::propertyValueNotInList(
378 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
379 return;
380 }
381 }
382 else
383 {
384 messages::actionParameterNotSupported(
385 asyncResp->res, "KeyUsage", "GenerateCSR");
386 return;
387 }
388 }
389
390 // Only allow one CSR matcher at a time so setting retry
391 // time-out and timer expiry to 10 seconds for now.
392 static const int timeOut = 10;
393 if (csrMatcher)
394 {
395 messages::serviceTemporarilyUnavailable(
396 asyncResp->res, std::to_string(timeOut));
Marri Devender Rao30215812019-03-18 08:59:21 -0500397 return;
398 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700399
400 // Make this static so it survives outside this method
401 static boost::asio::steady_timer timeout(*req.ioService);
402 timeout.expires_after(std::chrono::seconds(timeOut));
403 timeout.async_wait([asyncResp](
404 const boost::system::error_code& ec) {
405 csrMatcher = nullptr;
406 if (ec)
407 {
408 // operation_aborted is expected if timer is canceled
409 // before completion.
410 if (ec != boost::asio::error::operation_aborted)
411 {
412 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
413 }
414 return;
415 }
416 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
417 messages::internalError(asyncResp->res);
418 });
419
420 // create a matcher to wait on CSR object
421 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
422 std::string match(
423 "type='signal',"
424 "interface='org.freedesktop.DBus.ObjectManager',"
425 "path='" +
426 objectPath +
427 "',"
428 "member='InterfacesAdded'");
429 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
430 *crow::connections::systemBus, match,
431 [asyncResp, service, objectPath,
432 certURI](sdbusplus::message::message& m) {
433 timeout.cancel();
434 if (m.is_method_error())
435 {
436 BMCWEB_LOG_ERROR << "Dbus method error!!!";
437 messages::internalError(asyncResp->res);
438 return;
439 }
440 std::vector<std::pair<
441 std::string,
442 std::vector<std::pair<std::string,
443 std::variant<std::string>>>>>
444 interfacesProperties;
445 sdbusplus::message::object_path csrObjectPath;
446 m.read(csrObjectPath, interfacesProperties);
447 BMCWEB_LOG_DEBUG << "CSR object added"
448 << csrObjectPath.str;
449 for (auto& interface : interfacesProperties)
450 {
451 if (interface.first ==
452 "xyz.openbmc_project.Certs.CSR")
453 {
454 getCSR(asyncResp, certURI, service, objectPath,
455 csrObjectPath.str);
456 break;
457 }
458 }
459 });
460 crow::connections::systemBus->async_method_call(
461 [asyncResp](const boost::system::error_code& ec,
462 const std::string&) {
463 if (ec)
464 {
465 BMCWEB_LOG_ERROR << "DBUS response error: "
466 << ec.message();
467 messages::internalError(asyncResp->res);
468 return;
469 }
470 },
471 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
472 "GenerateCSR", *optAlternativeNames, *optChallengePassword,
473 city, commonName, *optContactPerson, country, *optEmail,
474 *optGivenName, *optInitials, *optKeyBitLength,
475 *optKeyCurveId, *optKeyPairAlgorithm, *optKeyUsage,
476 organization, organizationalUnit, state, *optSurname,
477 *optUnstructuredName);
478 });
479} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500480
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600481/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500482 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600483 *
484 * @param[in] asyncResp Shared pointer to the response message
485 * @param[in] str Issuer/Subject value in key=value pairs
486 * @param[in] type Issuer/Subject
487 * @return None
488 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500489static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600490 const std::string_view value)
491{
492 // example: O=openbmc-project.xyz,CN=localhost
493 std::string_view::iterator i = value.begin();
494 while (i != value.end())
495 {
496 std::string_view::iterator tokenBegin = i;
497 while (i != value.end() && *i != '=')
498 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530499 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600500 }
501 if (i == value.end())
502 {
503 break;
504 }
Ed Tanous271584a2019-07-09 16:24:22 -0700505 const std::string_view key(tokenBegin,
506 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530507 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600508 tokenBegin = i;
509 while (i != value.end() && *i != ',')
510 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530511 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600512 }
Ed Tanous271584a2019-07-09 16:24:22 -0700513 const std::string_view val(tokenBegin,
514 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600515 if (key == "L")
516 {
517 out["City"] = val;
518 }
519 else if (key == "CN")
520 {
521 out["CommonName"] = val;
522 }
523 else if (key == "C")
524 {
525 out["Country"] = val;
526 }
527 else if (key == "O")
528 {
529 out["Organization"] = val;
530 }
531 else if (key == "OU")
532 {
533 out["OrganizationalUnit"] = val;
534 }
535 else if (key == "ST")
536 {
537 out["State"] = val;
538 }
539 // skip comma character
540 if (i != value.end())
541 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530542 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600543 }
544 }
545}
546
547/**
548 * @brief Retrieve the certificates properties and append to the response
549 * message
550 *
551 * @param[in] asyncResp Shared pointer to the response message
552 * @param[in] objectPath Path of the D-Bus service object
553 * @param[in] certId Id of the certificate
554 * @param[in] certURL URL of the certificate object
555 * @param[in] name name of the certificate
556 * @return None
557 */
558static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800559 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
560 const std::string& objectPath, const std::string& service, long certId,
561 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600562{
563 using PropertyType =
564 std::variant<std::string, uint64_t, std::vector<std::string>>;
565 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
566 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
567 << " certId=" << certId << " certURl=" << certURL;
568 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600569 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500570 const PropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600571 if (ec)
572 {
573 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500574 messages::resourceNotFound(asyncResp->res, name,
575 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600576 return;
577 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600578 asyncResp->res.jsonValue = {
579 {"@odata.id", certURL},
580 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600581 {"Id", std::to_string(certId)},
582 {"Name", name},
583 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500584 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600585 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600586 if (property.first == "CertificateString")
587 {
588 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500589 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600590 std::get_if<std::string>(&property.second);
591 if (value)
592 {
593 asyncResp->res.jsonValue["CertificateString"] = *value;
594 }
595 }
596 else if (property.first == "KeyUsage")
597 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500598 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600599 asyncResp->res.jsonValue["KeyUsage"];
600 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500601 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600602 std::get_if<std::vector<std::string>>(&property.second);
603 if (value)
604 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500605 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600606 {
607 keyUsage.push_back(usage);
608 }
609 }
610 }
611 else if (property.first == "Issuer")
612 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500613 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600614 std::get_if<std::string>(&property.second);
615 if (value)
616 {
617 updateCertIssuerOrSubject(
618 asyncResp->res.jsonValue["Issuer"], *value);
619 }
620 }
621 else if (property.first == "Subject")
622 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500623 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600624 std::get_if<std::string>(&property.second);
625 if (value)
626 {
627 updateCertIssuerOrSubject(
628 asyncResp->res.jsonValue["Subject"], *value);
629 }
630 }
631 else if (property.first == "ValidNotAfter")
632 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500633 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600634 std::get_if<uint64_t>(&property.second);
635 if (value)
636 {
637 std::time_t time = static_cast<std::time_t>(*value);
638 asyncResp->res.jsonValue["ValidNotAfter"] =
639 crow::utility::getDateTime(time);
640 }
641 }
642 else if (property.first == "ValidNotBefore")
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["ValidNotBefore"] =
650 crow::utility::getDateTime(time);
651 }
652 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600653 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600654 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600655 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600656 service, objectPath, certs::dbusPropIntf, "GetAll",
657 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600658}
659
660using GetObjectType =
661 std::vector<std::pair<std::string, std::vector<std::string>>>;
662
663/**
664 * Action to replace an existing certificate
665 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700666inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600667{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700668 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/Actions/"
669 "CertificateService.ReplaceCertificate/")
670 .privileges({"ConfigureComponents"})
671 .methods(
672 boost::beast::http::verb::
673 post)([](const crow::Request& req,
674 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
675 std::string certificate;
676 nlohmann::json certificateUri;
677 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600678
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700679 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
680 certificate, "CertificateUri",
681 certificateUri, "CertificateType",
682 certificateType))
683 {
684 BMCWEB_LOG_ERROR << "Required parameters are missing";
685 messages::internalError(asyncResp->res);
686 return;
687 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800688
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700689 if (!certificateType)
690 {
691 // should never happen, but it never hurts to be paranoid.
692 return;
693 }
694 if (certificateType != "PEM")
695 {
696 messages::actionParameterNotSupported(
697 asyncResp->res, "CertificateType", "ReplaceCertificate");
698 return;
699 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600700
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700701 std::string certURI;
702 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
703 "@odata.id", certURI))
704 {
705 messages::actionParameterMissing(
706 asyncResp->res, "ReplaceCertificate", "CertificateUri");
707 return;
708 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600709
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700710 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
711 long id = getIDFromURL(certURI);
712 if (id < 0)
713 {
714 messages::actionParameterValueFormatError(
715 asyncResp->res, certURI, "CertificateUri",
716 "ReplaceCertificate");
717 return;
718 }
719 std::string objectPath;
720 std::string name;
721 std::string service;
722 if (boost::starts_with(certURI,
723 "/redfish/v1/Managers/bmc/NetworkProtocol/"
724 "HTTPS/Certificates/"))
725 {
726 objectPath = std::string(certs::httpsObjectPath) + "/" +
727 std::to_string(id);
728 name = "HTTPS certificate";
729 service = certs::httpsServiceName;
730 }
731 else if (boost::starts_with(
732 certURI,
733 "/redfish/v1/AccountService/LDAP/Certificates/"))
734 {
735 objectPath = std::string(certs::ldapObjectPath) + "/" +
736 std::to_string(id);
737 name = "LDAP certificate";
738 service = certs::ldapServiceName;
739 }
740 else if (boost::starts_with(
741 certURI,
742 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
743 {
744 objectPath = std::string(certs::authorityObjectPath) + "/" +
745 std::to_string(id);
746 name = "TrustStore certificate";
747 service = certs::authorityServiceName;
748 }
749 else
750 {
751 messages::actionParameterNotSupported(
752 asyncResp->res, "CertificateUri", "ReplaceCertificate");
753 return;
754 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600755
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700756 std::shared_ptr<CertificateFile> certFile =
757 std::make_shared<CertificateFile>(certificate);
758 crow::connections::systemBus->async_method_call(
759 [asyncResp, certFile, objectPath, service, certURI, id,
760 name](const boost::system::error_code ec) {
761 if (ec)
762 {
763 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
764 messages::resourceNotFound(asyncResp->res, name,
765 std::to_string(id));
766 return;
767 }
768 getCertificateProperties(asyncResp, objectPath, service, id,
769 certURI, name);
770 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
771 << certFile->getCertFilePath();
772 },
773 service, objectPath, certs::certReplaceIntf, "Replace",
774 certFile->getCertFilePath());
775 });
776} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600777
778/**
779 * Certificate resource describes a certificate used to prove the identity
780 * of a component, account or service.
781 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700782
783inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600784{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700785 BMCWEB_ROUTE(
786 app,
787 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
788 .privileges({"Login"})
789 .methods(
790 boost::beast::http::verb::
791 get)([](const crow::Request& req,
792 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
793 const std::string& param) -> void {
794 if (param.empty())
795 {
796 messages::internalError(asyncResp->res);
797 return;
798 }
799 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600800
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700801 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
802 << std::to_string(id);
803 std::string certURL =
804 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
805 std::to_string(id);
806 std::string objectPath = certs::httpsObjectPath;
807 objectPath += "/";
808 objectPath += std::to_string(id);
809 getCertificateProperties(asyncResp, objectPath,
810 certs::httpsServiceName, id, certURL,
811 "HTTPS Certificate");
812 });
813}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600814
815/**
816 * Collection of HTTPS certificates
817 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700818inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600819{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700820 BMCWEB_ROUTE(app,
821 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
822 .privileges({"Login"})
823 .methods(
824 boost::beast::http::verb::
825 get)([](const crow::Request&,
826 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
827 asyncResp->res.jsonValue = {
828 {"@odata.id",
829 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
830 {"@odata.type", "#CertificateCollection.CertificateCollection"},
831 {"Name", "HTTPS Certificates Collection"},
832 {"Description", "A Collection of HTTPS certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +0800833
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700834 crow::connections::systemBus->async_method_call(
835 [asyncResp](const boost::system::error_code ec,
836 const ManagedObjectType& certs) {
837 if (ec)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600838 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700839 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
840 messages::internalError(asyncResp->res);
841 return;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600842 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700843 nlohmann::json& members =
844 asyncResp->res.jsonValue["Members"];
845 members = nlohmann::json::array();
846 for (const auto& cert : certs)
847 {
848 long id = getIDFromURL(cert.first.str);
849 if (id >= 0)
850 {
851 members.push_back(
852 {{"@odata.id",
853 "/redfish/v1/Managers/bmc/"
854 "NetworkProtocol/HTTPS/Certificates/" +
855 std::to_string(id)}});
856 }
857 }
858 asyncResp->res.jsonValue["Members@odata.count"] =
859 members.size();
860 },
861 certs::httpsServiceName, certs::httpsObjectPath,
862 certs::dbusObjManagerIntf, "GetManagedObjects");
863 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600864
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700865 BMCWEB_ROUTE(app,
866 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
867 .privileges({"ConfigureComponents"})
868 .methods(boost::beast::http::verb::post)(
869 [](const crow::Request& req,
870 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
871 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
zhanghch058d1b46d2021-04-01 11:18:24 +0800872
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700873 asyncResp->res.jsonValue = {
874 {"Name", "HTTPS Certificate"},
875 {"Description", "HTTPS Certificate"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600876
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700877 std::string certFileBody =
878 getCertificateFromReqBody(asyncResp, req);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200879
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700880 if (certFileBody.empty())
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600881 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700882 BMCWEB_LOG_ERROR
883 << "Cannot get certificate from request body.";
884 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600885 return;
886 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700887
888 std::shared_ptr<CertificateFile> certFile =
889 std::make_shared<CertificateFile>(certFileBody);
890
891 crow::connections::systemBus->async_method_call(
892 [asyncResp, certFile](const boost::system::error_code ec,
893 const std::string& objectPath) {
894 if (ec)
895 {
896 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
897 messages::internalError(asyncResp->res);
898 return;
899 }
900 long certId = getIDFromURL(objectPath);
901 if (certId < 0)
902 {
903 BMCWEB_LOG_ERROR << "Invalid objectPath value"
904 << objectPath;
905 messages::internalError(asyncResp->res);
906 return;
907 }
908 std::string certURL =
909 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
910 "Certificates/" +
911 std::to_string(certId);
912 getCertificateProperties(
913 asyncResp, objectPath, certs::httpsServiceName,
914 certId, certURL, "HTTPS Certificate");
915 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
916 << certFile->getCertFilePath();
917 },
918 certs::httpsServiceName, certs::httpsObjectPath,
919 certs::certInstallIntf, "Install",
920 certFile->getCertFilePath());
921 });
922} // requestRoutesHTTPSCertificateCollection
923
924/**
925 * @brief Retrieve the certificates installed list and append to the
926 * response
927 *
928 * @param[in] asyncResp Shared pointer to the response message
929 * @param[in] certURL Path of the certificate object
930 * @param[in] path Path of the D-Bus service object
931 * @return None
932 */
933void getCertificateLocations(
934 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
935 const std::string& certURL, const std::string& path,
936 const std::string& service)
937{
938 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
939 << " Path=" << path << " service= " << service;
940 crow::connections::systemBus->async_method_call(
941 [asyncResp, certURL](const boost::system::error_code ec,
942 const ManagedObjectType& certs) {
943 if (ec)
944 {
945 BMCWEB_LOG_WARNING
946 << "Certificate collection query failed: " << ec
947 << ", skipping " << certURL;
948 return;
949 }
950 nlohmann::json& links =
951 asyncResp->res.jsonValue["Links"]["Certificates"];
952 for (auto& cert : certs)
953 {
954 long id = getIDFromURL(cert.first.str);
955 if (id >= 0)
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200956 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700957 links.push_back(
958 {{"@odata.id", certURL + std::to_string(id)}});
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200959 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700960 }
961 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
962 links.size();
963 },
964 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
965}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600966
967/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600968 * The certificate location schema defines a resource that an administrator
969 * can use in order to locate all certificates installed on a given service.
970 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700971inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600972{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700973 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
974 .privileges({"Login"})
975 .methods(
976 boost::beast::http::verb::
977 get)([](const crow::Request&,
978 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
979 asyncResp->res.jsonValue = {
980 {"@odata.id",
981 "/redfish/v1/CertificateService/CertificateLocations"},
982 {"@odata.type",
983 "#CertificateLocations.v1_0_0.CertificateLocations"},
984 {"Name", "Certificate Locations"},
985 {"Id", "CertificateLocations"},
986 {"Description",
987 "Defines a resource that an administrator can use in order to "
988 "locate all certificates installed on a given service"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600989
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700990 nlohmann::json& links =
991 asyncResp->res.jsonValue["Links"]["Certificates"];
992 links = nlohmann::json::array();
993 getCertificateLocations(
994 asyncResp,
995 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
996 certs::httpsObjectPath, certs::httpsServiceName);
997 getCertificateLocations(
998 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/",
999 certs::ldapObjectPath, certs::ldapServiceName);
1000 getCertificateLocations(
1001 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1002 certs::authorityObjectPath, certs::authorityServiceName);
1003 });
1004}
1005// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001006
1007/**
1008 * Collection of LDAP certificates
1009 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001010inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001011{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001012 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1013 .privileges({"Login"})
1014 .methods(boost::beast::http::verb::get)(
1015 [](const crow::Request&,
1016 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1017 asyncResp->res.jsonValue = {
1018 {"@odata.id",
1019 "/redfish/v1/AccountService/LDAP/Certificates"},
1020 {"@odata.type",
1021 "#CertificateCollection.CertificateCollection"},
1022 {"Name", "LDAP Certificates Collection"},
1023 {"Description",
1024 "A Collection of LDAP certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001025
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001026 crow::connections::systemBus->async_method_call(
1027 [asyncResp](const boost::system::error_code ec,
1028 const ManagedObjectType& certs) {
1029 nlohmann::json& members =
1030 asyncResp->res.jsonValue["Members"];
1031 nlohmann::json& count =
1032 asyncResp->res.jsonValue["Members@odata.count"];
1033 members = nlohmann::json::array();
1034 count = 0;
1035 if (ec)
1036 {
1037 BMCWEB_LOG_WARNING
1038 << "LDAP certificate query failed: " << ec;
1039 return;
1040 }
1041 for (const auto& cert : certs)
1042 {
1043 long id = getIDFromURL(cert.first.str);
1044 if (id >= 0)
1045 {
1046 members.push_back(
1047 {{"@odata.id", "/redfish/v1/AccountService/"
1048 "LDAP/Certificates/" +
1049 std::to_string(id)}});
1050 }
1051 }
1052 count = members.size();
1053 },
1054 certs::ldapServiceName, certs::ldapObjectPath,
1055 certs::dbusObjManagerIntf, "GetManagedObjects");
1056 });
1057
1058 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1059 .privileges({"ConfigureComponents"})
1060 .methods(boost::beast::http::verb::post)(
1061 [](const crow::Request& req,
1062 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1063 std::string certFileBody =
1064 getCertificateFromReqBody(asyncResp, req);
1065
1066 if (certFileBody.empty())
Marri Devender Rao37cce912019-02-20 01:05:22 -06001067 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001068 BMCWEB_LOG_ERROR
1069 << "Cannot get certificate from request body.";
1070 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001071 return;
1072 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001073
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001074 std::shared_ptr<CertificateFile> certFile =
1075 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001076
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001077 crow::connections::systemBus->async_method_call(
1078 [asyncResp, certFile](const boost::system::error_code ec,
1079 const std::string& objectPath) {
1080 if (ec)
1081 {
1082 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1083 messages::internalError(asyncResp->res);
1084 return;
1085 }
1086 long certId = getIDFromURL(objectPath);
1087 if (certId < 0)
1088 {
1089 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1090 << objectPath;
1091 messages::internalError(asyncResp->res);
1092 return;
1093 }
1094 std::string certURL =
1095 "/redfish/v1/AccountService/LDAP/Certificates/" +
1096 std::to_string(certId);
1097 getCertificateProperties(asyncResp, objectPath,
1098 certs::ldapServiceName, certId,
1099 certURL, "LDAP Certificate");
1100 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1101 << certFile->getCertFilePath();
1102 },
1103 certs::ldapServiceName, certs::ldapObjectPath,
1104 certs::certInstallIntf, "Install",
1105 certFile->getCertFilePath());
1106 });
1107} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001108
1109/**
1110 * Certificate resource describes a certificate used to prove the identity
1111 * of a component, account or service.
1112 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001113inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001114{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001115 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
1116 .privileges({"Login"})
1117 .methods(boost::beast::http::verb::get)(
1118 [](const crow::Request& req,
1119 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1120 const std::string&) {
1121 long id = getIDFromURL(req.url);
1122 if (id < 0)
1123 {
1124 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1125 messages::internalError(asyncResp->res);
1126 return;
1127 }
1128 BMCWEB_LOG_DEBUG << "LDAP Certificate ID="
1129 << std::to_string(id);
1130 std::string certURL =
1131 "/redfish/v1/AccountService/LDAP/Certificates/" +
1132 std::to_string(id);
1133 std::string objectPath = certs::ldapObjectPath;
1134 objectPath += "/";
1135 objectPath += std::to_string(id);
1136 getCertificateProperties(asyncResp, objectPath,
1137 certs::ldapServiceName, id, certURL,
1138 "LDAP Certificate");
1139 });
1140} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001141/**
1142 * Collection of TrustStoreCertificate certificates
1143 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001144inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001145{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001146 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1147 .privileges({"Login"})
1148 .methods(boost::beast::http::verb::get)(
1149 [](const crow::Request&,
1150 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1151 asyncResp->res.jsonValue = {
1152 {"@odata.id",
1153 "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1154 {"@odata.type",
1155 "#CertificateCollection.CertificateCollection"},
1156 {"Name", "TrustStore Certificates Collection"},
1157 {"Description",
1158 "A Collection of TrustStore certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001159
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001160 crow::connections::systemBus->async_method_call(
1161 [asyncResp](const boost::system::error_code ec,
1162 const ManagedObjectType& certs) {
1163 if (ec)
1164 {
1165 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1166 messages::internalError(asyncResp->res);
1167 return;
1168 }
1169 nlohmann::json& members =
1170 asyncResp->res.jsonValue["Members"];
1171 members = nlohmann::json::array();
1172 for (const auto& cert : certs)
1173 {
1174 long id = getIDFromURL(cert.first.str);
1175 if (id >= 0)
1176 {
1177 members.push_back(
1178 {{"@odata.id", "/redfish/v1/Managers/bmc/"
1179 "Truststore/Certificates/" +
1180 std::to_string(id)}});
1181 }
1182 }
1183 asyncResp->res.jsonValue["Members@odata.count"] =
1184 members.size();
1185 },
1186 certs::authorityServiceName, certs::authorityObjectPath,
1187 certs::dbusObjManagerIntf, "GetManagedObjects");
1188 });
1189
1190 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1191 .privileges({"ConfigureComponents"})
1192 .methods(boost::beast::http::verb::post)(
1193 [](const crow::Request& req,
1194 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1195 std::string certFileBody =
1196 getCertificateFromReqBody(asyncResp, req);
1197
1198 if (certFileBody.empty())
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001199 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001200 BMCWEB_LOG_ERROR
1201 << "Cannot get certificate from request body.";
1202 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001203 return;
1204 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001205
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001206 std::shared_ptr<CertificateFile> certFile =
1207 std::make_shared<CertificateFile>(certFileBody);
1208 crow::connections::systemBus->async_method_call(
1209 [asyncResp, certFile](const boost::system::error_code ec,
1210 const std::string& objectPath) {
1211 if (ec)
1212 {
1213 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1214 messages::internalError(asyncResp->res);
1215 return;
1216 }
1217 long certId = getIDFromURL(objectPath);
1218 if (certId < 0)
1219 {
1220 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1221 << objectPath;
1222 messages::internalError(asyncResp->res);
1223 return;
1224 }
1225 std::string certURL = "/redfish/v1/Managers/bmc/"
1226 "Truststore/Certificates/" +
1227 std::to_string(certId);
zhanghch058d1b46d2021-04-01 11:18:24 +08001228
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001229 getCertificateProperties(
1230 asyncResp, objectPath, certs::authorityServiceName,
1231 certId, certURL, "TrustStore Certificate");
1232 BMCWEB_LOG_DEBUG
1233 << "TrustStore certificate install file="
1234 << certFile->getCertFilePath();
1235 },
1236 certs::authorityServiceName, certs::authorityObjectPath,
1237 certs::certInstallIntf, "Install",
1238 certFile->getCertFilePath());
1239 });
1240} // 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>/")
1249 .privileges({"Loign"})
1250 .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>/")
1275 .privileges({"ConfigureComponents"})
1276 .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