blob: a4ecbf048c61e2879f11ee3e42d93438dd4533f1 [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>
Jiaqing Zhao90d2d1e2022-04-13 17:01:57 +08006#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -08007#include <dbus_utility.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -07008#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -07009#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050010
Marri Devender Rao5968cae2019-01-21 10:27:12 -060011namespace redfish
12{
13namespace certs
14{
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";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050021constexpr 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";
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +080027constexpr char const* baseObjectPath = "/xyz/openbmc_project/certs";
28constexpr char const* httpsObjectPath =
29 "/xyz/openbmc_project/certs/server/https";
30constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050031constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050032 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060033} // namespace certs
34
35/**
36 * The Certificate schema defines a Certificate Service which represents the
37 * actions available to manage certificates and links to where certificates
38 * are installed.
39 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060040
John Edward Broadbent7e860f12021-04-08 15:57:16 -070041// TODO: Issue#61 No entries are available for Certificate
42// service at https://www.dmtf.org/standards/redfish
43// "redfish standard registries". Need to modify after DMTF
44// publish Privilege details for certificate service
45
46inline void requestRoutesCertificateService(App& app)
47{
48 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
Ed Tanoused398212021-06-09 17:05:54 -070049 .privileges(redfish::privileges::getCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -070050 .methods(boost::beast::http::verb::get)(
51 [&app](const crow::Request& req,
52 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +000053 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -070054 {
55 return;
56 }
Ed Tanous14766872022-03-15 10:44:42 -070057
Ed Tanous002d39b2022-05-31 08:59:27 -070058 asyncResp->res.jsonValue["@odata.type"] =
59 "#CertificateService.v1_0_0.CertificateService";
60 asyncResp->res.jsonValue["@odata.id"] =
61 "/redfish/v1/CertificateService";
62 asyncResp->res.jsonValue["Id"] = "CertificateService";
63 asyncResp->res.jsonValue["Name"] = "Certificate Service";
64 asyncResp->res.jsonValue["Description"] =
65 "Actions available to manage certificates";
66 // /redfish/v1/CertificateService/CertificateLocations is something
67 // only ConfigureManager can access then only display when the user
68 // has permissions ConfigureManager
69 Privileges effectiveUserPrivileges =
70 redfish::getUserPrivileges(req.userRole);
71 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
72 effectiveUserPrivileges))
73 {
74 asyncResp->res.jsonValue["CertificateLocations"]["@odata.id"] =
75 "/redfish/v1/CertificateService/CertificateLocations";
76 }
77 asyncResp->res
78 .jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = {
79 {"target",
80 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"},
81 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
82 asyncResp->res
83 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
84 {"target",
85 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR"}};
Abhishek Patel72048782021-06-02 09:53:24 -050086 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070087} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060088
Marri Devender Rao5968cae2019-01-21 10:27:12 -060089/**
90 * @brief Find the ID specified in the URL
91 * Finds the numbers specified after the last "/" in the URL and returns.
92 * @param[in] path URL
93 * @return -1 on failure and number on success
94 */
Ed Tanous23a21a12020-07-25 04:45:05 +000095inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060096{
Ed Tanousf23b7292020-10-15 09:41:17 -070097 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -060098 if (found == std::string::npos)
99 {
100 return -1;
101 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200102
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600103 if ((found + 1) < url.length())
104 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600105 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200106
107 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600108 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200109
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600110 return -1;
111}
112
zhanghch058d1b46d2021-04-01 11:18:24 +0800113inline std::string getCertificateFromReqBody(
114 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
115 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200116{
117 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
118
119 if (reqJson.is_discarded())
120 {
121 // We did not receive JSON request, proceed as it is RAW data
122 return req.body;
123 }
124
125 std::string certificate;
126 std::optional<std::string> certificateType = "PEM";
127
Willy Tu15ed6782021-12-14 11:03:16 -0800128 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
129 certificate, "CertificateType",
130 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200131 {
132 BMCWEB_LOG_ERROR << "Required parameters are missing";
133 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700134 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200135 }
136
137 if (*certificateType != "PEM")
138 {
139 messages::propertyValueNotInList(asyncResp->res, *certificateType,
140 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700141 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200142 }
143
144 return certificate;
145}
146
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600147/**
148 * Class to create a temporary certificate file for uploading to system
149 */
150class CertificateFile
151{
152 public:
153 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500154 CertificateFile(const CertificateFile&) = delete;
155 CertificateFile& operator=(const CertificateFile&) = delete;
156 CertificateFile(CertificateFile&&) = delete;
157 CertificateFile& operator=(CertificateFile&&) = delete;
Ed Tanous4e23a442022-06-06 09:57:26 -0700158 explicit CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600159 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700160 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700161 'e', 'r', 't', 's', '.', 'X',
162 'X', 'X', 'X', 'X', 'X', '\0'};
163 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800164 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600165 {
166 certDirectory = tempDirectory;
167 certificateFile = certDirectory / "cert.pem";
168 std::ofstream out(certificateFile, std::ofstream::out |
169 std::ofstream::binary |
170 std::ofstream::trunc);
171 out << certString;
172 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800173 BMCWEB_LOG_DEBUG << "Creating certificate file"
174 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600175 }
176 }
177 ~CertificateFile()
178 {
179 if (std::filesystem::exists(certDirectory))
180 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800181 BMCWEB_LOG_DEBUG << "Removing certificate file"
182 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000183 std::error_code ec;
184 std::filesystem::remove_all(certDirectory, ec);
185 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600186 {
187 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800188 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600189 }
190 }
191 }
192 std::string getCertFilePath()
193 {
194 return certificateFile;
195 }
196
197 private:
198 std::filesystem::path certificateFile;
199 std::filesystem::path certDirectory;
200};
201
Marri Devender Rao30215812019-03-18 08:59:21 -0500202static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
203/**
204 * @brief Read data from CSR D-bus object and set to response
205 *
206 * @param[in] asyncResp Shared pointer to the response message
207 * @param[in] certURI Link to certifiate collection URI
208 * @param[in] service D-Bus service name
209 * @param[in] certObjPath certificate D-Bus object path
210 * @param[in] csrObjPath CSR D-Bus object path
211 * @return None
212 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800213static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500214 const std::string& certURI, const std::string& service,
215 const std::string& certObjPath,
216 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500217{
218 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
219 << " CSRObjectPath=" << csrObjPath
220 << " service=" << service;
221 crow::connections::systemBus->async_method_call(
222 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500223 const std::string& csr) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700224 if (ec)
225 {
226 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
227 messages::internalError(asyncResp->res);
228 return;
229 }
230 if (csr.empty())
231 {
232 BMCWEB_LOG_ERROR << "CSR read is empty";
233 messages::internalError(asyncResp->res);
234 return;
235 }
236 asyncResp->res.jsonValue["CSRString"] = csr;
237 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
238 certURI;
Marri Devender Rao30215812019-03-18 08:59:21 -0500239 },
240 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
241}
242
243/**
244 * Action to Generate CSR
245 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700246inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500247{
George Liu0fda0f12021-11-16 10:06:17 +0800248 BMCWEB_ROUTE(
249 app,
250 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
Abhishek Patel5344ab82021-07-31 17:42:09 -0500251 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700252 .methods(boost::beast::http::verb::post)(
253 [&app](const crow::Request& req,
254 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000255 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700256 {
257 return;
258 }
259 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500260
Ed Tanous002d39b2022-05-31 08:59:27 -0700261 // Required parameters
262 std::string city;
263 std::string commonName;
264 std::string country;
265 std::string organization;
266 std::string organizationalUnit;
267 std::string state;
268 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800269
Ed Tanous002d39b2022-05-31 08:59:27 -0700270 // Optional parameters
271 std::optional<std::vector<std::string>> optAlternativeNames =
272 std::vector<std::string>();
273 std::optional<std::string> optContactPerson = "";
274 std::optional<std::string> optChallengePassword = "";
275 std::optional<std::string> optEmail = "";
276 std::optional<std::string> optGivenName = "";
277 std::optional<std::string> optInitials = "";
278 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
279 std::optional<std::string> optKeyCurveId = "secp384r1";
280 std::optional<std::string> optKeyPairAlgorithm = "EC";
281 std::optional<std::vector<std::string>> optKeyUsage =
282 std::vector<std::string>();
283 std::optional<std::string> optSurname = "";
284 std::optional<std::string> optUnstructuredName = "";
285 if (!json_util::readJsonAction(
286 req, asyncResp->res, "City", city, "CommonName", commonName,
287 "ContactPerson", optContactPerson, "Country", country,
288 "Organization", organization, "OrganizationalUnit",
289 organizationalUnit, "State", state, "CertificateCollection",
290 certificateCollection, "AlternativeNames", optAlternativeNames,
291 "ChallengePassword", optChallengePassword, "Email", optEmail,
292 "GivenName", optGivenName, "Initials", optInitials,
293 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
294 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
295 optKeyUsage, "Surname", optSurname, "UnstructuredName",
296 optUnstructuredName))
297 {
298 return;
299 }
George Liu0fda0f12021-11-16 10:06:17 +0800300
Ed Tanous002d39b2022-05-31 08:59:27 -0700301 // bmcweb has no way to store or decode a private key challenge
302 // password, which will likely cause bmcweb to crash on startup
303 // if this is not set on a post so not allowing the user to set
304 // value
305 if (!optChallengePassword->empty())
306 {
307 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
308 "ChallengePassword");
309 return;
310 }
George Liu0fda0f12021-11-16 10:06:17 +0800311
Ed Tanous002d39b2022-05-31 08:59:27 -0700312 std::string certURI;
313 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
314 "@odata.id", certURI))
315 {
316 return;
317 }
George Liu0fda0f12021-11-16 10:06:17 +0800318
Ed Tanous002d39b2022-05-31 08:59:27 -0700319 std::string objectPath;
320 std::string service;
321 if (boost::starts_with(
322 certURI,
323 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
324 {
325 objectPath = certs::httpsObjectPath;
326 service = certs::httpsServiceName;
327 }
328 else if (boost::starts_with(
329 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
330 {
331 objectPath = certs::ldapObjectPath;
332 service = certs::ldapServiceName;
333 }
334 else
335 {
336 messages::actionParameterNotSupported(
337 asyncResp->res, "CertificateCollection", "GenerateCSR");
338 return;
339 }
340
341 // supporting only EC and RSA algorithm
342 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
343 {
344 messages::actionParameterNotSupported(
345 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
346 return;
347 }
348
349 // supporting only 2048 key bit length for RSA algorithm due to
350 // time consumed in generating private key
351 if (*optKeyPairAlgorithm == "RSA" &&
352 *optKeyBitLength != rsaKeyBitLength)
353 {
354 messages::propertyValueNotInList(asyncResp->res,
355 std::to_string(*optKeyBitLength),
356 "KeyBitLength");
357 return;
358 }
359
360 // validate KeyUsage supporting only 1 type based on URL
361 if (boost::starts_with(
362 certURI,
363 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
364 {
365 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800366 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700367 optKeyUsage->push_back("ServerAuthentication");
George Liu0fda0f12021-11-16 10:06:17 +0800368 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700369 else if (optKeyUsage->size() == 1)
George Liu0fda0f12021-11-16 10:06:17 +0800370 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700371 if ((*optKeyUsage)[0] != "ServerAuthentication")
372 {
373 messages::propertyValueNotInList(
374 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
375 return;
376 }
George Liu0fda0f12021-11-16 10:06:17 +0800377 }
378 else
379 {
380 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700381 asyncResp->res, "KeyUsage", "GenerateCSR");
George Liu0fda0f12021-11-16 10:06:17 +0800382 return;
383 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700384 }
385 else if (boost::starts_with(
386 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
387 {
388 if (optKeyUsage->empty())
389 {
390 optKeyUsage->push_back("ClientAuthentication");
391 }
392 else if (optKeyUsage->size() == 1)
393 {
394 if ((*optKeyUsage)[0] != "ClientAuthentication")
395 {
396 messages::propertyValueNotInList(
397 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
398 return;
399 }
400 }
401 else
George Liu0fda0f12021-11-16 10:06:17 +0800402 {
403 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700404 asyncResp->res, "KeyUsage", "GenerateCSR");
405 return;
406 }
407 }
408
409 // Only allow one CSR matcher at a time so setting retry
410 // time-out and timer expiry to 10 seconds for now.
411 static const int timeOut = 10;
412 if (csrMatcher)
413 {
414 messages::serviceTemporarilyUnavailable(asyncResp->res,
415 std::to_string(timeOut));
416 return;
417 }
418
419 // Make this static so it survives outside this method
420 static boost::asio::steady_timer timeout(*req.ioService);
421 timeout.expires_after(std::chrono::seconds(timeOut));
422 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
423 csrMatcher = nullptr;
424 if (ec)
425 {
426 // operation_aborted is expected if timer is canceled
427 // before completion.
428 if (ec != boost::asio::error::operation_aborted)
429 {
430 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
431 }
432 return;
433 }
434 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
435 messages::internalError(asyncResp->res);
436 });
437
438 // create a matcher to wait on CSR object
439 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
440 std::string match("type='signal',"
441 "interface='org.freedesktop.DBus.ObjectManager',"
442 "path='" +
443 objectPath +
444 "',"
445 "member='InterfacesAdded'");
446 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
447 *crow::connections::systemBus, match,
448 [asyncResp, service, objectPath,
449 certURI](sdbusplus::message::message& m) {
450 timeout.cancel();
451 if (m.is_method_error())
452 {
453 BMCWEB_LOG_ERROR << "Dbus method error!!!";
454 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800455 return;
456 }
457
Ed Tanous002d39b2022-05-31 08:59:27 -0700458 dbus::utility::DBusInteracesMap interfacesProperties;
459
460 sdbusplus::message::object_path csrObjectPath;
461 m.read(csrObjectPath, interfacesProperties);
462 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
Ed Tanous02cad962022-06-30 16:50:15 -0700463 for (const auto& interface : interfacesProperties)
George Liu0fda0f12021-11-16 10:06:17 +0800464 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700465 if (interface.first == "xyz.openbmc_project.Certs.CSR")
466 {
467 getCSR(asyncResp, certURI, service, objectPath,
468 csrObjectPath.str);
469 break;
470 }
471 }
472 });
473 crow::connections::systemBus->async_method_call(
474 [asyncResp](const boost::system::error_code ec,
475 const std::string&) {
476 if (ec)
477 {
478 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
479 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800480 return;
481 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700482 },
483 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
484 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
485 commonName, *optContactPerson, country, *optEmail, *optGivenName,
486 *optInitials, *optKeyBitLength, *optKeyCurveId,
487 *optKeyPairAlgorithm, *optKeyUsage, organization,
488 organizationalUnit, state, *optSurname, *optUnstructuredName);
George Liu0fda0f12021-11-16 10:06:17 +0800489 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700490} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500491
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600492/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500493 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600494 *
495 * @param[in] asyncResp Shared pointer to the response message
496 * @param[in] str Issuer/Subject value in key=value pairs
497 * @param[in] type Issuer/Subject
498 * @return None
499 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500500static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600501 const std::string_view value)
502{
503 // example: O=openbmc-project.xyz,CN=localhost
504 std::string_view::iterator i = value.begin();
505 while (i != value.end())
506 {
507 std::string_view::iterator tokenBegin = i;
508 while (i != value.end() && *i != '=')
509 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530510 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600511 }
512 if (i == value.end())
513 {
514 break;
515 }
Ed Tanous271584a2019-07-09 16:24:22 -0700516 const std::string_view key(tokenBegin,
517 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530518 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600519 tokenBegin = i;
520 while (i != value.end() && *i != ',')
521 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530522 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600523 }
Ed Tanous271584a2019-07-09 16:24:22 -0700524 const std::string_view val(tokenBegin,
525 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600526 if (key == "L")
527 {
528 out["City"] = val;
529 }
530 else if (key == "CN")
531 {
532 out["CommonName"] = val;
533 }
534 else if (key == "C")
535 {
536 out["Country"] = val;
537 }
538 else if (key == "O")
539 {
540 out["Organization"] = val;
541 }
542 else if (key == "OU")
543 {
544 out["OrganizationalUnit"] = val;
545 }
546 else if (key == "ST")
547 {
548 out["State"] = val;
549 }
550 // skip comma character
551 if (i != value.end())
552 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530553 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600554 }
555 }
556}
557
558/**
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800559 * @brief Retrieve the installed certificate list
560 *
561 * @param[in] asyncResp Shared pointer to the response message
562 * @param[in] basePath DBus object path to search
563 * @param[in] listPtr Json pointer to the list in asyncResp
564 * @param[in] countPtr Json pointer to the count in asyncResp
565 * @return None
566 */
567static void
568 getCertificateList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
569 const std::string& basePath,
570 const nlohmann::json::json_pointer& listPtr,
571 const nlohmann::json::json_pointer& countPtr)
572{
573 crow::connections::systemBus->async_method_call(
574 [asyncResp, listPtr, countPtr](
575 const boost::system::error_code ec,
576 const dbus::utility::MapperGetSubTreePathsResponse& certPaths) {
577 if (ec)
578 {
579 BMCWEB_LOG_ERROR << "Certificate collection query failed: " << ec;
580 messages::internalError(asyncResp->res);
581 return;
582 }
583
584 nlohmann::json& links = asyncResp->res.jsonValue[listPtr];
585 links = nlohmann::json::array();
586 for (const auto& certPath : certPaths)
587 {
588 sdbusplus::message::object_path objPath(certPath);
589 std::string certId = objPath.filename();
590 if (certId.empty())
591 {
592 BMCWEB_LOG_ERROR << "Invalid certificate objPath " << certPath;
593 continue;
594 }
595
596 boost::urls::url certURL;
597 if (objPath.parent_path() == certs::httpsObjectPath)
598 {
599 certURL = crow::utility::urlFromPieces(
600 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
601 "HTTPS", "Certificates", certId);
602 }
603 else if (objPath.parent_path() == certs::ldapObjectPath)
604 {
605 certURL = crow::utility::urlFromPieces("redfish", "v1",
606 "AccountService", "LDAP",
607 "Certificates", certId);
608 }
609 else if (objPath.parent_path() == certs::authorityObjectPath)
610 {
611 certURL = crow::utility::urlFromPieces(
612 "redfish", "v1", "Managers", "bmc", "Truststore",
613 "Certificates", certId);
614 }
615 else
616 {
617 continue;
618 }
619
620 nlohmann::json::object_t link;
621 link["@odata.id"] = certURL;
622 links.emplace_back(std::move(link));
623 }
624
625 asyncResp->res.jsonValue[countPtr] = links.size();
626 },
627 "xyz.openbmc_project.ObjectMapper",
628 "/xyz/openbmc_project/object_mapper",
629 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", basePath, 0,
630 std::array<const char*, 1>{certs::certPropIntf});
631}
632
633/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600634 * @brief Retrieve the certificates properties and append to the response
635 * message
636 *
637 * @param[in] asyncResp Shared pointer to the response message
638 * @param[in] objectPath Path of the D-Bus service object
639 * @param[in] certId Id of the certificate
640 * @param[in] certURL URL of the certificate object
641 * @param[in] name name of the certificate
642 * @return None
643 */
644static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800645 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
646 const std::string& objectPath, const std::string& service, long certId,
647 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600648{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600649 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
650 << " certId=" << certId << " certURl=" << certURL;
651 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800652 [asyncResp, certURL, certId,
653 name](const boost::system::error_code ec,
654 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700655 if (ec)
656 {
657 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
658 messages::resourceNotFound(asyncResp->res, name,
659 std::to_string(certId));
660 return;
661 }
662 asyncResp->res.jsonValue["@odata.id"] = certURL;
663 asyncResp->res.jsonValue["@odata.type"] =
664 "#Certificate.v1_0_0.Certificate";
665 asyncResp->res.jsonValue["Id"] = std::to_string(certId);
666 asyncResp->res.jsonValue["Name"] = name;
667 asyncResp->res.jsonValue["Description"] = name;
668 for (const auto& property : properties)
669 {
670 if (property.first == "CertificateString")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600671 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700672 asyncResp->res.jsonValue["CertificateString"] = "";
673 const std::string* value =
674 std::get_if<std::string>(&property.second);
675 if (value != nullptr)
676 {
677 asyncResp->res.jsonValue["CertificateString"] = *value;
678 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600679 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700680 else if (property.first == "KeyUsage")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600681 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700682 nlohmann::json& keyUsage = asyncResp->res.jsonValue["KeyUsage"];
683 keyUsage = nlohmann::json::array();
684 const std::vector<std::string>* value =
685 std::get_if<std::vector<std::string>>(&property.second);
686 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600687 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700688 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600689 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700690 keyUsage.push_back(usage);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600691 }
692 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600693 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700694 else if (property.first == "Issuer")
695 {
696 const std::string* value =
697 std::get_if<std::string>(&property.second);
698 if (value != nullptr)
699 {
700 updateCertIssuerOrSubject(
701 asyncResp->res.jsonValue["Issuer"], *value);
702 }
703 }
704 else if (property.first == "Subject")
705 {
706 const std::string* value =
707 std::get_if<std::string>(&property.second);
708 if (value != nullptr)
709 {
710 updateCertIssuerOrSubject(
711 asyncResp->res.jsonValue["Subject"], *value);
712 }
713 }
714 else if (property.first == "ValidNotAfter")
715 {
716 const uint64_t* value = std::get_if<uint64_t>(&property.second);
717 if (value != nullptr)
718 {
719 asyncResp->res.jsonValue["ValidNotAfter"] =
720 crow::utility::getDateTimeUint(*value);
721 }
722 }
723 else if (property.first == "ValidNotBefore")
724 {
725 const uint64_t* value = std::get_if<uint64_t>(&property.second);
726 if (value != nullptr)
727 {
728 asyncResp->res.jsonValue["ValidNotBefore"] =
729 crow::utility::getDateTimeUint(*value);
730 }
731 }
732 }
733 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600734 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600735 service, objectPath, certs::dbusPropIntf, "GetAll",
736 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600737}
738
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600739/**
740 * Action to replace an existing certificate
741 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700742inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600743{
George Liu0fda0f12021-11-16 10:06:17 +0800744 BMCWEB_ROUTE(
745 app,
746 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700747 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700748 .methods(boost::beast::http::verb::post)(
749 [&app](const crow::Request& req,
750 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000751 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700752 {
753 return;
754 }
755 std::string certificate;
756 nlohmann::json certificateUri;
757 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600758
Ed Tanous002d39b2022-05-31 08:59:27 -0700759 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
760 certificate, "CertificateUri",
761 certificateUri, "CertificateType",
762 certificateType))
763 {
764 BMCWEB_LOG_ERROR << "Required parameters are missing";
765 messages::internalError(asyncResp->res);
766 return;
767 }
768
769 if (!certificateType)
770 {
771 // should never happen, but it never hurts to be paranoid.
772 return;
773 }
774 if (certificateType != "PEM")
775 {
776 messages::actionParameterNotSupported(
777 asyncResp->res, "CertificateType", "ReplaceCertificate");
778 return;
779 }
780
781 std::string certURI;
782 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
783 "@odata.id", certURI))
784 {
785 messages::actionParameterMissing(
786 asyncResp->res, "ReplaceCertificate", "CertificateUri");
787 return;
788 }
789
790 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
791 long id = getIDFromURL(certURI);
792 if (id < 0)
793 {
794 messages::actionParameterValueFormatError(asyncResp->res, certURI,
795 "CertificateUri",
796 "ReplaceCertificate");
797 return;
798 }
799 std::string objectPath;
800 std::string name;
801 std::string service;
802 if (boost::starts_with(
803 certURI,
804 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
805 {
806 objectPath =
807 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
808 name = "HTTPS certificate";
809 service = certs::httpsServiceName;
810 }
811 else if (boost::starts_with(
812 certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
813 {
814 objectPath =
815 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
816 name = "LDAP certificate";
817 service = certs::ldapServiceName;
818 }
819 else if (boost::starts_with(
820 certURI,
821 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
822 {
823 objectPath = std::string(certs::authorityObjectPath) + "/" +
824 std::to_string(id);
825 name = "TrustStore certificate";
826 service = certs::authorityServiceName;
827 }
828 else
829 {
830 messages::actionParameterNotSupported(
831 asyncResp->res, "CertificateUri", "ReplaceCertificate");
832 return;
833 }
834
835 std::shared_ptr<CertificateFile> certFile =
836 std::make_shared<CertificateFile>(certificate);
837 crow::connections::systemBus->async_method_call(
838 [asyncResp, certFile, objectPath, service, certURI, id,
839 name](const boost::system::error_code ec) {
840 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700841 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700842 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
843 if (ec.value() ==
844 boost::system::linux_error::bad_request_descriptor)
845 {
846 messages::resourceNotFound(asyncResp->res, name,
847 std::to_string(id));
848 return;
849 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700850 messages::internalError(asyncResp->res);
851 return;
852 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700853 getCertificateProperties(asyncResp, objectPath, service, id,
854 certURI, name);
855 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
856 << certFile->getCertFilePath();
857 },
858 service, objectPath, certs::certReplaceIntf, "Replace",
859 certFile->getCertFilePath());
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700860 });
861} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600862
863/**
864 * Certificate resource describes a certificate used to prove the identity
865 * of a component, account or service.
866 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700867
868inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600869{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700870 BMCWEB_ROUTE(
871 app,
872 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700873 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700874 .methods(
875 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700876 get)([&app](const crow::Request& req,
877 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
878 const std::string& param) -> void {
Carson Labrado3ba00072022-06-06 19:40:56 +0000879 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous45ca1b82022-03-25 13:07:27 -0700880 {
881 return;
882 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700883 if (param.empty())
884 {
885 messages::internalError(asyncResp->res);
886 return;
887 }
888 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600889
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700890 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
891 << std::to_string(id);
892 std::string certURL =
893 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
894 std::to_string(id);
895 std::string objectPath = certs::httpsObjectPath;
896 objectPath += "/";
897 objectPath += std::to_string(id);
898 getCertificateProperties(asyncResp, objectPath,
899 certs::httpsServiceName, id, certURL,
900 "HTTPS Certificate");
901 });
902}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600903
904/**
905 * Collection of HTTPS certificates
906 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700907inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600908{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700909 BMCWEB_ROUTE(app,
910 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700911 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700912 .methods(boost::beast::http::verb::get)(
913 [&app](const crow::Request& req,
914 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000915 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700916 {
917 return;
918 }
919
920 asyncResp->res.jsonValue["@odata.id"] =
921 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
922 asyncResp->res.jsonValue["@odata.type"] =
923 "#CertificateCollection.CertificateCollection";
924 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
925 asyncResp->res.jsonValue["Description"] =
926 "A Collection of HTTPS certificate instances";
927
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800928 getCertificateList(asyncResp, certs::httpsObjectPath,
929 "/Members"_json_pointer,
930 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700931 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600932
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700933 BMCWEB_ROUTE(app,
934 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700935 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700936 .methods(boost::beast::http::verb::post)(
937 [&app](const crow::Request& req,
938 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000939 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700940 {
941 return;
942 }
943 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
944
945 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
946 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
947
948 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
949
950 if (certFileBody.empty())
951 {
952 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
953 messages::unrecognizedRequestBody(asyncResp->res);
954 return;
955 }
956
957 std::shared_ptr<CertificateFile> certFile =
958 std::make_shared<CertificateFile>(certFileBody);
959
960 crow::connections::systemBus->async_method_call(
961 [asyncResp, certFile](const boost::system::error_code ec,
962 const std::string& objectPath) {
963 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700964 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700965 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
966 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700967 return;
968 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700969 long certId = getIDFromURL(objectPath);
970 if (certId < 0)
George Liu0fda0f12021-11-16 10:06:17 +0800971 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700972 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
973 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800974 return;
975 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700976 std::string certURL =
977 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
978 std::to_string(certId);
979 getCertificateProperties(asyncResp, objectPath,
980 certs::httpsServiceName, certId, certURL,
981 "HTTPS Certificate");
982 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
983 << certFile->getCertFilePath();
984 },
985 certs::httpsServiceName, certs::httpsObjectPath,
986 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +0800987 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700988} // requestRoutesHTTPSCertificateCollection
989
990/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600991 * The certificate location schema defines a resource that an administrator
992 * can use in order to locate all certificates installed on a given service.
993 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700994inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600995{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700996 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700997 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous002d39b2022-05-31 08:59:27 -0700998 .methods(boost::beast::http::verb::get)(
999 [&app](const crow::Request& req,
1000 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001001 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001002 {
1003 return;
1004 }
1005 asyncResp->res.jsonValue["@odata.id"] =
1006 "/redfish/v1/CertificateService/CertificateLocations";
1007 asyncResp->res.jsonValue["@odata.type"] =
1008 "#CertificateLocations.v1_0_0.CertificateLocations";
1009 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
1010 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
1011 asyncResp->res.jsonValue["Description"] =
1012 "Defines a resource that an administrator can use in order to "
1013 "locate all certificates installed on a given service";
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001014
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001015 getCertificateList(asyncResp, certs::baseObjectPath,
1016 "/Links/Certificates"_json_pointer,
1017 "/Links/Certificates@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001018 });
1019}
1020// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001021
1022/**
1023 * Collection of LDAP certificates
1024 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001025inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001026{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001027 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001028 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001029 .methods(boost::beast::http::verb::get)(
1030 [&app](const crow::Request& req,
1031 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001032 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001033 {
1034 return;
1035 }
1036
1037 asyncResp->res.jsonValue["@odata.id"] =
1038 "/redfish/v1/AccountService/LDAP/Certificates";
1039 asyncResp->res.jsonValue["@odata.type"] =
1040 "#CertificateCollection.CertificateCollection";
1041 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1042 asyncResp->res.jsonValue["Description"] =
1043 "A Collection of LDAP certificate instances";
1044
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001045 getCertificateList(asyncResp, certs::ldapObjectPath,
1046 "/Members"_json_pointer,
1047 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001048 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001049
1050 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001051 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001052 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001053 [&app](const crow::Request& req,
1054 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001055 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001056 {
1057 return;
1058 }
1059 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001060
Ed Tanous002d39b2022-05-31 08:59:27 -07001061 if (certFileBody.empty())
1062 {
1063 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1064 messages::unrecognizedRequestBody(asyncResp->res);
1065 return;
1066 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001067
Ed Tanous002d39b2022-05-31 08:59:27 -07001068 std::shared_ptr<CertificateFile> certFile =
1069 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001070
Ed Tanous002d39b2022-05-31 08:59:27 -07001071 crow::connections::systemBus->async_method_call(
1072 [asyncResp, certFile](const boost::system::error_code ec,
1073 const std::string& objectPath) {
1074 if (ec)
1075 {
1076 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1077 messages::internalError(asyncResp->res);
1078 return;
1079 }
1080 long certId = getIDFromURL(objectPath);
1081 if (certId < 0)
1082 {
1083 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
1084 messages::internalError(asyncResp->res);
1085 return;
1086 }
1087 std::string certURL =
1088 "/redfish/v1/AccountService/LDAP/Certificates/" +
1089 std::to_string(certId);
1090 getCertificateProperties(asyncResp, objectPath,
1091 certs::ldapServiceName, certId, certURL,
1092 "LDAP Certificate");
1093 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1094 << certFile->getCertFilePath();
1095 },
1096 certs::ldapServiceName, certs::ldapObjectPath,
1097 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1098 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001099} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001100
1101/**
1102 * Certificate resource describes a certificate used to prove the identity
1103 * of a component, account or service.
1104 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001105inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001106{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001107 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001108 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001109 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001110 [&app](const crow::Request& req,
1111 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1112 const std::string&) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001113 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001114 {
1115 return;
1116 }
1117 long id = getIDFromURL(req.url);
1118 if (id < 0)
1119 {
1120 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1121 messages::internalError(asyncResp->res);
1122 return;
1123 }
1124 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1125 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1126 std::to_string(id);
1127 std::string objectPath = certs::ldapObjectPath;
1128 objectPath += "/";
1129 objectPath += std::to_string(id);
1130 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1131 id, certURL, "LDAP Certificate");
1132 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001133} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001134/**
1135 * Collection of TrustStoreCertificate certificates
1136 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001137inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001138{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001139 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001140 .privileges(redfish::privileges::getCertificate)
Ed Tanous002d39b2022-05-31 08:59:27 -07001141 .methods(boost::beast::http::verb::get)(
1142 [&app](const crow::Request& req,
1143 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001144 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001145 {
1146 return;
1147 }
1148
1149 asyncResp->res.jsonValue["@odata.id"] =
1150 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1151 asyncResp->res.jsonValue["@odata.type"] =
1152 "#CertificateCollection.CertificateCollection";
1153 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1154 asyncResp->res.jsonValue["Description"] =
1155 "A Collection of TrustStore certificate instances";
1156
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001157 getCertificateList(asyncResp, certs::authorityObjectPath,
1158 "/Members"_json_pointer,
1159 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001160 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001161
1162 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001163 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001164 .methods(boost::beast::http::verb::post)(
1165 [&app](const crow::Request& req,
1166 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001167 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001168 {
1169 return;
1170 }
1171 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1172
1173 if (certFileBody.empty())
1174 {
1175 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1176 messages::unrecognizedRequestBody(asyncResp->res);
1177 return;
1178 }
1179
1180 std::shared_ptr<CertificateFile> certFile =
1181 std::make_shared<CertificateFile>(certFileBody);
1182 crow::connections::systemBus->async_method_call(
1183 [asyncResp, certFile](const boost::system::error_code ec,
1184 const std::string& objectPath) {
1185 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001186 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001187 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1188 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001189 return;
1190 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001191 long certId = getIDFromURL(objectPath);
1192 if (certId < 0)
George Liu0fda0f12021-11-16 10:06:17 +08001193 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001194 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
1195 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +08001196 return;
1197 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001198 std::string certURL =
1199 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1200 std::to_string(certId);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001201
Ed Tanous002d39b2022-05-31 08:59:27 -07001202 getCertificateProperties(asyncResp, objectPath,
1203 certs::authorityServiceName, certId,
1204 certURL, "TrustStore Certificate");
1205 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1206 << certFile->getCertFilePath();
1207 },
1208 certs::authorityServiceName, certs::authorityObjectPath,
1209 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +08001210 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001211} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001212
1213/**
1214 * Certificate resource describes a certificate used to prove the identity
1215 * of a component, account or service.
1216 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001217inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001218{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001219 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001220 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001221 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001222 [&app](const crow::Request& req,
1223 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1224 const std::string&) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001225 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001226 {
1227 return;
1228 }
1229 long id = getIDFromURL(req.url);
1230 if (id < 0)
1231 {
1232 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1233 messages::internalError(asyncResp->res);
1234 return;
1235 }
1236 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1237 << std::to_string(id);
1238 std::string certURL =
1239 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1240 std::to_string(id);
1241 std::string objectPath = certs::authorityObjectPath;
1242 objectPath += "/";
1243 objectPath += std::to_string(id);
1244 getCertificateProperties(asyncResp, objectPath,
1245 certs::authorityServiceName, id, certURL,
1246 "TrustStore Certificate");
1247 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001248
1249 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001250 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001251 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001252 [&app](const crow::Request& req,
1253 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1254 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001255 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001256 {
1257 return;
1258 }
1259 if (param.empty())
1260 {
1261 messages::internalError(asyncResp->res);
1262 return;
1263 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001264
Ed Tanous002d39b2022-05-31 08:59:27 -07001265 long id = getIDFromURL(req.url);
1266 if (id < 0)
1267 {
1268 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1269 messages::resourceNotFound(asyncResp->res, "TrustStore Certificate",
1270 std::string(req.url));
1271 return;
1272 }
1273 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1274 << std::to_string(id);
1275 std::string certPath = certs::authorityObjectPath;
1276 certPath += "/";
1277 certPath += std::to_string(id);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001278
Ed Tanous002d39b2022-05-31 08:59:27 -07001279 crow::connections::systemBus->async_method_call(
1280 [asyncResp, id](const boost::system::error_code ec) {
1281 if (ec)
1282 {
1283 messages::resourceNotFound(asyncResp->res,
1284 "TrustStore Certificate",
1285 std::to_string(id));
1286 return;
1287 }
1288 BMCWEB_LOG_INFO << "Certificate deleted";
1289 asyncResp->res.result(boost::beast::http::status::no_content);
1290 },
1291 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1292 "Delete");
1293 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001294} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001295} // namespace redfish