blob: 6eb66c593e8f83690b42d4a4ede6137823fa56f9 [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/")
Ed Tanous432a8902021-06-14 15:28:56 -070045 .privileges({{"Login"}})
Abhishek Patel72048782021-06-02 09:53:24 -050046 .methods(
47 boost::beast::http::verb::
48 get)([](const crow::Request& req,
49 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
50 asyncResp->res.jsonValue = {
51 {"@odata.type",
52 "#CertificateService.v1_0_0.CertificateService"},
53 {"@odata.id", "/redfish/v1/CertificateService"},
54 {"Id", "CertificateService"},
55 {"Name", "Certificate Service"},
56 {"Description", "Actions available to manage certificates"}};
57 // /redfish/v1/CertificateService/CertificateLocations is something
58 // only ConfigureManager can access then only display when the user
59 // has permissions ConfigureManager
60 Privileges effectiveUserPrivileges =
61 redfish::getUserPrivileges(req.userRole);
62 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
63 effectiveUserPrivileges))
64 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -070065 asyncResp->res.jsonValue["CertificateLocations"] = {
66 {"@odata.id",
67 "/redfish/v1/CertificateService/CertificateLocations"}};
Abhishek Patel72048782021-06-02 09:53:24 -050068 }
69 asyncResp->res.jsonValue["Actions"]
70 ["#CertificateService.ReplaceCertificate"] =
71 {{"target", "/redfish/v1/CertificateService/Actions/"
72 "CertificateService.ReplaceCertificate"},
73 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
74 asyncResp->res
75 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
76 {"target", "/redfish/v1/CertificateService/Actions/"
77 "CertificateService.GenerateCSR"}};
78 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070079} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060080
Marri Devender Rao5968cae2019-01-21 10:27:12 -060081/**
82 * @brief Find the ID specified in the URL
83 * Finds the numbers specified after the last "/" in the URL and returns.
84 * @param[in] path URL
85 * @return -1 on failure and number on success
86 */
Ed Tanous23a21a12020-07-25 04:45:05 +000087inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060088{
Ed Tanousf23b7292020-10-15 09:41:17 -070089 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -060090 if (found == std::string::npos)
91 {
92 return -1;
93 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020094
Marri Devender Rao5968cae2019-01-21 10:27:12 -060095 if ((found + 1) < url.length())
96 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -060097 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020098
99 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600100 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200101
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600102 return -1;
103}
104
zhanghch058d1b46d2021-04-01 11:18:24 +0800105inline std::string getCertificateFromReqBody(
106 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
107 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200108{
109 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
110
111 if (reqJson.is_discarded())
112 {
113 // We did not receive JSON request, proceed as it is RAW data
114 return req.body;
115 }
116
117 std::string certificate;
118 std::optional<std::string> certificateType = "PEM";
119
120 if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
121 certificate, "CertificateType", certificateType))
122 {
123 BMCWEB_LOG_ERROR << "Required parameters are missing";
124 messages::internalError(asyncResp->res);
125 return std::string();
126 }
127
128 if (*certificateType != "PEM")
129 {
130 messages::propertyValueNotInList(asyncResp->res, *certificateType,
131 "CertificateType");
132 return std::string();
133 }
134
135 return certificate;
136}
137
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600138/**
139 * Class to create a temporary certificate file for uploading to system
140 */
141class CertificateFile
142{
143 public:
144 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500145 CertificateFile(const CertificateFile&) = delete;
146 CertificateFile& operator=(const CertificateFile&) = delete;
147 CertificateFile(CertificateFile&&) = delete;
148 CertificateFile& operator=(CertificateFile&&) = delete;
149 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600150 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700151 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700152 'e', 'r', 't', 's', '.', 'X',
153 'X', 'X', 'X', 'X', 'X', '\0'};
154 char* tempDirectory = mkdtemp(dirTemplate.data());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600155 if (tempDirectory)
156 {
157 certDirectory = tempDirectory;
158 certificateFile = certDirectory / "cert.pem";
159 std::ofstream out(certificateFile, std::ofstream::out |
160 std::ofstream::binary |
161 std::ofstream::trunc);
162 out << certString;
163 out.close();
164 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
165 }
166 }
167 ~CertificateFile()
168 {
169 if (std::filesystem::exists(certDirectory))
170 {
171 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
Ed Tanous23a21a12020-07-25 04:45:05 +0000172 std::error_code ec;
173 std::filesystem::remove_all(certDirectory, ec);
174 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600175 {
176 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
177 << certDirectory;
178 }
179 }
180 }
181 std::string getCertFilePath()
182 {
183 return certificateFile;
184 }
185
186 private:
187 std::filesystem::path certificateFile;
188 std::filesystem::path certDirectory;
189};
190
Marri Devender Rao30215812019-03-18 08:59:21 -0500191static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
192/**
193 * @brief Read data from CSR D-bus object and set to response
194 *
195 * @param[in] asyncResp Shared pointer to the response message
196 * @param[in] certURI Link to certifiate collection URI
197 * @param[in] service D-Bus service name
198 * @param[in] certObjPath certificate D-Bus object path
199 * @param[in] csrObjPath CSR D-Bus object path
200 * @return None
201 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800202static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500203 const std::string& certURI, const std::string& service,
204 const std::string& certObjPath,
205 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500206{
207 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
208 << " CSRObjectPath=" << csrObjPath
209 << " service=" << service;
210 crow::connections::systemBus->async_method_call(
211 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500212 const std::string& csr) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500213 if (ec)
214 {
215 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
216 messages::internalError(asyncResp->res);
217 return;
218 }
219 if (csr.empty())
220 {
221 BMCWEB_LOG_ERROR << "CSR read is empty";
222 messages::internalError(asyncResp->res);
223 return;
224 }
225 asyncResp->res.jsonValue["CSRString"] = csr;
226 asyncResp->res.jsonValue["CertificateCollection"] = {
227 {"@odata.id", certURI}};
228 },
229 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
230}
231
232/**
233 * Action to Generate CSR
234 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700235inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500236{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700237 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/Actions/"
238 "CertificateService.GenerateCSR/")
Ed Tanous432a8902021-06-14 15:28:56 -0700239 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700240 .methods(boost::beast::http::verb::post)(
241 [](const crow::Request& req,
242 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
243 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500244
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700245 // Required parameters
246 std::string city;
247 std::string commonName;
248 std::string country;
249 std::string organization;
250 std::string organizationalUnit;
251 std::string state;
252 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800253
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700254 // Optional parameters
255 std::optional<std::vector<std::string>> optAlternativeNames =
256 std::vector<std::string>();
257 std::optional<std::string> optContactPerson = "";
258 std::optional<std::string> optChallengePassword = "";
259 std::optional<std::string> optEmail = "";
260 std::optional<std::string> optGivenName = "";
261 std::optional<std::string> optInitials = "";
262 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
263 std::optional<std::string> optKeyCurveId = "secp384r1";
264 std::optional<std::string> optKeyPairAlgorithm = "EC";
265 std::optional<std::vector<std::string>> optKeyUsage =
266 std::vector<std::string>();
267 std::optional<std::string> optSurname = "";
268 std::optional<std::string> optUnstructuredName = "";
269 if (!json_util::readJson(
270 req, asyncResp->res, "City", city, "CommonName",
271 commonName, "ContactPerson", optContactPerson,
272 "Country", country, "Organization", organization,
273 "OrganizationalUnit", organizationalUnit, "State",
274 state, "CertificateCollection", certificateCollection,
275 "AlternativeNames", optAlternativeNames,
276 "ChallengePassword", optChallengePassword, "Email",
277 optEmail, "GivenName", optGivenName, "Initials",
278 optInitials, "KeyBitLength", optKeyBitLength,
279 "KeyCurveId", optKeyCurveId, "KeyPairAlgorithm",
280 optKeyPairAlgorithm, "KeyUsage", optKeyUsage, "Surname",
281 optSurname, "UnstructuredName", optUnstructuredName))
282 {
283 return;
284 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500285
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700286 // bmcweb has no way to store or decode a private key challenge
287 // password, which will likely cause bmcweb to crash on startup
288 // if this is not set on a post so not allowing the user to set
289 // value
290 if (*optChallengePassword != "")
291 {
292 messages::actionParameterNotSupported(
293 asyncResp->res, "GenerateCSR", "ChallengePassword");
294 return;
295 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500296
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700297 std::string certURI;
298 if (!redfish::json_util::readJson(certificateCollection,
299 asyncResp->res, "@odata.id",
300 certURI))
301 {
302 return;
303 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500304
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700305 std::string objectPath;
306 std::string service;
307 if (boost::starts_with(certURI,
308 "/redfish/v1/Managers/bmc/"
309 "NetworkProtocol/HTTPS/Certificates"))
310 {
311 objectPath = certs::httpsObjectPath;
312 service = certs::httpsServiceName;
313 }
314 else if (boost::starts_with(
315 certURI,
316 "/redfish/v1/AccountService/LDAP/Certificates"))
317 {
318 objectPath = certs::ldapObjectPath;
319 service = certs::ldapServiceName;
320 }
321 else
322 {
323 messages::actionParameterNotSupported(
324 asyncResp->res, "CertificateCollection", "GenerateCSR");
325 return;
326 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500327
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700328 // supporting only EC and RSA algorithm
329 if (*optKeyPairAlgorithm != "EC" &&
330 *optKeyPairAlgorithm != "RSA")
331 {
332 messages::actionParameterNotSupported(
333 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
334 return;
335 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500336
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700337 // supporting only 2048 key bit length for RSA algorithm due to
338 // time consumed in generating private key
339 if (*optKeyPairAlgorithm == "RSA" &&
340 *optKeyBitLength != rsaKeyBitLength)
Marri Devender Rao30215812019-03-18 08:59:21 -0500341 {
342 messages::propertyValueNotInList(
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700343 asyncResp->res, std::to_string(*optKeyBitLength),
344 "KeyBitLength");
Marri Devender Rao30215812019-03-18 08:59:21 -0500345 return;
346 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500347
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700348 // validate KeyUsage supporting only 1 type based on URL
349 if (boost::starts_with(certURI,
350 "/redfish/v1/Managers/bmc/"
351 "NetworkProtocol/HTTPS/Certificates"))
Marri Devender Rao30215812019-03-18 08:59:21 -0500352 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700353 if (optKeyUsage->size() == 0)
Marri Devender Rao30215812019-03-18 08:59:21 -0500354 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700355 optKeyUsage->push_back("ServerAuthentication");
356 }
357 else if (optKeyUsage->size() == 1)
358 {
359 if ((*optKeyUsage)[0] != "ServerAuthentication")
360 {
361 messages::propertyValueNotInList(
362 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
363 return;
364 }
365 }
366 else
367 {
368 messages::actionParameterNotSupported(
369 asyncResp->res, "KeyUsage", "GenerateCSR");
370 return;
Marri Devender Rao30215812019-03-18 08:59:21 -0500371 }
372 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700373 else if (boost::starts_with(
374 certURI,
375 "/redfish/v1/AccountService/LDAP/Certificates"))
Marri Devender Rao30215812019-03-18 08:59:21 -0500376 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700377 if (optKeyUsage->size() == 0)
378 {
379 optKeyUsage->push_back("ClientAuthentication");
380 }
381 else if (optKeyUsage->size() == 1)
382 {
383 if ((*optKeyUsage)[0] != "ClientAuthentication")
384 {
385 messages::propertyValueNotInList(
386 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
387 return;
388 }
389 }
390 else
391 {
392 messages::actionParameterNotSupported(
393 asyncResp->res, "KeyUsage", "GenerateCSR");
394 return;
395 }
396 }
397
398 // Only allow one CSR matcher at a time so setting retry
399 // time-out and timer expiry to 10 seconds for now.
400 static const int timeOut = 10;
401 if (csrMatcher)
402 {
403 messages::serviceTemporarilyUnavailable(
404 asyncResp->res, std::to_string(timeOut));
Marri Devender Rao30215812019-03-18 08:59:21 -0500405 return;
406 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700407
408 // Make this static so it survives outside this method
409 static boost::asio::steady_timer timeout(*req.ioService);
410 timeout.expires_after(std::chrono::seconds(timeOut));
411 timeout.async_wait([asyncResp](
412 const boost::system::error_code& ec) {
413 csrMatcher = nullptr;
414 if (ec)
415 {
416 // operation_aborted is expected if timer is canceled
417 // before completion.
418 if (ec != boost::asio::error::operation_aborted)
419 {
420 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
421 }
422 return;
423 }
424 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
425 messages::internalError(asyncResp->res);
426 });
427
428 // create a matcher to wait on CSR object
429 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
430 std::string match(
431 "type='signal',"
432 "interface='org.freedesktop.DBus.ObjectManager',"
433 "path='" +
434 objectPath +
435 "',"
436 "member='InterfacesAdded'");
437 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
438 *crow::connections::systemBus, match,
439 [asyncResp, service, objectPath,
440 certURI](sdbusplus::message::message& m) {
441 timeout.cancel();
442 if (m.is_method_error())
443 {
444 BMCWEB_LOG_ERROR << "Dbus method error!!!";
445 messages::internalError(asyncResp->res);
446 return;
447 }
448 std::vector<std::pair<
449 std::string,
450 std::vector<std::pair<std::string,
451 std::variant<std::string>>>>>
452 interfacesProperties;
453 sdbusplus::message::object_path csrObjectPath;
454 m.read(csrObjectPath, interfacesProperties);
455 BMCWEB_LOG_DEBUG << "CSR object added"
456 << csrObjectPath.str;
457 for (auto& interface : interfacesProperties)
458 {
459 if (interface.first ==
460 "xyz.openbmc_project.Certs.CSR")
461 {
462 getCSR(asyncResp, certURI, service, objectPath,
463 csrObjectPath.str);
464 break;
465 }
466 }
467 });
468 crow::connections::systemBus->async_method_call(
469 [asyncResp](const boost::system::error_code& ec,
470 const std::string&) {
471 if (ec)
472 {
473 BMCWEB_LOG_ERROR << "DBUS response error: "
474 << ec.message();
475 messages::internalError(asyncResp->res);
476 return;
477 }
478 },
479 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
480 "GenerateCSR", *optAlternativeNames, *optChallengePassword,
481 city, commonName, *optContactPerson, country, *optEmail,
482 *optGivenName, *optInitials, *optKeyBitLength,
483 *optKeyCurveId, *optKeyPairAlgorithm, *optKeyUsage,
484 organization, organizationalUnit, state, *optSurname,
485 *optUnstructuredName);
486 });
487} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500488
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600489/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500490 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600491 *
492 * @param[in] asyncResp Shared pointer to the response message
493 * @param[in] str Issuer/Subject value in key=value pairs
494 * @param[in] type Issuer/Subject
495 * @return None
496 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500497static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600498 const std::string_view value)
499{
500 // example: O=openbmc-project.xyz,CN=localhost
501 std::string_view::iterator i = value.begin();
502 while (i != value.end())
503 {
504 std::string_view::iterator tokenBegin = i;
505 while (i != value.end() && *i != '=')
506 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530507 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600508 }
509 if (i == value.end())
510 {
511 break;
512 }
Ed Tanous271584a2019-07-09 16:24:22 -0700513 const std::string_view key(tokenBegin,
514 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530515 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600516 tokenBegin = i;
517 while (i != value.end() && *i != ',')
518 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530519 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600520 }
Ed Tanous271584a2019-07-09 16:24:22 -0700521 const std::string_view val(tokenBegin,
522 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600523 if (key == "L")
524 {
525 out["City"] = val;
526 }
527 else if (key == "CN")
528 {
529 out["CommonName"] = val;
530 }
531 else if (key == "C")
532 {
533 out["Country"] = val;
534 }
535 else if (key == "O")
536 {
537 out["Organization"] = val;
538 }
539 else if (key == "OU")
540 {
541 out["OrganizationalUnit"] = val;
542 }
543 else if (key == "ST")
544 {
545 out["State"] = val;
546 }
547 // skip comma character
548 if (i != value.end())
549 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530550 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600551 }
552 }
553}
554
555/**
556 * @brief Retrieve the certificates properties and append to the response
557 * message
558 *
559 * @param[in] asyncResp Shared pointer to the response message
560 * @param[in] objectPath Path of the D-Bus service object
561 * @param[in] certId Id of the certificate
562 * @param[in] certURL URL of the certificate object
563 * @param[in] name name of the certificate
564 * @return None
565 */
566static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800567 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
568 const std::string& objectPath, const std::string& service, long certId,
569 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600570{
571 using PropertyType =
572 std::variant<std::string, uint64_t, std::vector<std::string>>;
573 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
574 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
575 << " certId=" << certId << " certURl=" << certURL;
576 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600577 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500578 const PropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600579 if (ec)
580 {
581 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500582 messages::resourceNotFound(asyncResp->res, name,
583 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600584 return;
585 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600586 asyncResp->res.jsonValue = {
587 {"@odata.id", certURL},
588 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600589 {"Id", std::to_string(certId)},
590 {"Name", name},
591 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500592 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600593 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600594 if (property.first == "CertificateString")
595 {
596 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500597 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600598 std::get_if<std::string>(&property.second);
599 if (value)
600 {
601 asyncResp->res.jsonValue["CertificateString"] = *value;
602 }
603 }
604 else if (property.first == "KeyUsage")
605 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500606 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600607 asyncResp->res.jsonValue["KeyUsage"];
608 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500609 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600610 std::get_if<std::vector<std::string>>(&property.second);
611 if (value)
612 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500613 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600614 {
615 keyUsage.push_back(usage);
616 }
617 }
618 }
619 else if (property.first == "Issuer")
620 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500621 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600622 std::get_if<std::string>(&property.second);
623 if (value)
624 {
625 updateCertIssuerOrSubject(
626 asyncResp->res.jsonValue["Issuer"], *value);
627 }
628 }
629 else if (property.first == "Subject")
630 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500631 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600632 std::get_if<std::string>(&property.second);
633 if (value)
634 {
635 updateCertIssuerOrSubject(
636 asyncResp->res.jsonValue["Subject"], *value);
637 }
638 }
639 else if (property.first == "ValidNotAfter")
640 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500641 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600642 std::get_if<uint64_t>(&property.second);
643 if (value)
644 {
645 std::time_t time = static_cast<std::time_t>(*value);
646 asyncResp->res.jsonValue["ValidNotAfter"] =
647 crow::utility::getDateTime(time);
648 }
649 }
650 else if (property.first == "ValidNotBefore")
651 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500652 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600653 std::get_if<uint64_t>(&property.second);
654 if (value)
655 {
656 std::time_t time = static_cast<std::time_t>(*value);
657 asyncResp->res.jsonValue["ValidNotBefore"] =
658 crow::utility::getDateTime(time);
659 }
660 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600661 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600662 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600663 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600664 service, objectPath, certs::dbusPropIntf, "GetAll",
665 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600666}
667
668using GetObjectType =
669 std::vector<std::pair<std::string, std::vector<std::string>>>;
670
671/**
672 * Action to replace an existing certificate
673 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700674inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600675{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700676 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/Actions/"
677 "CertificateService.ReplaceCertificate/")
Abhishek Patel72048782021-06-02 09:53:24 -0500678 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700679 .methods(
680 boost::beast::http::verb::
681 post)([](const crow::Request& req,
682 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
683 std::string certificate;
684 nlohmann::json certificateUri;
685 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600686
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700687 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
688 certificate, "CertificateUri",
689 certificateUri, "CertificateType",
690 certificateType))
691 {
692 BMCWEB_LOG_ERROR << "Required parameters are missing";
693 messages::internalError(asyncResp->res);
694 return;
695 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800696
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700697 if (!certificateType)
698 {
699 // should never happen, but it never hurts to be paranoid.
700 return;
701 }
702 if (certificateType != "PEM")
703 {
704 messages::actionParameterNotSupported(
705 asyncResp->res, "CertificateType", "ReplaceCertificate");
706 return;
707 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600708
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700709 std::string certURI;
710 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
711 "@odata.id", certURI))
712 {
713 messages::actionParameterMissing(
714 asyncResp->res, "ReplaceCertificate", "CertificateUri");
715 return;
716 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600717
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700718 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
719 long id = getIDFromURL(certURI);
720 if (id < 0)
721 {
722 messages::actionParameterValueFormatError(
723 asyncResp->res, certURI, "CertificateUri",
724 "ReplaceCertificate");
725 return;
726 }
727 std::string objectPath;
728 std::string name;
729 std::string service;
730 if (boost::starts_with(certURI,
731 "/redfish/v1/Managers/bmc/NetworkProtocol/"
732 "HTTPS/Certificates/"))
733 {
734 objectPath = std::string(certs::httpsObjectPath) + "/" +
735 std::to_string(id);
736 name = "HTTPS certificate";
737 service = certs::httpsServiceName;
738 }
739 else if (boost::starts_with(
740 certURI,
741 "/redfish/v1/AccountService/LDAP/Certificates/"))
742 {
743 objectPath = std::string(certs::ldapObjectPath) + "/" +
744 std::to_string(id);
745 name = "LDAP certificate";
746 service = certs::ldapServiceName;
747 }
748 else if (boost::starts_with(
749 certURI,
750 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
751 {
752 objectPath = std::string(certs::authorityObjectPath) + "/" +
753 std::to_string(id);
754 name = "TrustStore certificate";
755 service = certs::authorityServiceName;
756 }
757 else
758 {
759 messages::actionParameterNotSupported(
760 asyncResp->res, "CertificateUri", "ReplaceCertificate");
761 return;
762 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600763
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700764 std::shared_ptr<CertificateFile> certFile =
765 std::make_shared<CertificateFile>(certificate);
766 crow::connections::systemBus->async_method_call(
767 [asyncResp, certFile, objectPath, service, certURI, id,
768 name](const boost::system::error_code ec) {
769 if (ec)
770 {
771 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
772 messages::resourceNotFound(asyncResp->res, name,
773 std::to_string(id));
774 return;
775 }
776 getCertificateProperties(asyncResp, objectPath, service, id,
777 certURI, name);
778 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
779 << certFile->getCertFilePath();
780 },
781 service, objectPath, certs::certReplaceIntf, "Replace",
782 certFile->getCertFilePath());
783 });
784} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600785
786/**
787 * Certificate resource describes a certificate used to prove the identity
788 * of a component, account or service.
789 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700790
791inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600792{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700793 BMCWEB_ROUTE(
794 app,
795 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Abhishek Patel72048782021-06-02 09:53:24 -0500796 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700797 .methods(
798 boost::beast::http::verb::
799 get)([](const crow::Request& req,
800 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
801 const std::string& param) -> void {
802 if (param.empty())
803 {
804 messages::internalError(asyncResp->res);
805 return;
806 }
807 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600808
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700809 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
810 << std::to_string(id);
811 std::string certURL =
812 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
813 std::to_string(id);
814 std::string objectPath = certs::httpsObjectPath;
815 objectPath += "/";
816 objectPath += std::to_string(id);
817 getCertificateProperties(asyncResp, objectPath,
818 certs::httpsServiceName, id, certURL,
819 "HTTPS Certificate");
820 });
821}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600822
823/**
824 * Collection of HTTPS certificates
825 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700826inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600827{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700828 BMCWEB_ROUTE(app,
829 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Abhishek Patel72048782021-06-02 09:53:24 -0500830 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700831 .methods(
832 boost::beast::http::verb::
833 get)([](const crow::Request&,
834 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
835 asyncResp->res.jsonValue = {
836 {"@odata.id",
837 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
838 {"@odata.type", "#CertificateCollection.CertificateCollection"},
839 {"Name", "HTTPS Certificates Collection"},
840 {"Description", "A Collection of HTTPS certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +0800841
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700842 crow::connections::systemBus->async_method_call(
843 [asyncResp](const boost::system::error_code ec,
844 const ManagedObjectType& certs) {
845 if (ec)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600846 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700847 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
848 messages::internalError(asyncResp->res);
849 return;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600850 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700851 nlohmann::json& members =
852 asyncResp->res.jsonValue["Members"];
853 members = nlohmann::json::array();
854 for (const auto& cert : certs)
855 {
856 long id = getIDFromURL(cert.first.str);
857 if (id >= 0)
858 {
859 members.push_back(
860 {{"@odata.id",
861 "/redfish/v1/Managers/bmc/"
862 "NetworkProtocol/HTTPS/Certificates/" +
863 std::to_string(id)}});
864 }
865 }
866 asyncResp->res.jsonValue["Members@odata.count"] =
867 members.size();
868 },
869 certs::httpsServiceName, certs::httpsObjectPath,
870 certs::dbusObjManagerIntf, "GetManagedObjects");
871 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600872
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700873 BMCWEB_ROUTE(app,
874 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Abhishek Patel72048782021-06-02 09:53:24 -0500875 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700876 .methods(boost::beast::http::verb::post)(
877 [](const crow::Request& req,
878 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
879 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
zhanghch058d1b46d2021-04-01 11:18:24 +0800880
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700881 asyncResp->res.jsonValue = {
882 {"Name", "HTTPS Certificate"},
883 {"Description", "HTTPS Certificate"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600884
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700885 std::string certFileBody =
886 getCertificateFromReqBody(asyncResp, req);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200887
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700888 if (certFileBody.empty())
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600889 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700890 BMCWEB_LOG_ERROR
891 << "Cannot get certificate from request body.";
892 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600893 return;
894 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700895
896 std::shared_ptr<CertificateFile> certFile =
897 std::make_shared<CertificateFile>(certFileBody);
898
899 crow::connections::systemBus->async_method_call(
900 [asyncResp, certFile](const boost::system::error_code ec,
901 const std::string& objectPath) {
902 if (ec)
903 {
904 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
905 messages::internalError(asyncResp->res);
906 return;
907 }
908 long certId = getIDFromURL(objectPath);
909 if (certId < 0)
910 {
911 BMCWEB_LOG_ERROR << "Invalid objectPath value"
912 << objectPath;
913 messages::internalError(asyncResp->res);
914 return;
915 }
916 std::string certURL =
917 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
918 "Certificates/" +
919 std::to_string(certId);
920 getCertificateProperties(
921 asyncResp, objectPath, certs::httpsServiceName,
922 certId, certURL, "HTTPS Certificate");
923 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
924 << certFile->getCertFilePath();
925 },
926 certs::httpsServiceName, certs::httpsObjectPath,
927 certs::certInstallIntf, "Install",
928 certFile->getCertFilePath());
929 });
930} // requestRoutesHTTPSCertificateCollection
931
932/**
933 * @brief Retrieve the certificates installed list and append to the
934 * response
935 *
936 * @param[in] asyncResp Shared pointer to the response message
937 * @param[in] certURL Path of the certificate object
938 * @param[in] path Path of the D-Bus service object
939 * @return None
940 */
941void getCertificateLocations(
942 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
943 const std::string& certURL, const std::string& path,
944 const std::string& service)
945{
946 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
947 << " Path=" << path << " service= " << service;
948 crow::connections::systemBus->async_method_call(
949 [asyncResp, certURL](const boost::system::error_code ec,
950 const ManagedObjectType& certs) {
951 if (ec)
952 {
953 BMCWEB_LOG_WARNING
954 << "Certificate collection query failed: " << ec
955 << ", skipping " << certURL;
956 return;
957 }
958 nlohmann::json& links =
959 asyncResp->res.jsonValue["Links"]["Certificates"];
960 for (auto& cert : certs)
961 {
962 long id = getIDFromURL(cert.first.str);
963 if (id >= 0)
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200964 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700965 links.push_back(
966 {{"@odata.id", certURL + std::to_string(id)}});
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200967 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700968 }
969 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
970 links.size();
971 },
972 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
973}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600974
975/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600976 * The certificate location schema defines a resource that an administrator
977 * can use in order to locate all certificates installed on a given service.
978 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700979inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600980{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700981 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Abhishek Patel72048782021-06-02 09:53:24 -0500982 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700983 .methods(
984 boost::beast::http::verb::
985 get)([](const crow::Request&,
986 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
987 asyncResp->res.jsonValue = {
988 {"@odata.id",
989 "/redfish/v1/CertificateService/CertificateLocations"},
990 {"@odata.type",
991 "#CertificateLocations.v1_0_0.CertificateLocations"},
992 {"Name", "Certificate Locations"},
993 {"Id", "CertificateLocations"},
994 {"Description",
995 "Defines a resource that an administrator can use in order to "
996 "locate all certificates installed on a given service"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600997
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700998 nlohmann::json& links =
999 asyncResp->res.jsonValue["Links"]["Certificates"];
1000 links = nlohmann::json::array();
1001 getCertificateLocations(
1002 asyncResp,
1003 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
1004 certs::httpsObjectPath, certs::httpsServiceName);
1005 getCertificateLocations(
1006 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/",
1007 certs::ldapObjectPath, certs::ldapServiceName);
1008 getCertificateLocations(
1009 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1010 certs::authorityObjectPath, certs::authorityServiceName);
1011 });
1012}
1013// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001014
1015/**
1016 * Collection of LDAP certificates
1017 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001018inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001019{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001020 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Abhishek Patel72048782021-06-02 09:53:24 -05001021 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001022 .methods(boost::beast::http::verb::get)(
1023 [](const crow::Request&,
1024 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1025 asyncResp->res.jsonValue = {
1026 {"@odata.id",
1027 "/redfish/v1/AccountService/LDAP/Certificates"},
1028 {"@odata.type",
1029 "#CertificateCollection.CertificateCollection"},
1030 {"Name", "LDAP Certificates Collection"},
1031 {"Description",
1032 "A Collection of LDAP certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001033
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001034 crow::connections::systemBus->async_method_call(
1035 [asyncResp](const boost::system::error_code ec,
1036 const ManagedObjectType& certs) {
1037 nlohmann::json& members =
1038 asyncResp->res.jsonValue["Members"];
1039 nlohmann::json& count =
1040 asyncResp->res.jsonValue["Members@odata.count"];
1041 members = nlohmann::json::array();
1042 count = 0;
1043 if (ec)
1044 {
1045 BMCWEB_LOG_WARNING
1046 << "LDAP certificate query failed: " << ec;
1047 return;
1048 }
1049 for (const auto& cert : certs)
1050 {
1051 long id = getIDFromURL(cert.first.str);
1052 if (id >= 0)
1053 {
1054 members.push_back(
1055 {{"@odata.id", "/redfish/v1/AccountService/"
1056 "LDAP/Certificates/" +
1057 std::to_string(id)}});
1058 }
1059 }
1060 count = members.size();
1061 },
1062 certs::ldapServiceName, certs::ldapObjectPath,
1063 certs::dbusObjManagerIntf, "GetManagedObjects");
1064 });
1065
1066 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Abhishek Patel72048782021-06-02 09:53:24 -05001067 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001068 .methods(boost::beast::http::verb::post)(
1069 [](const crow::Request& req,
1070 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1071 std::string certFileBody =
1072 getCertificateFromReqBody(asyncResp, req);
1073
1074 if (certFileBody.empty())
Marri Devender Rao37cce912019-02-20 01:05:22 -06001075 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001076 BMCWEB_LOG_ERROR
1077 << "Cannot get certificate from request body.";
1078 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001079 return;
1080 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001081
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001082 std::shared_ptr<CertificateFile> certFile =
1083 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001084
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001085 crow::connections::systemBus->async_method_call(
1086 [asyncResp, certFile](const boost::system::error_code ec,
1087 const std::string& objectPath) {
1088 if (ec)
1089 {
1090 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1091 messages::internalError(asyncResp->res);
1092 return;
1093 }
1094 long certId = getIDFromURL(objectPath);
1095 if (certId < 0)
1096 {
1097 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1098 << objectPath;
1099 messages::internalError(asyncResp->res);
1100 return;
1101 }
1102 std::string certURL =
1103 "/redfish/v1/AccountService/LDAP/Certificates/" +
1104 std::to_string(certId);
1105 getCertificateProperties(asyncResp, objectPath,
1106 certs::ldapServiceName, certId,
1107 certURL, "LDAP Certificate");
1108 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1109 << certFile->getCertFilePath();
1110 },
1111 certs::ldapServiceName, certs::ldapObjectPath,
1112 certs::certInstallIntf, "Install",
1113 certFile->getCertFilePath());
1114 });
1115} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001116
1117/**
1118 * Certificate resource describes a certificate used to prove the identity
1119 * of a component, account or service.
1120 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001121inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001122{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001123 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Abhishek Patel72048782021-06-02 09:53:24 -05001124 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001125 .methods(boost::beast::http::verb::get)(
1126 [](const crow::Request& req,
1127 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1128 const std::string&) {
1129 long id = getIDFromURL(req.url);
1130 if (id < 0)
1131 {
1132 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1133 messages::internalError(asyncResp->res);
1134 return;
1135 }
1136 BMCWEB_LOG_DEBUG << "LDAP Certificate ID="
1137 << std::to_string(id);
1138 std::string certURL =
1139 "/redfish/v1/AccountService/LDAP/Certificates/" +
1140 std::to_string(id);
1141 std::string objectPath = certs::ldapObjectPath;
1142 objectPath += "/";
1143 objectPath += std::to_string(id);
1144 getCertificateProperties(asyncResp, objectPath,
1145 certs::ldapServiceName, id, certURL,
1146 "LDAP Certificate");
1147 });
1148} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001149/**
1150 * Collection of TrustStoreCertificate certificates
1151 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001152inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001153{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001154 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Abhishek Patel72048782021-06-02 09:53:24 -05001155 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001156 .methods(boost::beast::http::verb::get)(
1157 [](const crow::Request&,
1158 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1159 asyncResp->res.jsonValue = {
1160 {"@odata.id",
1161 "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1162 {"@odata.type",
1163 "#CertificateCollection.CertificateCollection"},
1164 {"Name", "TrustStore Certificates Collection"},
1165 {"Description",
1166 "A Collection of TrustStore certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001167
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001168 crow::connections::systemBus->async_method_call(
1169 [asyncResp](const boost::system::error_code ec,
1170 const ManagedObjectType& certs) {
1171 if (ec)
1172 {
1173 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1174 messages::internalError(asyncResp->res);
1175 return;
1176 }
1177 nlohmann::json& members =
1178 asyncResp->res.jsonValue["Members"];
1179 members = nlohmann::json::array();
1180 for (const auto& cert : certs)
1181 {
1182 long id = getIDFromURL(cert.first.str);
1183 if (id >= 0)
1184 {
1185 members.push_back(
1186 {{"@odata.id", "/redfish/v1/Managers/bmc/"
1187 "Truststore/Certificates/" +
1188 std::to_string(id)}});
1189 }
1190 }
1191 asyncResp->res.jsonValue["Members@odata.count"] =
1192 members.size();
1193 },
1194 certs::authorityServiceName, certs::authorityObjectPath,
1195 certs::dbusObjManagerIntf, "GetManagedObjects");
1196 });
1197
1198 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Abhishek Patel72048782021-06-02 09:53:24 -05001199 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001200 .methods(boost::beast::http::verb::post)(
1201 [](const crow::Request& req,
1202 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1203 std::string certFileBody =
1204 getCertificateFromReqBody(asyncResp, req);
1205
1206 if (certFileBody.empty())
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001207 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001208 BMCWEB_LOG_ERROR
1209 << "Cannot get certificate from request body.";
1210 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001211 return;
1212 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001213
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001214 std::shared_ptr<CertificateFile> certFile =
1215 std::make_shared<CertificateFile>(certFileBody);
1216 crow::connections::systemBus->async_method_call(
1217 [asyncResp, certFile](const boost::system::error_code ec,
1218 const std::string& objectPath) {
1219 if (ec)
1220 {
1221 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1222 messages::internalError(asyncResp->res);
1223 return;
1224 }
1225 long certId = getIDFromURL(objectPath);
1226 if (certId < 0)
1227 {
1228 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1229 << objectPath;
1230 messages::internalError(asyncResp->res);
1231 return;
1232 }
1233 std::string certURL = "/redfish/v1/Managers/bmc/"
1234 "Truststore/Certificates/" +
1235 std::to_string(certId);
zhanghch058d1b46d2021-04-01 11:18:24 +08001236
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001237 getCertificateProperties(
1238 asyncResp, objectPath, certs::authorityServiceName,
1239 certId, certURL, "TrustStore Certificate");
1240 BMCWEB_LOG_DEBUG
1241 << "TrustStore certificate install file="
1242 << certFile->getCertFilePath();
1243 },
1244 certs::authorityServiceName, certs::authorityObjectPath,
1245 certs::certInstallIntf, "Install",
1246 certFile->getCertFilePath());
1247 });
1248} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001249
1250/**
1251 * Certificate resource describes a certificate used to prove the identity
1252 * of a component, account or service.
1253 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001254inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001255{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001256 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Abhishek Patel72048782021-06-02 09:53:24 -05001257 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001258 .methods(boost::beast::http::verb::get)(
1259 [](const crow::Request& req,
1260 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1261 const std::string&) {
1262 long id = getIDFromURL(req.url);
1263 if (id < 0)
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001264 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001265 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1266 messages::internalError(asyncResp->res);
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001267 return;
1268 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001269 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1270 << std::to_string(id);
1271 std::string certURL =
1272 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1273 std::to_string(id);
1274 std::string objectPath = certs::authorityObjectPath;
1275 objectPath += "/";
1276 objectPath += std::to_string(id);
1277 getCertificateProperties(asyncResp, objectPath,
1278 certs::authorityServiceName, id,
1279 certURL, "TrustStore Certificate");
1280 });
1281
1282 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Abhishek Patel72048782021-06-02 09:53:24 -05001283 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001284 .methods(boost::beast::http::verb::delete_)(
1285 [](const crow::Request& req,
1286 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1287 const std::string& param) {
1288 if (param.empty())
1289 {
1290 messages::internalError(asyncResp->res);
1291 return;
1292 }
1293
1294 long id = getIDFromURL(req.url);
1295 if (id < 0)
1296 {
1297 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1298 messages::resourceNotFound(asyncResp->res,
1299 "TrustStore Certificate",
1300 std::string(req.url));
1301 return;
1302 }
1303 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1304 << std::to_string(id);
1305 std::string certPath = certs::authorityObjectPath;
1306 certPath += "/";
1307 certPath += std::to_string(id);
1308
1309 crow::connections::systemBus->async_method_call(
1310 [asyncResp, id](const boost::system::error_code ec) {
1311 if (ec)
1312 {
1313 messages::resourceNotFound(asyncResp->res,
1314 "TrustStore Certificate",
1315 std::to_string(id));
1316 return;
1317 }
1318 BMCWEB_LOG_INFO << "Certificate deleted";
1319 asyncResp->res.result(
1320 boost::beast::http::status::no_content);
1321 },
1322 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1323 "Delete");
1324 });
1325} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001326} // namespace redfish