blob: ff172553d13b644319be9359250712ead6b415a7 [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;
158 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;
463 for (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/**
559 * @brief Retrieve the certificates properties and append to the response
560 * message
561 *
562 * @param[in] asyncResp Shared pointer to the response message
563 * @param[in] objectPath Path of the D-Bus service object
564 * @param[in] certId Id of the certificate
565 * @param[in] certURL URL of the certificate object
566 * @param[in] name name of the certificate
567 * @return None
568 */
569static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800570 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
571 const std::string& objectPath, const std::string& service, long certId,
572 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600573{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600574 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
575 << " certId=" << certId << " certURl=" << certURL;
576 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800577 [asyncResp, certURL, certId,
578 name](const boost::system::error_code ec,
579 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700580 if (ec)
581 {
582 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
583 messages::resourceNotFound(asyncResp->res, name,
584 std::to_string(certId));
585 return;
586 }
587 asyncResp->res.jsonValue["@odata.id"] = certURL;
588 asyncResp->res.jsonValue["@odata.type"] =
589 "#Certificate.v1_0_0.Certificate";
590 asyncResp->res.jsonValue["Id"] = std::to_string(certId);
591 asyncResp->res.jsonValue["Name"] = name;
592 asyncResp->res.jsonValue["Description"] = name;
593 for (const auto& property : properties)
594 {
595 if (property.first == "CertificateString")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600596 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700597 asyncResp->res.jsonValue["CertificateString"] = "";
598 const std::string* value =
599 std::get_if<std::string>(&property.second);
600 if (value != nullptr)
601 {
602 asyncResp->res.jsonValue["CertificateString"] = *value;
603 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600604 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700605 else if (property.first == "KeyUsage")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600606 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700607 nlohmann::json& keyUsage = asyncResp->res.jsonValue["KeyUsage"];
608 keyUsage = nlohmann::json::array();
609 const std::vector<std::string>* value =
610 std::get_if<std::vector<std::string>>(&property.second);
611 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600612 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700613 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600614 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700615 keyUsage.push_back(usage);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600616 }
617 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600618 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700619 else if (property.first == "Issuer")
620 {
621 const std::string* value =
622 std::get_if<std::string>(&property.second);
623 if (value != nullptr)
624 {
625 updateCertIssuerOrSubject(
626 asyncResp->res.jsonValue["Issuer"], *value);
627 }
628 }
629 else if (property.first == "Subject")
630 {
631 const std::string* value =
632 std::get_if<std::string>(&property.second);
633 if (value != nullptr)
634 {
635 updateCertIssuerOrSubject(
636 asyncResp->res.jsonValue["Subject"], *value);
637 }
638 }
639 else if (property.first == "ValidNotAfter")
640 {
641 const uint64_t* value = std::get_if<uint64_t>(&property.second);
642 if (value != nullptr)
643 {
644 asyncResp->res.jsonValue["ValidNotAfter"] =
645 crow::utility::getDateTimeUint(*value);
646 }
647 }
648 else if (property.first == "ValidNotBefore")
649 {
650 const uint64_t* value = std::get_if<uint64_t>(&property.second);
651 if (value != nullptr)
652 {
653 asyncResp->res.jsonValue["ValidNotBefore"] =
654 crow::utility::getDateTimeUint(*value);
655 }
656 }
657 }
658 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600659 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600660 service, objectPath, certs::dbusPropIntf, "GetAll",
661 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600662}
663
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600664/**
665 * Action to replace an existing certificate
666 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700667inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600668{
George Liu0fda0f12021-11-16 10:06:17 +0800669 BMCWEB_ROUTE(
670 app,
671 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700672 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700673 .methods(boost::beast::http::verb::post)(
674 [&app](const crow::Request& req,
675 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000676 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700677 {
678 return;
679 }
680 std::string certificate;
681 nlohmann::json certificateUri;
682 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600683
Ed Tanous002d39b2022-05-31 08:59:27 -0700684 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
685 certificate, "CertificateUri",
686 certificateUri, "CertificateType",
687 certificateType))
688 {
689 BMCWEB_LOG_ERROR << "Required parameters are missing";
690 messages::internalError(asyncResp->res);
691 return;
692 }
693
694 if (!certificateType)
695 {
696 // should never happen, but it never hurts to be paranoid.
697 return;
698 }
699 if (certificateType != "PEM")
700 {
701 messages::actionParameterNotSupported(
702 asyncResp->res, "CertificateType", "ReplaceCertificate");
703 return;
704 }
705
706 std::string certURI;
707 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
708 "@odata.id", certURI))
709 {
710 messages::actionParameterMissing(
711 asyncResp->res, "ReplaceCertificate", "CertificateUri");
712 return;
713 }
714
715 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
716 long id = getIDFromURL(certURI);
717 if (id < 0)
718 {
719 messages::actionParameterValueFormatError(asyncResp->res, certURI,
720 "CertificateUri",
721 "ReplaceCertificate");
722 return;
723 }
724 std::string objectPath;
725 std::string name;
726 std::string service;
727 if (boost::starts_with(
728 certURI,
729 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
730 {
731 objectPath =
732 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
733 name = "HTTPS certificate";
734 service = certs::httpsServiceName;
735 }
736 else if (boost::starts_with(
737 certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
738 {
739 objectPath =
740 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
741 name = "LDAP certificate";
742 service = certs::ldapServiceName;
743 }
744 else if (boost::starts_with(
745 certURI,
746 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
747 {
748 objectPath = std::string(certs::authorityObjectPath) + "/" +
749 std::to_string(id);
750 name = "TrustStore certificate";
751 service = certs::authorityServiceName;
752 }
753 else
754 {
755 messages::actionParameterNotSupported(
756 asyncResp->res, "CertificateUri", "ReplaceCertificate");
757 return;
758 }
759
760 std::shared_ptr<CertificateFile> certFile =
761 std::make_shared<CertificateFile>(certificate);
762 crow::connections::systemBus->async_method_call(
763 [asyncResp, certFile, objectPath, service, certURI, id,
764 name](const boost::system::error_code ec) {
765 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700766 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700767 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
768 if (ec.value() ==
769 boost::system::linux_error::bad_request_descriptor)
770 {
771 messages::resourceNotFound(asyncResp->res, name,
772 std::to_string(id));
773 return;
774 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700775 messages::internalError(asyncResp->res);
776 return;
777 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700778 getCertificateProperties(asyncResp, objectPath, service, id,
779 certURI, name);
780 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
781 << certFile->getCertFilePath();
782 },
783 service, objectPath, certs::certReplaceIntf, "Replace",
784 certFile->getCertFilePath());
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700785 });
786} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600787
788/**
789 * Certificate resource describes a certificate used to prove the identity
790 * of a component, account or service.
791 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700792
793inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600794{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700795 BMCWEB_ROUTE(
796 app,
797 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700798 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700799 .methods(
800 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700801 get)([&app](const crow::Request& req,
802 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
803 const std::string& param) -> void {
Carson Labrado3ba00072022-06-06 19:40:56 +0000804 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous45ca1b82022-03-25 13:07:27 -0700805 {
806 return;
807 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700808 if (param.empty())
809 {
810 messages::internalError(asyncResp->res);
811 return;
812 }
813 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600814
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700815 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
816 << std::to_string(id);
817 std::string certURL =
818 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
819 std::to_string(id);
820 std::string objectPath = certs::httpsObjectPath;
821 objectPath += "/";
822 objectPath += std::to_string(id);
823 getCertificateProperties(asyncResp, objectPath,
824 certs::httpsServiceName, id, certURL,
825 "HTTPS Certificate");
826 });
827}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600828
829/**
830 * Collection of HTTPS certificates
831 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700832inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600833{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700834 BMCWEB_ROUTE(app,
835 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700836 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700837 .methods(boost::beast::http::verb::get)(
838 [&app](const crow::Request& req,
839 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000840 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700841 {
842 return;
843 }
844
845 asyncResp->res.jsonValue["@odata.id"] =
846 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
847 asyncResp->res.jsonValue["@odata.type"] =
848 "#CertificateCollection.CertificateCollection";
849 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
850 asyncResp->res.jsonValue["Description"] =
851 "A Collection of HTTPS certificate instances";
852
853 crow::connections::systemBus->async_method_call(
854 [asyncResp](const boost::system::error_code ec,
855 const dbus::utility::ManagedObjectType& certs) {
856 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700857 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700858 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
859 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700860 return;
861 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700862 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
863 members = nlohmann::json::array();
864 for (const auto& cert : certs)
865 {
866 long id = getIDFromURL(cert.first.str);
867 if (id >= 0)
868 {
869 nlohmann::json::object_t member;
870 member["@odata.id"] =
871 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
872 std::to_string(id);
873 members.push_back(std::move(member));
874 }
875 }
876 asyncResp->res.jsonValue["Members@odata.count"] = members.size();
877 },
878 certs::httpsServiceName, certs::httpsObjectPath,
879 certs::dbusObjManagerIntf, "GetManagedObjects");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700880 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600881
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700882 BMCWEB_ROUTE(app,
883 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700884 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700885 .methods(boost::beast::http::verb::post)(
886 [&app](const crow::Request& req,
887 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000888 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700889 {
890 return;
891 }
892 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
893
894 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
895 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
896
897 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
898
899 if (certFileBody.empty())
900 {
901 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
902 messages::unrecognizedRequestBody(asyncResp->res);
903 return;
904 }
905
906 std::shared_ptr<CertificateFile> certFile =
907 std::make_shared<CertificateFile>(certFileBody);
908
909 crow::connections::systemBus->async_method_call(
910 [asyncResp, certFile](const boost::system::error_code ec,
911 const std::string& objectPath) {
912 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700913 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700914 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
915 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700916 return;
917 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700918 long certId = getIDFromURL(objectPath);
919 if (certId < 0)
George Liu0fda0f12021-11-16 10:06:17 +0800920 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700921 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
922 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800923 return;
924 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700925 std::string certURL =
926 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
927 std::to_string(certId);
928 getCertificateProperties(asyncResp, objectPath,
929 certs::httpsServiceName, certId, certURL,
930 "HTTPS Certificate");
931 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
932 << certFile->getCertFilePath();
933 },
934 certs::httpsServiceName, certs::httpsObjectPath,
935 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +0800936 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700937} // requestRoutesHTTPSCertificateCollection
938
939/**
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +0800940 * @brief Retrieve the installed certificate list
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700941 *
942 * @param[in] asyncResp Shared pointer to the response message
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700943 * @return None
944 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700945inline void
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +0800946 getCertificateLocations(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700947{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700948 crow::connections::systemBus->async_method_call(
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +0800949 [asyncResp](
950 const boost::system::error_code ec,
951 const dbus::utility::MapperGetSubTreePathsResponse& certPaths) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700952 if (ec)
953 {
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +0800954 BMCWEB_LOG_ERROR << "Certificate collection query failed: " << ec;
955 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -0700956 return;
957 }
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +0800958
Ed Tanous002d39b2022-05-31 08:59:27 -0700959 nlohmann::json& links =
960 asyncResp->res.jsonValue["Links"]["Certificates"];
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +0800961 links = nlohmann::json::array();
962 for (const auto& certPath : certPaths)
Ed Tanous002d39b2022-05-31 08:59:27 -0700963 {
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +0800964 sdbusplus::message::object_path objPath(certPath);
965 std::string certId = objPath.filename();
966 if (certId.empty())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700967 {
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +0800968 BMCWEB_LOG_ERROR << "Invalid certificate objPath " << certPath;
969 continue;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700970 }
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +0800971
972 boost::urls::url certURL;
973 if (objPath.parent_path() == certs::httpsObjectPath)
974 {
975 certURL = crow::utility::urlFromPieces(
976 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
977 "HTTPS", "Certificates", certId);
978 }
979 else if (objPath.parent_path() == certs::ldapObjectPath)
980 {
981 certURL = crow::utility::urlFromPieces("redfish", "v1",
982 "AccountService", "LDAP",
983 "Certificates", certId);
984 }
985 else if (objPath.parent_path() == certs::authorityObjectPath)
986 {
987 certURL = crow::utility::urlFromPieces(
988 "redfish", "v1", "Managers", "bmc", "Truststore",
989 "Certificates", certId);
990 }
991 else
992 {
993 continue;
994 }
995 nlohmann::json::object_t link;
996 link["@odata.id"] = certURL;
997 links.emplace_back(std::move(link));
Ed Tanous002d39b2022-05-31 08:59:27 -0700998 }
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +0800999
Ed Tanous002d39b2022-05-31 08:59:27 -07001000 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
1001 links.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001002 },
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +08001003 "xyz.openbmc_project.ObjectMapper",
1004 "/xyz/openbmc_project/object_mapper",
1005 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1006 certs::baseObjectPath, 0,
1007 std::array<const char*, 1>{certs::certPropIntf});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001008}
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001009
1010/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001011 * The certificate location schema defines a resource that an administrator
1012 * can use in order to locate all certificates installed on a given service.
1013 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001014inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001015{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001016 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -07001017 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous002d39b2022-05-31 08:59:27 -07001018 .methods(boost::beast::http::verb::get)(
1019 [&app](const crow::Request& req,
1020 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001021 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001022 {
1023 return;
1024 }
1025 asyncResp->res.jsonValue["@odata.id"] =
1026 "/redfish/v1/CertificateService/CertificateLocations";
1027 asyncResp->res.jsonValue["@odata.type"] =
1028 "#CertificateLocations.v1_0_0.CertificateLocations";
1029 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
1030 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
1031 asyncResp->res.jsonValue["Description"] =
1032 "Defines a resource that an administrator can use in order to "
1033 "locate all certificates installed on a given service";
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001034
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +08001035 getCertificateLocations(asyncResp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001036 });
1037}
1038// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001039
1040/**
1041 * Collection of LDAP certificates
1042 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001043inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001044{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001045 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001046 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001047 .methods(boost::beast::http::verb::get)(
1048 [&app](const crow::Request& req,
1049 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001050 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001051 {
1052 return;
1053 }
1054
1055 asyncResp->res.jsonValue["@odata.id"] =
1056 "/redfish/v1/AccountService/LDAP/Certificates";
1057 asyncResp->res.jsonValue["@odata.type"] =
1058 "#CertificateCollection.CertificateCollection";
1059 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1060 asyncResp->res.jsonValue["Description"] =
1061 "A Collection of LDAP certificate instances";
1062
1063 crow::connections::systemBus->async_method_call(
1064 [asyncResp](const boost::system::error_code ec,
1065 const dbus::utility::ManagedObjectType& certs) {
1066 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
1067 nlohmann::json& count =
1068 asyncResp->res.jsonValue["Members@odata.count"];
1069 members = nlohmann::json::array();
1070 count = 0;
1071 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001072 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001073 BMCWEB_LOG_WARNING << "LDAP certificate query failed: " << ec;
Ed Tanous45ca1b82022-03-25 13:07:27 -07001074 return;
1075 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001076 for (const auto& cert : certs)
1077 {
1078 long id = getIDFromURL(cert.first.str);
1079 if (id >= 0)
1080 {
1081 nlohmann::json::object_t member;
1082 member["@odata.id"] =
1083 "/redfish/v1/AccountService/LDAP/Certificates/" +
1084 std::to_string(id);
1085 members.push_back(std::move(member));
1086 }
1087 }
1088 count = members.size();
1089 },
1090 certs::ldapServiceName, certs::ldapObjectPath,
1091 certs::dbusObjManagerIntf, "GetManagedObjects");
George Liu0fda0f12021-11-16 10:06:17 +08001092 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001093
1094 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001095 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001096 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001097 [&app](const crow::Request& req,
1098 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001099 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001100 {
1101 return;
1102 }
1103 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001104
Ed Tanous002d39b2022-05-31 08:59:27 -07001105 if (certFileBody.empty())
1106 {
1107 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1108 messages::unrecognizedRequestBody(asyncResp->res);
1109 return;
1110 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001111
Ed Tanous002d39b2022-05-31 08:59:27 -07001112 std::shared_ptr<CertificateFile> certFile =
1113 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001114
Ed Tanous002d39b2022-05-31 08:59:27 -07001115 crow::connections::systemBus->async_method_call(
1116 [asyncResp, certFile](const boost::system::error_code ec,
1117 const std::string& objectPath) {
1118 if (ec)
1119 {
1120 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1121 messages::internalError(asyncResp->res);
1122 return;
1123 }
1124 long certId = getIDFromURL(objectPath);
1125 if (certId < 0)
1126 {
1127 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
1128 messages::internalError(asyncResp->res);
1129 return;
1130 }
1131 std::string certURL =
1132 "/redfish/v1/AccountService/LDAP/Certificates/" +
1133 std::to_string(certId);
1134 getCertificateProperties(asyncResp, objectPath,
1135 certs::ldapServiceName, certId, certURL,
1136 "LDAP Certificate");
1137 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1138 << certFile->getCertFilePath();
1139 },
1140 certs::ldapServiceName, certs::ldapObjectPath,
1141 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1142 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001143} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001144
1145/**
1146 * Certificate resource describes a certificate used to prove the identity
1147 * of a component, account or service.
1148 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001149inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001150{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001151 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001152 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001153 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001154 [&app](const crow::Request& req,
1155 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1156 const std::string&) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001157 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001158 {
1159 return;
1160 }
1161 long id = getIDFromURL(req.url);
1162 if (id < 0)
1163 {
1164 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1165 messages::internalError(asyncResp->res);
1166 return;
1167 }
1168 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1169 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1170 std::to_string(id);
1171 std::string objectPath = certs::ldapObjectPath;
1172 objectPath += "/";
1173 objectPath += std::to_string(id);
1174 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1175 id, certURL, "LDAP Certificate");
1176 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001177} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001178/**
1179 * Collection of TrustStoreCertificate certificates
1180 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001181inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001182{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001183 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001184 .privileges(redfish::privileges::getCertificate)
Ed Tanous002d39b2022-05-31 08:59:27 -07001185 .methods(boost::beast::http::verb::get)(
1186 [&app](const crow::Request& req,
1187 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001188 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001189 {
1190 return;
1191 }
1192
1193 asyncResp->res.jsonValue["@odata.id"] =
1194 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1195 asyncResp->res.jsonValue["@odata.type"] =
1196 "#CertificateCollection.CertificateCollection";
1197 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1198 asyncResp->res.jsonValue["Description"] =
1199 "A Collection of TrustStore certificate instances";
1200
1201 crow::connections::systemBus->async_method_call(
1202 [asyncResp](const boost::system::error_code ec,
1203 const dbus::utility::ManagedObjectType& certs) {
1204 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001205 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001206 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1207 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001208 return;
1209 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001210 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
1211 members = nlohmann::json::array();
1212 for (const auto& cert : certs)
1213 {
1214 long id = getIDFromURL(cert.first.str);
1215 if (id >= 0)
1216 {
1217 nlohmann::json::object_t member;
1218 member["@odata.id"] =
1219 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1220 std::to_string(id);
1221 members.push_back(std::move(member));
1222 }
1223 }
1224 asyncResp->res.jsonValue["Members@odata.count"] = members.size();
1225 },
1226 certs::authorityServiceName, certs::authorityObjectPath,
1227 certs::dbusObjManagerIntf, "GetManagedObjects");
George Liu0fda0f12021-11-16 10:06:17 +08001228 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001229
1230 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001231 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001232 .methods(boost::beast::http::verb::post)(
1233 [&app](const crow::Request& req,
1234 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001235 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001236 {
1237 return;
1238 }
1239 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1240
1241 if (certFileBody.empty())
1242 {
1243 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1244 messages::unrecognizedRequestBody(asyncResp->res);
1245 return;
1246 }
1247
1248 std::shared_ptr<CertificateFile> certFile =
1249 std::make_shared<CertificateFile>(certFileBody);
1250 crow::connections::systemBus->async_method_call(
1251 [asyncResp, certFile](const boost::system::error_code ec,
1252 const std::string& objectPath) {
1253 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001254 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001255 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1256 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001257 return;
1258 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001259 long certId = getIDFromURL(objectPath);
1260 if (certId < 0)
George Liu0fda0f12021-11-16 10:06:17 +08001261 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001262 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
1263 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +08001264 return;
1265 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001266 std::string certURL =
1267 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1268 std::to_string(certId);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001269
Ed Tanous002d39b2022-05-31 08:59:27 -07001270 getCertificateProperties(asyncResp, objectPath,
1271 certs::authorityServiceName, certId,
1272 certURL, "TrustStore Certificate");
1273 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1274 << certFile->getCertFilePath();
1275 },
1276 certs::authorityServiceName, certs::authorityObjectPath,
1277 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +08001278 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001279} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001280
1281/**
1282 * Certificate resource describes a certificate used to prove the identity
1283 * of a component, account or service.
1284 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001285inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001286{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001287 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001288 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001289 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001290 [&app](const crow::Request& req,
1291 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1292 const std::string&) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001293 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001294 {
1295 return;
1296 }
1297 long id = getIDFromURL(req.url);
1298 if (id < 0)
1299 {
1300 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1301 messages::internalError(asyncResp->res);
1302 return;
1303 }
1304 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1305 << std::to_string(id);
1306 std::string certURL =
1307 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1308 std::to_string(id);
1309 std::string objectPath = certs::authorityObjectPath;
1310 objectPath += "/";
1311 objectPath += std::to_string(id);
1312 getCertificateProperties(asyncResp, objectPath,
1313 certs::authorityServiceName, id, certURL,
1314 "TrustStore Certificate");
1315 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001316
1317 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001318 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001319 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001320 [&app](const crow::Request& req,
1321 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1322 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001323 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001324 {
1325 return;
1326 }
1327 if (param.empty())
1328 {
1329 messages::internalError(asyncResp->res);
1330 return;
1331 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001332
Ed Tanous002d39b2022-05-31 08:59:27 -07001333 long id = getIDFromURL(req.url);
1334 if (id < 0)
1335 {
1336 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1337 messages::resourceNotFound(asyncResp->res, "TrustStore Certificate",
1338 std::string(req.url));
1339 return;
1340 }
1341 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1342 << std::to_string(id);
1343 std::string certPath = certs::authorityObjectPath;
1344 certPath += "/";
1345 certPath += std::to_string(id);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001346
Ed Tanous002d39b2022-05-31 08:59:27 -07001347 crow::connections::systemBus->async_method_call(
1348 [asyncResp, id](const boost::system::error_code ec) {
1349 if (ec)
1350 {
1351 messages::resourceNotFound(asyncResp->res,
1352 "TrustStore Certificate",
1353 std::to_string(id));
1354 return;
1355 }
1356 BMCWEB_LOG_INFO << "Certificate deleted";
1357 asyncResp->res.result(boost::beast::http::status::no_content);
1358 },
1359 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1360 "Delete");
1361 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001362} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001363} // namespace redfish