blob: 53c72e6780e495f4a9052c1aa517ab519db1d3f1 [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
Patrick Williams59d494e2022-07-22 19:26:55 -0500202static std::unique_ptr<sdbusplus::bus::match_t> csrMatcher;
Marri Devender Rao30215812019-03-18 08:59:21 -0500203/**
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;
Ed Tanous11ba3972022-07-11 09:50:41 -0700321 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700322 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
323 {
324 objectPath = certs::httpsObjectPath;
325 service = certs::httpsServiceName;
326 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700327 else if (certURI.starts_with(
328 "/redfish/v1/AccountService/LDAP/Certificates"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700329 {
330 objectPath = certs::ldapObjectPath;
331 service = certs::ldapServiceName;
332 }
333 else
334 {
335 messages::actionParameterNotSupported(
336 asyncResp->res, "CertificateCollection", "GenerateCSR");
337 return;
338 }
339
340 // supporting only EC and RSA algorithm
341 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
342 {
343 messages::actionParameterNotSupported(
344 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
345 return;
346 }
347
348 // supporting only 2048 key bit length for RSA algorithm due to
349 // time consumed in generating private key
350 if (*optKeyPairAlgorithm == "RSA" &&
351 *optKeyBitLength != rsaKeyBitLength)
352 {
353 messages::propertyValueNotInList(asyncResp->res,
354 std::to_string(*optKeyBitLength),
355 "KeyBitLength");
356 return;
357 }
358
359 // validate KeyUsage supporting only 1 type based on URL
Ed Tanous11ba3972022-07-11 09:50:41 -0700360 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700361 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
362 {
363 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800364 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700365 optKeyUsage->push_back("ServerAuthentication");
George Liu0fda0f12021-11-16 10:06:17 +0800366 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700367 else if (optKeyUsage->size() == 1)
George Liu0fda0f12021-11-16 10:06:17 +0800368 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700369 if ((*optKeyUsage)[0] != "ServerAuthentication")
370 {
371 messages::propertyValueNotInList(
372 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
373 return;
374 }
George Liu0fda0f12021-11-16 10:06:17 +0800375 }
376 else
377 {
378 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700379 asyncResp->res, "KeyUsage", "GenerateCSR");
George Liu0fda0f12021-11-16 10:06:17 +0800380 return;
381 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700382 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700383 else if (certURI.starts_with(
384 "/redfish/v1/AccountService/LDAP/Certificates"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700385 {
386 if (optKeyUsage->empty())
387 {
388 optKeyUsage->push_back("ClientAuthentication");
389 }
390 else if (optKeyUsage->size() == 1)
391 {
392 if ((*optKeyUsage)[0] != "ClientAuthentication")
393 {
394 messages::propertyValueNotInList(
395 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
396 return;
397 }
398 }
399 else
George Liu0fda0f12021-11-16 10:06:17 +0800400 {
401 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700402 asyncResp->res, "KeyUsage", "GenerateCSR");
403 return;
404 }
405 }
406
407 // Only allow one CSR matcher at a time so setting retry
408 // time-out and timer expiry to 10 seconds for now.
409 static const int timeOut = 10;
410 if (csrMatcher)
411 {
412 messages::serviceTemporarilyUnavailable(asyncResp->res,
413 std::to_string(timeOut));
414 return;
415 }
416
417 // Make this static so it survives outside this method
418 static boost::asio::steady_timer timeout(*req.ioService);
419 timeout.expires_after(std::chrono::seconds(timeOut));
420 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
421 csrMatcher = nullptr;
422 if (ec)
423 {
424 // operation_aborted is expected if timer is canceled
425 // before completion.
426 if (ec != boost::asio::error::operation_aborted)
427 {
428 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
429 }
430 return;
431 }
432 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
433 messages::internalError(asyncResp->res);
434 });
435
436 // create a matcher to wait on CSR object
437 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
438 std::string match("type='signal',"
439 "interface='org.freedesktop.DBus.ObjectManager',"
440 "path='" +
441 objectPath +
442 "',"
443 "member='InterfacesAdded'");
Patrick Williams59d494e2022-07-22 19:26:55 -0500444 csrMatcher = std::make_unique<sdbusplus::bus::match_t>(
Ed Tanous002d39b2022-05-31 08:59:27 -0700445 *crow::connections::systemBus, match,
Patrick Williams59d494e2022-07-22 19:26:55 -0500446 [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700447 timeout.cancel();
448 if (m.is_method_error())
449 {
450 BMCWEB_LOG_ERROR << "Dbus method error!!!";
451 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800452 return;
453 }
454
Ed Tanous002d39b2022-05-31 08:59:27 -0700455 dbus::utility::DBusInteracesMap interfacesProperties;
456
457 sdbusplus::message::object_path csrObjectPath;
458 m.read(csrObjectPath, interfacesProperties);
459 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
Ed Tanous02cad962022-06-30 16:50:15 -0700460 for (const auto& interface : interfacesProperties)
George Liu0fda0f12021-11-16 10:06:17 +0800461 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700462 if (interface.first == "xyz.openbmc_project.Certs.CSR")
463 {
464 getCSR(asyncResp, certURI, service, objectPath,
465 csrObjectPath.str);
466 break;
467 }
468 }
469 });
470 crow::connections::systemBus->async_method_call(
471 [asyncResp](const boost::system::error_code ec,
472 const std::string&) {
473 if (ec)
474 {
475 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
476 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800477 return;
478 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700479 },
480 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
481 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
482 commonName, *optContactPerson, country, *optEmail, *optGivenName,
483 *optInitials, *optKeyBitLength, *optKeyCurveId,
484 *optKeyPairAlgorithm, *optKeyUsage, organization,
485 organizationalUnit, state, *optSurname, *optUnstructuredName);
George Liu0fda0f12021-11-16 10:06:17 +0800486 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700487} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500488
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600489/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500490 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600491 *
492 * @param[in] asyncResp Shared pointer to the response message
493 * @param[in] str Issuer/Subject value in key=value pairs
494 * @param[in] type Issuer/Subject
495 * @return None
496 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500497static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600498 const std::string_view value)
499{
500 // example: O=openbmc-project.xyz,CN=localhost
501 std::string_view::iterator i = value.begin();
502 while (i != value.end())
503 {
504 std::string_view::iterator tokenBegin = i;
505 while (i != value.end() && *i != '=')
506 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530507 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600508 }
509 if (i == value.end())
510 {
511 break;
512 }
Ed Tanous271584a2019-07-09 16:24:22 -0700513 const std::string_view key(tokenBegin,
514 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530515 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600516 tokenBegin = i;
517 while (i != value.end() && *i != ',')
518 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530519 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600520 }
Ed Tanous271584a2019-07-09 16:24:22 -0700521 const std::string_view val(tokenBegin,
522 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600523 if (key == "L")
524 {
525 out["City"] = val;
526 }
527 else if (key == "CN")
528 {
529 out["CommonName"] = val;
530 }
531 else if (key == "C")
532 {
533 out["Country"] = val;
534 }
535 else if (key == "O")
536 {
537 out["Organization"] = val;
538 }
539 else if (key == "OU")
540 {
541 out["OrganizationalUnit"] = val;
542 }
543 else if (key == "ST")
544 {
545 out["State"] = val;
546 }
547 // skip comma character
548 if (i != value.end())
549 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530550 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600551 }
552 }
553}
554
555/**
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800556 * @brief Retrieve the installed certificate list
557 *
558 * @param[in] asyncResp Shared pointer to the response message
559 * @param[in] basePath DBus object path to search
560 * @param[in] listPtr Json pointer to the list in asyncResp
561 * @param[in] countPtr Json pointer to the count in asyncResp
562 * @return None
563 */
564static void
565 getCertificateList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
566 const std::string& basePath,
567 const nlohmann::json::json_pointer& listPtr,
568 const nlohmann::json::json_pointer& countPtr)
569{
570 crow::connections::systemBus->async_method_call(
571 [asyncResp, listPtr, countPtr](
572 const boost::system::error_code ec,
573 const dbus::utility::MapperGetSubTreePathsResponse& certPaths) {
574 if (ec)
575 {
576 BMCWEB_LOG_ERROR << "Certificate collection query failed: " << ec;
577 messages::internalError(asyncResp->res);
578 return;
579 }
580
581 nlohmann::json& links = asyncResp->res.jsonValue[listPtr];
582 links = nlohmann::json::array();
583 for (const auto& certPath : certPaths)
584 {
585 sdbusplus::message::object_path objPath(certPath);
586 std::string certId = objPath.filename();
587 if (certId.empty())
588 {
589 BMCWEB_LOG_ERROR << "Invalid certificate objPath " << certPath;
590 continue;
591 }
592
593 boost::urls::url certURL;
594 if (objPath.parent_path() == certs::httpsObjectPath)
595 {
596 certURL = crow::utility::urlFromPieces(
597 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
598 "HTTPS", "Certificates", certId);
599 }
600 else if (objPath.parent_path() == certs::ldapObjectPath)
601 {
602 certURL = crow::utility::urlFromPieces("redfish", "v1",
603 "AccountService", "LDAP",
604 "Certificates", certId);
605 }
606 else if (objPath.parent_path() == certs::authorityObjectPath)
607 {
608 certURL = crow::utility::urlFromPieces(
609 "redfish", "v1", "Managers", "bmc", "Truststore",
610 "Certificates", certId);
611 }
612 else
613 {
614 continue;
615 }
616
617 nlohmann::json::object_t link;
618 link["@odata.id"] = certURL;
619 links.emplace_back(std::move(link));
620 }
621
622 asyncResp->res.jsonValue[countPtr] = links.size();
623 },
624 "xyz.openbmc_project.ObjectMapper",
625 "/xyz/openbmc_project/object_mapper",
626 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", basePath, 0,
627 std::array<const char*, 1>{certs::certPropIntf});
628}
629
630/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600631 * @brief Retrieve the certificates properties and append to the response
632 * message
633 *
634 * @param[in] asyncResp Shared pointer to the response message
635 * @param[in] objectPath Path of the D-Bus service object
636 * @param[in] certId Id of the certificate
637 * @param[in] certURL URL of the certificate object
638 * @param[in] name name of the certificate
639 * @return None
640 */
641static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800642 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800643 const std::string& objectPath, const std::string& service,
644 const std::string& certId, const std::string& certURL,
645 const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600646{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600647 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
648 << " certId=" << certId << " certURl=" << certURL;
649 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800650 [asyncResp, certURL, certId,
651 name](const boost::system::error_code ec,
652 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700653 if (ec)
654 {
655 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800656 messages::resourceNotFound(asyncResp->res, name, certId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700657 return;
658 }
659 asyncResp->res.jsonValue["@odata.id"] = certURL;
660 asyncResp->res.jsonValue["@odata.type"] =
661 "#Certificate.v1_0_0.Certificate";
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800662 asyncResp->res.jsonValue["Id"] = certId;
Ed Tanous002d39b2022-05-31 08:59:27 -0700663 asyncResp->res.jsonValue["Name"] = name;
664 asyncResp->res.jsonValue["Description"] = name;
665 for (const auto& property : properties)
666 {
667 if (property.first == "CertificateString")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600668 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700669 asyncResp->res.jsonValue["CertificateString"] = "";
670 const std::string* value =
671 std::get_if<std::string>(&property.second);
672 if (value != nullptr)
673 {
674 asyncResp->res.jsonValue["CertificateString"] = *value;
675 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600676 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700677 else if (property.first == "KeyUsage")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600678 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700679 nlohmann::json& keyUsage = asyncResp->res.jsonValue["KeyUsage"];
680 keyUsage = nlohmann::json::array();
681 const std::vector<std::string>* value =
682 std::get_if<std::vector<std::string>>(&property.second);
683 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600684 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700685 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600686 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700687 keyUsage.push_back(usage);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600688 }
689 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600690 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700691 else if (property.first == "Issuer")
692 {
693 const std::string* value =
694 std::get_if<std::string>(&property.second);
695 if (value != nullptr)
696 {
697 updateCertIssuerOrSubject(
698 asyncResp->res.jsonValue["Issuer"], *value);
699 }
700 }
701 else if (property.first == "Subject")
702 {
703 const std::string* value =
704 std::get_if<std::string>(&property.second);
705 if (value != nullptr)
706 {
707 updateCertIssuerOrSubject(
708 asyncResp->res.jsonValue["Subject"], *value);
709 }
710 }
711 else if (property.first == "ValidNotAfter")
712 {
713 const uint64_t* value = std::get_if<uint64_t>(&property.second);
714 if (value != nullptr)
715 {
716 asyncResp->res.jsonValue["ValidNotAfter"] =
717 crow::utility::getDateTimeUint(*value);
718 }
719 }
720 else if (property.first == "ValidNotBefore")
721 {
722 const uint64_t* value = std::get_if<uint64_t>(&property.second);
723 if (value != nullptr)
724 {
725 asyncResp->res.jsonValue["ValidNotBefore"] =
726 crow::utility::getDateTimeUint(*value);
727 }
728 }
729 }
730 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600731 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600732 service, objectPath, certs::dbusPropIntf, "GetAll",
733 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600734}
735
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600736/**
737 * Action to replace an existing certificate
738 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700739inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600740{
George Liu0fda0f12021-11-16 10:06:17 +0800741 BMCWEB_ROUTE(
742 app,
743 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700744 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700745 .methods(boost::beast::http::verb::post)(
746 [&app](const crow::Request& req,
747 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000748 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700749 {
750 return;
751 }
752 std::string certificate;
753 nlohmann::json certificateUri;
754 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600755
Ed Tanous002d39b2022-05-31 08:59:27 -0700756 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
757 certificate, "CertificateUri",
758 certificateUri, "CertificateType",
759 certificateType))
760 {
761 BMCWEB_LOG_ERROR << "Required parameters are missing";
762 messages::internalError(asyncResp->res);
763 return;
764 }
765
766 if (!certificateType)
767 {
768 // should never happen, but it never hurts to be paranoid.
769 return;
770 }
771 if (certificateType != "PEM")
772 {
773 messages::actionParameterNotSupported(
774 asyncResp->res, "CertificateType", "ReplaceCertificate");
775 return;
776 }
777
778 std::string certURI;
779 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
780 "@odata.id", certURI))
781 {
782 messages::actionParameterMissing(
783 asyncResp->res, "ReplaceCertificate", "CertificateUri");
784 return;
785 }
786
787 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
788 long id = getIDFromURL(certURI);
789 if (id < 0)
790 {
791 messages::actionParameterValueFormatError(asyncResp->res, certURI,
792 "CertificateUri",
793 "ReplaceCertificate");
794 return;
795 }
796 std::string objectPath;
797 std::string name;
798 std::string service;
Ed Tanous11ba3972022-07-11 09:50:41 -0700799 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700800 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
801 {
802 objectPath =
803 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
804 name = "HTTPS certificate";
805 service = certs::httpsServiceName;
806 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700807 else if (certURI.starts_with(
808 "/redfish/v1/AccountService/LDAP/Certificates/"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700809 {
810 objectPath =
811 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
812 name = "LDAP certificate";
813 service = certs::ldapServiceName;
814 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700815 else if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700816 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
817 {
818 objectPath = std::string(certs::authorityObjectPath) + "/" +
819 std::to_string(id);
820 name = "TrustStore certificate";
821 service = certs::authorityServiceName;
822 }
823 else
824 {
825 messages::actionParameterNotSupported(
826 asyncResp->res, "CertificateUri", "ReplaceCertificate");
827 return;
828 }
829
830 std::shared_ptr<CertificateFile> certFile =
831 std::make_shared<CertificateFile>(certificate);
832 crow::connections::systemBus->async_method_call(
833 [asyncResp, certFile, objectPath, service, certURI, id,
834 name](const boost::system::error_code ec) {
835 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700836 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700837 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
838 if (ec.value() ==
839 boost::system::linux_error::bad_request_descriptor)
840 {
841 messages::resourceNotFound(asyncResp->res, name,
842 std::to_string(id));
843 return;
844 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700845 messages::internalError(asyncResp->res);
846 return;
847 }
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800848 getCertificateProperties(asyncResp, objectPath, service,
849 std::to_string(id), certURI, name);
Ed Tanous002d39b2022-05-31 08:59:27 -0700850 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
851 << certFile->getCertFilePath();
852 },
853 service, objectPath, certs::certReplaceIntf, "Replace",
854 certFile->getCertFilePath());
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700855 });
856} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600857
858/**
859 * Certificate resource describes a certificate used to prove the identity
860 * of a component, account or service.
861 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700862
863inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600864{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700865 BMCWEB_ROUTE(
866 app,
867 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700868 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700869 .methods(
870 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700871 get)([&app](const crow::Request& req,
872 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800873 const std::string& id) -> void {
Carson Labrado3ba00072022-06-06 19:40:56 +0000874 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous45ca1b82022-03-25 13:07:27 -0700875 {
876 return;
877 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600878
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800879 BMCWEB_LOG_DEBUG << "HTTPS Certificate ID=" << id;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700880 std::string certURL =
881 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800882 id;
883 std::string objPath =
884 sdbusplus::message::object_path(certs::httpsObjectPath) / id;
885 getCertificateProperties(asyncResp, objPath,
886 certs::httpsServiceName, id, certURL,
887 "HTTPS Certificate");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700888 });
889}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600890
891/**
892 * Collection of HTTPS certificates
893 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700894inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600895{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700896 BMCWEB_ROUTE(app,
897 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700898 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700899 .methods(boost::beast::http::verb::get)(
900 [&app](const crow::Request& req,
901 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000902 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700903 {
904 return;
905 }
906
907 asyncResp->res.jsonValue["@odata.id"] =
908 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
909 asyncResp->res.jsonValue["@odata.type"] =
910 "#CertificateCollection.CertificateCollection";
911 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
912 asyncResp->res.jsonValue["Description"] =
913 "A Collection of HTTPS certificate instances";
914
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800915 getCertificateList(asyncResp, certs::httpsObjectPath,
916 "/Members"_json_pointer,
917 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700918 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600919
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700920 BMCWEB_ROUTE(app,
921 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700922 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700923 .methods(boost::beast::http::verb::post)(
924 [&app](const crow::Request& req,
925 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000926 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700927 {
928 return;
929 }
930 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
931
932 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
933 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
934
935 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
936
937 if (certFileBody.empty())
938 {
939 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
940 messages::unrecognizedRequestBody(asyncResp->res);
941 return;
942 }
943
944 std::shared_ptr<CertificateFile> certFile =
945 std::make_shared<CertificateFile>(certFileBody);
946
947 crow::connections::systemBus->async_method_call(
948 [asyncResp, certFile](const boost::system::error_code ec,
949 const std::string& objectPath) {
950 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700951 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700952 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
953 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700954 return;
955 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800956
957 sdbusplus::message::object_path path(objectPath);
958 std::string certId = path.filename();
Ed Tanous002d39b2022-05-31 08:59:27 -0700959 std::string certURL =
960 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800961 certId;
962 getCertificateProperties(asyncResp, objectPath,
963 certs::httpsServiceName, certId, certURL,
964 "HTTPS Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -0700965 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
966 << certFile->getCertFilePath();
967 },
968 certs::httpsServiceName, certs::httpsObjectPath,
969 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +0800970 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700971} // requestRoutesHTTPSCertificateCollection
972
973/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600974 * The certificate location schema defines a resource that an administrator
975 * can use in order to locate all certificates installed on a given service.
976 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700977inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600978{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700979 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700980 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous002d39b2022-05-31 08:59:27 -0700981 .methods(boost::beast::http::verb::get)(
982 [&app](const crow::Request& req,
983 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000984 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700985 {
986 return;
987 }
988 asyncResp->res.jsonValue["@odata.id"] =
989 "/redfish/v1/CertificateService/CertificateLocations";
990 asyncResp->res.jsonValue["@odata.type"] =
991 "#CertificateLocations.v1_0_0.CertificateLocations";
992 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
993 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
994 asyncResp->res.jsonValue["Description"] =
995 "Defines a resource that an administrator can use in order to "
996 "locate all certificates installed on a given service";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600997
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800998 getCertificateList(asyncResp, certs::baseObjectPath,
999 "/Links/Certificates"_json_pointer,
1000 "/Links/Certificates@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001001 });
1002}
1003// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001004
1005/**
1006 * Collection of LDAP certificates
1007 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001008inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001009{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001010 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001011 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001012 .methods(boost::beast::http::verb::get)(
1013 [&app](const crow::Request& req,
1014 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001015 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001016 {
1017 return;
1018 }
1019
1020 asyncResp->res.jsonValue["@odata.id"] =
1021 "/redfish/v1/AccountService/LDAP/Certificates";
1022 asyncResp->res.jsonValue["@odata.type"] =
1023 "#CertificateCollection.CertificateCollection";
1024 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1025 asyncResp->res.jsonValue["Description"] =
1026 "A Collection of LDAP certificate instances";
1027
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001028 getCertificateList(asyncResp, certs::ldapObjectPath,
1029 "/Members"_json_pointer,
1030 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001031 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001032
1033 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001034 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001035 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001036 [&app](const crow::Request& req,
1037 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001038 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001039 {
1040 return;
1041 }
1042 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001043
Ed Tanous002d39b2022-05-31 08:59:27 -07001044 if (certFileBody.empty())
1045 {
1046 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1047 messages::unrecognizedRequestBody(asyncResp->res);
1048 return;
1049 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001050
Ed Tanous002d39b2022-05-31 08:59:27 -07001051 std::shared_ptr<CertificateFile> certFile =
1052 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001053
Ed Tanous002d39b2022-05-31 08:59:27 -07001054 crow::connections::systemBus->async_method_call(
1055 [asyncResp, certFile](const boost::system::error_code ec,
1056 const std::string& objectPath) {
1057 if (ec)
1058 {
1059 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1060 messages::internalError(asyncResp->res);
1061 return;
1062 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001063
1064 sdbusplus::message::object_path path(objectPath);
1065 std::string certId = path.filename();
Ed Tanous002d39b2022-05-31 08:59:27 -07001066 std::string certURL =
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001067 "/redfish/v1/AccountService/LDAP/Certificates/" + certId;
1068 getCertificateProperties(asyncResp, objectPath,
1069 certs::ldapServiceName, certId, certURL,
1070 "LDAP Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001071 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1072 << certFile->getCertFilePath();
1073 },
1074 certs::ldapServiceName, certs::ldapObjectPath,
1075 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1076 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001077} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001078
1079/**
1080 * Certificate resource describes a certificate used to prove the identity
1081 * of a component, account or service.
1082 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001083inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001084{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001085 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001086 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001087 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001088 [&app](const crow::Request& req,
1089 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001090 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001091 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001092 {
1093 return;
1094 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001095
1096 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << id;
1097 std::string certURL =
1098 "/redfish/v1/AccountService/LDAP/Certificates/" + id;
1099 std::string objPath =
1100 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1101 getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id,
1102 certURL, "LDAP Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001103 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001104} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001105/**
1106 * Collection of TrustStoreCertificate certificates
1107 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001108inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001109{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001110 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001111 .privileges(redfish::privileges::getCertificate)
Ed Tanous002d39b2022-05-31 08:59:27 -07001112 .methods(boost::beast::http::verb::get)(
1113 [&app](const crow::Request& req,
1114 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001115 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001116 {
1117 return;
1118 }
1119
1120 asyncResp->res.jsonValue["@odata.id"] =
1121 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1122 asyncResp->res.jsonValue["@odata.type"] =
1123 "#CertificateCollection.CertificateCollection";
1124 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1125 asyncResp->res.jsonValue["Description"] =
1126 "A Collection of TrustStore certificate instances";
1127
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001128 getCertificateList(asyncResp, certs::authorityObjectPath,
1129 "/Members"_json_pointer,
1130 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001131 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001132
1133 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001134 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001135 .methods(boost::beast::http::verb::post)(
1136 [&app](const crow::Request& req,
1137 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001138 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001139 {
1140 return;
1141 }
1142 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1143
1144 if (certFileBody.empty())
1145 {
1146 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1147 messages::unrecognizedRequestBody(asyncResp->res);
1148 return;
1149 }
1150
1151 std::shared_ptr<CertificateFile> certFile =
1152 std::make_shared<CertificateFile>(certFileBody);
1153 crow::connections::systemBus->async_method_call(
1154 [asyncResp, certFile](const boost::system::error_code ec,
1155 const std::string& objectPath) {
1156 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001157 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001158 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1159 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001160 return;
1161 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001162
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001163 sdbusplus::message::object_path path(objectPath);
1164 std::string certId = path.filename();
1165 std::string certURL =
1166 "/redfish/v1/Managers/bmc/Truststore/Certificates/" + certId;
1167 getCertificateProperties(asyncResp, objectPath,
1168 certs::authorityServiceName, certId,
1169 certURL, "TrustStore Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001170 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1171 << certFile->getCertFilePath();
1172 },
1173 certs::authorityServiceName, certs::authorityObjectPath,
1174 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +08001175 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001176} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001177
1178/**
1179 * Certificate resource describes a certificate used to prove the identity
1180 * of a component, account or service.
1181 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001182inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001183{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001184 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001185 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001186 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001187 [&app](const crow::Request& req,
1188 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001189 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001190 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001191 {
1192 return;
1193 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001194
1195 BMCWEB_LOG_DEBUG << "Truststore Certificate ID=" << id;
Ed Tanous002d39b2022-05-31 08:59:27 -07001196 std::string certURL =
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001197 "/redfish/v1/Managers/bmc/Truststore/Certificates/" + id;
1198 std::string objPath =
1199 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1200 getCertificateProperties(asyncResp, objPath,
1201 certs::authorityServiceName, id, certURL,
1202 "TrustStore Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001203 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001204
1205 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001206 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001207 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001208 [&app](const crow::Request& req,
1209 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001210 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001211 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001212 {
1213 return;
1214 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001215
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001216 BMCWEB_LOG_DEBUG << "Delete TrustStore Certificate ID=" << id;
1217 std::string objPath =
1218 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001219
Ed Tanous002d39b2022-05-31 08:59:27 -07001220 crow::connections::systemBus->async_method_call(
1221 [asyncResp, id](const boost::system::error_code ec) {
1222 if (ec)
1223 {
1224 messages::resourceNotFound(asyncResp->res,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001225 "TrustStore Certificate", id);
Ed Tanous002d39b2022-05-31 08:59:27 -07001226 return;
1227 }
1228 BMCWEB_LOG_INFO << "Certificate deleted";
1229 asyncResp->res.result(boost::beast::http::status::no_content);
1230 },
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001231 certs::authorityServiceName, objPath, certs::objDeleteIntf,
Ed Tanous002d39b2022-05-31 08:59:27 -07001232 "Delete");
1233 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001234} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001235} // namespace redfish