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