blob: 22d77a728b3da7aafe7b13f4e6dc3b54802b5bba [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,
643 const std::string& objectPath, const std::string& service, long certId,
644 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600645{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600646 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
647 << " certId=" << certId << " certURl=" << certURL;
648 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800649 [asyncResp, certURL, certId,
650 name](const boost::system::error_code ec,
651 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700652 if (ec)
653 {
654 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
655 messages::resourceNotFound(asyncResp->res, name,
656 std::to_string(certId));
657 return;
658 }
659 asyncResp->res.jsonValue["@odata.id"] = certURL;
660 asyncResp->res.jsonValue["@odata.type"] =
661 "#Certificate.v1_0_0.Certificate";
662 asyncResp->res.jsonValue["Id"] = std::to_string(certId);
663 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 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700848 getCertificateProperties(asyncResp, objectPath, service, id,
849 certURI, name);
850 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,
873 const std::string& param) -> 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 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700878 if (param.empty())
879 {
880 messages::internalError(asyncResp->res);
881 return;
882 }
883 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600884
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700885 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
886 << std::to_string(id);
887 std::string certURL =
888 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
889 std::to_string(id);
890 std::string objectPath = certs::httpsObjectPath;
891 objectPath += "/";
892 objectPath += std::to_string(id);
893 getCertificateProperties(asyncResp, objectPath,
894 certs::httpsServiceName, id, certURL,
895 "HTTPS Certificate");
896 });
897}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600898
899/**
900 * Collection of HTTPS certificates
901 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700902inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600903{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700904 BMCWEB_ROUTE(app,
905 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700906 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700907 .methods(boost::beast::http::verb::get)(
908 [&app](const crow::Request& req,
909 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000910 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700911 {
912 return;
913 }
914
915 asyncResp->res.jsonValue["@odata.id"] =
916 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
917 asyncResp->res.jsonValue["@odata.type"] =
918 "#CertificateCollection.CertificateCollection";
919 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
920 asyncResp->res.jsonValue["Description"] =
921 "A Collection of HTTPS certificate instances";
922
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800923 getCertificateList(asyncResp, certs::httpsObjectPath,
924 "/Members"_json_pointer,
925 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700926 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600927
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700928 BMCWEB_ROUTE(app,
929 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700930 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700931 .methods(boost::beast::http::verb::post)(
932 [&app](const crow::Request& req,
933 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000934 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700935 {
936 return;
937 }
938 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
939
940 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
941 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
942
943 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
944
945 if (certFileBody.empty())
946 {
947 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
948 messages::unrecognizedRequestBody(asyncResp->res);
949 return;
950 }
951
952 std::shared_ptr<CertificateFile> certFile =
953 std::make_shared<CertificateFile>(certFileBody);
954
955 crow::connections::systemBus->async_method_call(
956 [asyncResp, certFile](const boost::system::error_code ec,
957 const std::string& objectPath) {
958 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700959 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700960 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
961 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700962 return;
963 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700964 long certId = getIDFromURL(objectPath);
965 if (certId < 0)
George Liu0fda0f12021-11-16 10:06:17 +0800966 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700967 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
968 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800969 return;
970 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700971 std::string certURL =
972 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
973 std::to_string(certId);
974 getCertificateProperties(asyncResp, objectPath,
975 certs::httpsServiceName, certId, certURL,
976 "HTTPS Certificate");
977 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
978 << certFile->getCertFilePath();
979 },
980 certs::httpsServiceName, certs::httpsObjectPath,
981 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +0800982 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700983} // requestRoutesHTTPSCertificateCollection
984
985/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600986 * The certificate location schema defines a resource that an administrator
987 * can use in order to locate all certificates installed on a given service.
988 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700989inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600990{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700991 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700992 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous002d39b2022-05-31 08:59:27 -0700993 .methods(boost::beast::http::verb::get)(
994 [&app](const crow::Request& req,
995 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000996 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700997 {
998 return;
999 }
1000 asyncResp->res.jsonValue["@odata.id"] =
1001 "/redfish/v1/CertificateService/CertificateLocations";
1002 asyncResp->res.jsonValue["@odata.type"] =
1003 "#CertificateLocations.v1_0_0.CertificateLocations";
1004 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
1005 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
1006 asyncResp->res.jsonValue["Description"] =
1007 "Defines a resource that an administrator can use in order to "
1008 "locate all certificates installed on a given service";
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001009
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001010 getCertificateList(asyncResp, certs::baseObjectPath,
1011 "/Links/Certificates"_json_pointer,
1012 "/Links/Certificates@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001013 });
1014}
1015// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001016
1017/**
1018 * Collection of LDAP certificates
1019 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001020inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001021{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001022 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001023 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001024 .methods(boost::beast::http::verb::get)(
1025 [&app](const crow::Request& req,
1026 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001027 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001028 {
1029 return;
1030 }
1031
1032 asyncResp->res.jsonValue["@odata.id"] =
1033 "/redfish/v1/AccountService/LDAP/Certificates";
1034 asyncResp->res.jsonValue["@odata.type"] =
1035 "#CertificateCollection.CertificateCollection";
1036 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1037 asyncResp->res.jsonValue["Description"] =
1038 "A Collection of LDAP certificate instances";
1039
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001040 getCertificateList(asyncResp, certs::ldapObjectPath,
1041 "/Members"_json_pointer,
1042 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001043 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001044
1045 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001046 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001047 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001048 [&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 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001055
Ed Tanous002d39b2022-05-31 08:59:27 -07001056 if (certFileBody.empty())
1057 {
1058 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1059 messages::unrecognizedRequestBody(asyncResp->res);
1060 return;
1061 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001062
Ed Tanous002d39b2022-05-31 08:59:27 -07001063 std::shared_ptr<CertificateFile> certFile =
1064 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001065
Ed Tanous002d39b2022-05-31 08:59:27 -07001066 crow::connections::systemBus->async_method_call(
1067 [asyncResp, certFile](const boost::system::error_code ec,
1068 const std::string& objectPath) {
1069 if (ec)
1070 {
1071 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1072 messages::internalError(asyncResp->res);
1073 return;
1074 }
1075 long certId = getIDFromURL(objectPath);
1076 if (certId < 0)
1077 {
1078 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
1079 messages::internalError(asyncResp->res);
1080 return;
1081 }
1082 std::string certURL =
1083 "/redfish/v1/AccountService/LDAP/Certificates/" +
1084 std::to_string(certId);
1085 getCertificateProperties(asyncResp, objectPath,
1086 certs::ldapServiceName, certId, certURL,
1087 "LDAP Certificate");
1088 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1089 << certFile->getCertFilePath();
1090 },
1091 certs::ldapServiceName, certs::ldapObjectPath,
1092 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1093 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001094} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001095
1096/**
1097 * Certificate resource describes a certificate used to prove the identity
1098 * of a component, account or service.
1099 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001100inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001101{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001102 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001103 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001104 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001105 [&app](const crow::Request& req,
1106 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1107 const std::string&) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001108 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001109 {
1110 return;
1111 }
1112 long id = getIDFromURL(req.url);
1113 if (id < 0)
1114 {
1115 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1116 messages::internalError(asyncResp->res);
1117 return;
1118 }
1119 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1120 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1121 std::to_string(id);
1122 std::string objectPath = certs::ldapObjectPath;
1123 objectPath += "/";
1124 objectPath += std::to_string(id);
1125 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1126 id, certURL, "LDAP Certificate");
1127 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001128} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001129/**
1130 * Collection of TrustStoreCertificate certificates
1131 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001132inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001133{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001134 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001135 .privileges(redfish::privileges::getCertificate)
Ed Tanous002d39b2022-05-31 08:59:27 -07001136 .methods(boost::beast::http::verb::get)(
1137 [&app](const crow::Request& req,
1138 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001139 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001140 {
1141 return;
1142 }
1143
1144 asyncResp->res.jsonValue["@odata.id"] =
1145 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1146 asyncResp->res.jsonValue["@odata.type"] =
1147 "#CertificateCollection.CertificateCollection";
1148 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1149 asyncResp->res.jsonValue["Description"] =
1150 "A Collection of TrustStore certificate instances";
1151
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001152 getCertificateList(asyncResp, certs::authorityObjectPath,
1153 "/Members"_json_pointer,
1154 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001155 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001156
1157 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001158 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001159 .methods(boost::beast::http::verb::post)(
1160 [&app](const crow::Request& req,
1161 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001162 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001163 {
1164 return;
1165 }
1166 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1167
1168 if (certFileBody.empty())
1169 {
1170 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1171 messages::unrecognizedRequestBody(asyncResp->res);
1172 return;
1173 }
1174
1175 std::shared_ptr<CertificateFile> certFile =
1176 std::make_shared<CertificateFile>(certFileBody);
1177 crow::connections::systemBus->async_method_call(
1178 [asyncResp, certFile](const boost::system::error_code ec,
1179 const std::string& objectPath) {
1180 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001181 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001182 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1183 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001184 return;
1185 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001186 long certId = getIDFromURL(objectPath);
1187 if (certId < 0)
George Liu0fda0f12021-11-16 10:06:17 +08001188 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001189 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
1190 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +08001191 return;
1192 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001193 std::string certURL =
1194 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1195 std::to_string(certId);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001196
Ed Tanous002d39b2022-05-31 08:59:27 -07001197 getCertificateProperties(asyncResp, objectPath,
1198 certs::authorityServiceName, certId,
1199 certURL, "TrustStore Certificate");
1200 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1201 << certFile->getCertFilePath();
1202 },
1203 certs::authorityServiceName, certs::authorityObjectPath,
1204 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +08001205 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001206} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001207
1208/**
1209 * Certificate resource describes a certificate used to prove the identity
1210 * of a component, account or service.
1211 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001212inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001213{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001214 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001215 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001216 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001217 [&app](const crow::Request& req,
1218 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1219 const std::string&) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001220 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001221 {
1222 return;
1223 }
1224 long id = getIDFromURL(req.url);
1225 if (id < 0)
1226 {
1227 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1228 messages::internalError(asyncResp->res);
1229 return;
1230 }
1231 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1232 << std::to_string(id);
1233 std::string certURL =
1234 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1235 std::to_string(id);
1236 std::string objectPath = certs::authorityObjectPath;
1237 objectPath += "/";
1238 objectPath += std::to_string(id);
1239 getCertificateProperties(asyncResp, objectPath,
1240 certs::authorityServiceName, id, certURL,
1241 "TrustStore Certificate");
1242 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001243
1244 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001245 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001246 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001247 [&app](const crow::Request& req,
1248 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1249 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001250 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001251 {
1252 return;
1253 }
1254 if (param.empty())
1255 {
1256 messages::internalError(asyncResp->res);
1257 return;
1258 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001259
Ed Tanous002d39b2022-05-31 08:59:27 -07001260 long id = getIDFromURL(req.url);
1261 if (id < 0)
1262 {
1263 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1264 messages::resourceNotFound(asyncResp->res, "TrustStore Certificate",
1265 std::string(req.url));
1266 return;
1267 }
1268 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1269 << std::to_string(id);
1270 std::string certPath = certs::authorityObjectPath;
1271 certPath += "/";
1272 certPath += std::to_string(id);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001273
Ed Tanous002d39b2022-05-31 08:59:27 -07001274 crow::connections::systemBus->async_method_call(
1275 [asyncResp, id](const boost::system::error_code ec) {
1276 if (ec)
1277 {
1278 messages::resourceNotFound(asyncResp->res,
1279 "TrustStore Certificate",
1280 std::to_string(id));
1281 return;
1282 }
1283 BMCWEB_LOG_INFO << "Certificate deleted";
1284 asyncResp->res.result(boost::beast::http::status::no_content);
1285 },
1286 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1287 "Delete");
1288 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001289} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001290} // namespace redfish