blob: 9bea9b98c45ff8a2a7ddce23a85b401a0aa7988b [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001#pragma once
2
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003#include <app.hpp>
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +02004#include <boost/convert.hpp>
5#include <boost/convert/strtol.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -08006#include <dbus_utility.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -07007#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -07008#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05009
Marri Devender Rao5968cae2019-01-21 10:27:12 -060010namespace redfish
11{
12namespace certs
13{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050014constexpr char const* httpsObjectPath =
Marri Devender Rao5968cae2019-01-21 10:27:12 -060015 "/xyz/openbmc_project/certs/server/https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050016constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
17constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
18constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
19constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
20constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
21constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
22constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
23constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060024 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050025constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060026 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050027constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050028 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050029constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050030 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060031} // namespace certs
32
33/**
34 * The Certificate schema defines a Certificate Service which represents the
35 * actions available to manage certificates and links to where certificates
36 * are installed.
37 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060038
John Edward Broadbent7e860f12021-04-08 15:57:16 -070039// TODO: Issue#61 No entries are available for Certificate
40// service at https://www.dmtf.org/standards/redfish
41// "redfish standard registries". Need to modify after DMTF
42// publish Privilege details for certificate service
43
44inline void requestRoutesCertificateService(App& app)
45{
46 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
Ed Tanoused398212021-06-09 17:05:54 -070047 .privileges(redfish::privileges::getCertificateService)
Ed Tanous45ca1b82022-03-25 13:07:27 -070048 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
49 const std::shared_ptr<
50 bmcweb::AsyncResp>&
51 asyncResp) {
52 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
53 {
54 return;
55 }
Abhishek Patel72048782021-06-02 09:53:24 -050056 asyncResp->res.jsonValue = {
57 {"@odata.type",
58 "#CertificateService.v1_0_0.CertificateService"},
59 {"@odata.id", "/redfish/v1/CertificateService"},
60 {"Id", "CertificateService"},
61 {"Name", "Certificate Service"},
62 {"Description", "Actions available to manage certificates"}};
63 // /redfish/v1/CertificateService/CertificateLocations is something
64 // only ConfigureManager can access then only display when the user
65 // has permissions ConfigureManager
66 Privileges effectiveUserPrivileges =
67 redfish::getUserPrivileges(req.userRole);
68 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
69 effectiveUserPrivileges))
70 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -070071 asyncResp->res.jsonValue["CertificateLocations"] = {
72 {"@odata.id",
73 "/redfish/v1/CertificateService/CertificateLocations"}};
Abhishek Patel72048782021-06-02 09:53:24 -050074 }
George Liu0fda0f12021-11-16 10:06:17 +080075 asyncResp->res
76 .jsonValue["Actions"]
77 ["#CertificateService.ReplaceCertificate"] = {
78 {"target",
79 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"},
80 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
Abhishek Patel72048782021-06-02 09:53:24 -050081 asyncResp->res
82 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
George Liu0fda0f12021-11-16 10:06:17 +080083 {"target",
84 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR"}};
Abhishek Patel72048782021-06-02 09:53:24 -050085 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070086} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060087
Marri Devender Rao5968cae2019-01-21 10:27:12 -060088/**
89 * @brief Find the ID specified in the URL
90 * Finds the numbers specified after the last "/" in the URL and returns.
91 * @param[in] path URL
92 * @return -1 on failure and number on success
93 */
Ed Tanous23a21a12020-07-25 04:45:05 +000094inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060095{
Ed Tanousf23b7292020-10-15 09:41:17 -070096 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -060097 if (found == std::string::npos)
98 {
99 return -1;
100 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200101
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600102 if ((found + 1) < url.length())
103 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600104 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200105
106 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600107 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200108
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600109 return -1;
110}
111
zhanghch058d1b46d2021-04-01 11:18:24 +0800112inline std::string getCertificateFromReqBody(
113 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
114 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200115{
116 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
117
118 if (reqJson.is_discarded())
119 {
120 // We did not receive JSON request, proceed as it is RAW data
121 return req.body;
122 }
123
124 std::string certificate;
125 std::optional<std::string> certificateType = "PEM";
126
Willy Tu15ed6782021-12-14 11:03:16 -0800127 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
128 certificate, "CertificateType",
129 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200130 {
131 BMCWEB_LOG_ERROR << "Required parameters are missing";
132 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700133 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200134 }
135
136 if (*certificateType != "PEM")
137 {
138 messages::propertyValueNotInList(asyncResp->res, *certificateType,
139 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700140 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200141 }
142
143 return certificate;
144}
145
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600146/**
147 * Class to create a temporary certificate file for uploading to system
148 */
149class CertificateFile
150{
151 public:
152 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500153 CertificateFile(const CertificateFile&) = delete;
154 CertificateFile& operator=(const CertificateFile&) = delete;
155 CertificateFile(CertificateFile&&) = delete;
156 CertificateFile& operator=(CertificateFile&&) = delete;
157 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600158 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700159 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700160 'e', 'r', 't', 's', '.', 'X',
161 'X', 'X', 'X', 'X', 'X', '\0'};
162 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800163 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600164 {
165 certDirectory = tempDirectory;
166 certificateFile = certDirectory / "cert.pem";
167 std::ofstream out(certificateFile, std::ofstream::out |
168 std::ofstream::binary |
169 std::ofstream::trunc);
170 out << certString;
171 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800172 BMCWEB_LOG_DEBUG << "Creating certificate file"
173 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600174 }
175 }
176 ~CertificateFile()
177 {
178 if (std::filesystem::exists(certDirectory))
179 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800180 BMCWEB_LOG_DEBUG << "Removing certificate file"
181 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000182 std::error_code ec;
183 std::filesystem::remove_all(certDirectory, ec);
184 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600185 {
186 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800187 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600188 }
189 }
190 }
191 std::string getCertFilePath()
192 {
193 return certificateFile;
194 }
195
196 private:
197 std::filesystem::path certificateFile;
198 std::filesystem::path certDirectory;
199};
200
Marri Devender Rao30215812019-03-18 08:59:21 -0500201static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
202/**
203 * @brief Read data from CSR D-bus object and set to response
204 *
205 * @param[in] asyncResp Shared pointer to the response message
206 * @param[in] certURI Link to certifiate collection URI
207 * @param[in] service D-Bus service name
208 * @param[in] certObjPath certificate D-Bus object path
209 * @param[in] csrObjPath CSR D-Bus object path
210 * @return None
211 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800212static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500213 const std::string& certURI, const std::string& service,
214 const std::string& certObjPath,
215 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500216{
217 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
218 << " CSRObjectPath=" << csrObjPath
219 << " service=" << service;
220 crow::connections::systemBus->async_method_call(
221 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500222 const std::string& csr) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500223 if (ec)
224 {
225 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
226 messages::internalError(asyncResp->res);
227 return;
228 }
229 if (csr.empty())
230 {
231 BMCWEB_LOG_ERROR << "CSR read is empty";
232 messages::internalError(asyncResp->res);
233 return;
234 }
235 asyncResp->res.jsonValue["CSRString"] = csr;
236 asyncResp->res.jsonValue["CertificateCollection"] = {
237 {"@odata.id", certURI}};
238 },
239 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
240}
241
242/**
243 * Action to Generate CSR
244 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700245inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500246{
George Liu0fda0f12021-11-16 10:06:17 +0800247 BMCWEB_ROUTE(
248 app,
249 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
Ed Tanoused398212021-06-09 17:05:54 -0700250 // Incorrect Privilege; Should be ConfigureManager
251 //.privileges(redfish::privileges::postCertificateService)
Ed Tanous432a8902021-06-14 15:28:56 -0700252 .privileges({{"ConfigureComponents"}})
George Liu0fda0f12021-11-16 10:06:17 +0800253 .methods(
254 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700255 post)([&app](
256 const crow::Request& req,
257 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
258 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
259 {
260 return;
261 }
George Liu0fda0f12021-11-16 10:06:17 +0800262 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500263
George Liu0fda0f12021-11-16 10:06:17 +0800264 // Required parameters
265 std::string city;
266 std::string commonName;
267 std::string country;
268 std::string organization;
269 std::string organizationalUnit;
270 std::string state;
271 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800272
George Liu0fda0f12021-11-16 10:06:17 +0800273 // Optional parameters
274 std::optional<std::vector<std::string>> optAlternativeNames =
275 std::vector<std::string>();
276 std::optional<std::string> optContactPerson = "";
277 std::optional<std::string> optChallengePassword = "";
278 std::optional<std::string> optEmail = "";
279 std::optional<std::string> optGivenName = "";
280 std::optional<std::string> optInitials = "";
281 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
282 std::optional<std::string> optKeyCurveId = "secp384r1";
283 std::optional<std::string> optKeyPairAlgorithm = "EC";
284 std::optional<std::vector<std::string>> optKeyUsage =
285 std::vector<std::string>();
286 std::optional<std::string> optSurname = "";
287 std::optional<std::string> optUnstructuredName = "";
Willy Tu15ed6782021-12-14 11:03:16 -0800288 if (!json_util::readJsonAction(
George Liu0fda0f12021-11-16 10:06:17 +0800289 req, asyncResp->res, "City", city, "CommonName", commonName,
290 "ContactPerson", optContactPerson, "Country", country,
291 "Organization", organization, "OrganizationalUnit",
292 organizationalUnit, "State", state, "CertificateCollection",
293 certificateCollection, "AlternativeNames",
294 optAlternativeNames, "ChallengePassword",
295 optChallengePassword, "Email", optEmail, "GivenName",
296 optGivenName, "Initials", optInitials, "KeyBitLength",
297 optKeyBitLength, "KeyCurveId", optKeyCurveId,
298 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
299 optKeyUsage, "Surname", optSurname, "UnstructuredName",
300 optUnstructuredName))
301 {
302 return;
303 }
304
305 // bmcweb has no way to store or decode a private key challenge
306 // password, which will likely cause bmcweb to crash on startup
307 // if this is not set on a post so not allowing the user to set
308 // value
Ed Tanous26f69762022-01-25 09:49:11 -0800309 if (!optChallengePassword->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800310 {
311 messages::actionParameterNotSupported(
312 asyncResp->res, "GenerateCSR", "ChallengePassword");
313 return;
314 }
315
316 std::string certURI;
317 if (!redfish::json_util::readJson(certificateCollection,
318 asyncResp->res, "@odata.id",
319 certURI))
320 {
321 return;
322 }
323
324 std::string objectPath;
325 std::string service;
326 if (boost::starts_with(
327 certURI,
328 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
329 {
330 objectPath = certs::httpsObjectPath;
331 service = certs::httpsServiceName;
332 }
333 else if (boost::starts_with(
334 certURI,
335 "/redfish/v1/AccountService/LDAP/Certificates"))
336 {
337 objectPath = certs::ldapObjectPath;
338 service = certs::ldapServiceName;
339 }
340 else
341 {
342 messages::actionParameterNotSupported(
343 asyncResp->res, "CertificateCollection", "GenerateCSR");
344 return;
345 }
346
347 // supporting only EC and RSA algorithm
348 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
349 {
350 messages::actionParameterNotSupported(
351 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
352 return;
353 }
354
355 // supporting only 2048 key bit length for RSA algorithm due to
356 // time consumed in generating private key
357 if (*optKeyPairAlgorithm == "RSA" &&
358 *optKeyBitLength != rsaKeyBitLength)
359 {
360 messages::propertyValueNotInList(
361 asyncResp->res, std::to_string(*optKeyBitLength),
362 "KeyBitLength");
363 return;
364 }
365
366 // validate KeyUsage supporting only 1 type based on URL
367 if (boost::starts_with(
368 certURI,
369 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
370 {
Ed Tanous26f69762022-01-25 09:49:11 -0800371 if (optKeyUsage->empty())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700372 {
George Liu0fda0f12021-11-16 10:06:17 +0800373 optKeyUsage->push_back("ServerAuthentication");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700374 }
George Liu0fda0f12021-11-16 10:06:17 +0800375 else if (optKeyUsage->size() == 1)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700376 {
George Liu0fda0f12021-11-16 10:06:17 +0800377 if ((*optKeyUsage)[0] != "ServerAuthentication")
378 {
379 messages::propertyValueNotInList(
380 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
381 return;
382 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700383 }
384 else
385 {
386 messages::actionParameterNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800387 asyncResp->res, "KeyUsage", "GenerateCSR");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700388 return;
389 }
George Liu0fda0f12021-11-16 10:06:17 +0800390 }
391 else if (boost::starts_with(
392 certURI,
393 "/redfish/v1/AccountService/LDAP/Certificates"))
394 {
Ed Tanous26f69762022-01-25 09:49:11 -0800395 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800396 {
397 optKeyUsage->push_back("ClientAuthentication");
398 }
399 else if (optKeyUsage->size() == 1)
400 {
401 if ((*optKeyUsage)[0] != "ClientAuthentication")
402 {
403 messages::propertyValueNotInList(
404 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
405 return;
406 }
407 }
408 else
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700409 {
410 messages::actionParameterNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800411 asyncResp->res, "KeyUsage", "GenerateCSR");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700412 return;
413 }
George Liu0fda0f12021-11-16 10:06:17 +0800414 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500415
George Liu0fda0f12021-11-16 10:06:17 +0800416 // Only allow one CSR matcher at a time so setting retry
417 // time-out and timer expiry to 10 seconds for now.
418 static const int timeOut = 10;
419 if (csrMatcher)
420 {
421 messages::serviceTemporarilyUnavailable(
422 asyncResp->res, std::to_string(timeOut));
423 return;
424 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500425
George Liu0fda0f12021-11-16 10:06:17 +0800426 // Make this static so it survives outside this method
427 static boost::asio::steady_timer timeout(*req.ioService);
428 timeout.expires_after(std::chrono::seconds(timeOut));
429 timeout.async_wait(
430 [asyncResp](const boost::system::error_code& ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700431 csrMatcher = nullptr;
432 if (ec)
433 {
434 // operation_aborted is expected if timer is canceled
435 // before completion.
436 if (ec != boost::asio::error::operation_aborted)
437 {
438 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
439 }
440 return;
441 }
442 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
443 messages::internalError(asyncResp->res);
444 });
445
George Liu0fda0f12021-11-16 10:06:17 +0800446 // create a matcher to wait on CSR object
447 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
448 std::string match("type='signal',"
449 "interface='org.freedesktop.DBus.ObjectManager',"
450 "path='" +
451 objectPath +
452 "',"
453 "member='InterfacesAdded'");
454 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
455 *crow::connections::systemBus, match,
456 [asyncResp, service, objectPath,
457 certURI](sdbusplus::message::message& m) {
458 timeout.cancel();
459 if (m.is_method_error())
460 {
461 BMCWEB_LOG_ERROR << "Dbus method error!!!";
462 messages::internalError(asyncResp->res);
463 return;
464 }
Ed Tanousb9d36b42022-02-26 21:42:46 -0800465
466 dbus::utility::DBusInteracesMap interfacesProperties;
467
George Liu0fda0f12021-11-16 10:06:17 +0800468 sdbusplus::message::object_path csrObjectPath;
469 m.read(csrObjectPath, interfacesProperties);
470 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
471 for (auto& interface : interfacesProperties)
472 {
473 if (interface.first == "xyz.openbmc_project.Certs.CSR")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700474 {
George Liu0fda0f12021-11-16 10:06:17 +0800475 getCSR(asyncResp, certURI, service, objectPath,
476 csrObjectPath.str);
477 break;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700478 }
George Liu0fda0f12021-11-16 10:06:17 +0800479 }
480 });
481 crow::connections::systemBus->async_method_call(
Ed Tanous914e2d52022-01-07 11:38:34 -0800482 [asyncResp](const boost::system::error_code ec,
George Liu0fda0f12021-11-16 10:06:17 +0800483 const std::string&) {
484 if (ec)
485 {
486 BMCWEB_LOG_ERROR << "DBUS response error: "
487 << ec.message();
488 messages::internalError(asyncResp->res);
489 return;
490 }
491 },
492 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
493 "GenerateCSR", *optAlternativeNames, *optChallengePassword,
494 city, commonName, *optContactPerson, country, *optEmail,
495 *optGivenName, *optInitials, *optKeyBitLength, *optKeyCurveId,
496 *optKeyPairAlgorithm, *optKeyUsage, organization,
497 organizationalUnit, state, *optSurname, *optUnstructuredName);
498 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700499} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500500
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600501/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500502 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600503 *
504 * @param[in] asyncResp Shared pointer to the response message
505 * @param[in] str Issuer/Subject value in key=value pairs
506 * @param[in] type Issuer/Subject
507 * @return None
508 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500509static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600510 const std::string_view value)
511{
512 // example: O=openbmc-project.xyz,CN=localhost
513 std::string_view::iterator i = value.begin();
514 while (i != value.end())
515 {
516 std::string_view::iterator 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 }
521 if (i == value.end())
522 {
523 break;
524 }
Ed Tanous271584a2019-07-09 16:24:22 -0700525 const std::string_view key(tokenBegin,
526 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530527 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600528 tokenBegin = i;
529 while (i != value.end() && *i != ',')
530 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530531 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600532 }
Ed Tanous271584a2019-07-09 16:24:22 -0700533 const std::string_view val(tokenBegin,
534 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600535 if (key == "L")
536 {
537 out["City"] = val;
538 }
539 else if (key == "CN")
540 {
541 out["CommonName"] = val;
542 }
543 else if (key == "C")
544 {
545 out["Country"] = val;
546 }
547 else if (key == "O")
548 {
549 out["Organization"] = val;
550 }
551 else if (key == "OU")
552 {
553 out["OrganizationalUnit"] = val;
554 }
555 else if (key == "ST")
556 {
557 out["State"] = val;
558 }
559 // skip comma character
560 if (i != value.end())
561 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530562 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600563 }
564 }
565}
566
567/**
568 * @brief Retrieve the certificates properties and append to the response
569 * message
570 *
571 * @param[in] asyncResp Shared pointer to the response message
572 * @param[in] objectPath Path of the D-Bus service object
573 * @param[in] certId Id of the certificate
574 * @param[in] certURL URL of the certificate object
575 * @param[in] name name of the certificate
576 * @return None
577 */
578static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800579 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
580 const std::string& objectPath, const std::string& service, long certId,
581 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600582{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600583 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
584 << " certId=" << certId << " certURl=" << certURL;
585 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800586 [asyncResp, certURL, certId,
587 name](const boost::system::error_code ec,
588 const dbus::utility::DBusPropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600589 if (ec)
590 {
591 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500592 messages::resourceNotFound(asyncResp->res, name,
593 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600594 return;
595 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600596 asyncResp->res.jsonValue = {
597 {"@odata.id", certURL},
598 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600599 {"Id", std::to_string(certId)},
600 {"Name", name},
601 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500602 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600603 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600604 if (property.first == "CertificateString")
605 {
606 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500607 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600608 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800609 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600610 {
611 asyncResp->res.jsonValue["CertificateString"] = *value;
612 }
613 }
614 else if (property.first == "KeyUsage")
615 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500616 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600617 asyncResp->res.jsonValue["KeyUsage"];
618 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500619 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600620 std::get_if<std::vector<std::string>>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800621 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600622 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500623 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600624 {
625 keyUsage.push_back(usage);
626 }
627 }
628 }
629 else if (property.first == "Issuer")
630 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500631 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600632 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800633 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600634 {
635 updateCertIssuerOrSubject(
636 asyncResp->res.jsonValue["Issuer"], *value);
637 }
638 }
639 else if (property.first == "Subject")
640 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500641 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600642 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800643 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600644 {
645 updateCertIssuerOrSubject(
646 asyncResp->res.jsonValue["Subject"], *value);
647 }
648 }
649 else if (property.first == "ValidNotAfter")
650 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500651 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600652 std::get_if<uint64_t>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800653 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600654 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600655 asyncResp->res.jsonValue["ValidNotAfter"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800656 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600657 }
658 }
659 else if (property.first == "ValidNotBefore")
660 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500661 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600662 std::get_if<uint64_t>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800663 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600664 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600665 asyncResp->res.jsonValue["ValidNotBefore"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800666 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600667 }
668 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600669 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600670 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600671 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600672 service, objectPath, certs::dbusPropIntf, "GetAll",
673 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600674}
675
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600676/**
677 * Action to replace an existing certificate
678 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700679inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600680{
George Liu0fda0f12021-11-16 10:06:17 +0800681 BMCWEB_ROUTE(
682 app,
683 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700684 .privileges(redfish::privileges::postCertificateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700685 .methods(
686 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700687 post)([&app](
688 const crow::Request& req,
689 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
690 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
691 {
692 return;
693 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700694 std::string certificate;
695 nlohmann::json certificateUri;
696 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600697
Willy Tu15ed6782021-12-14 11:03:16 -0800698 if (!json_util::readJsonAction(req, asyncResp->res,
699 "CertificateString", certificate,
700 "CertificateUri", certificateUri,
701 "CertificateType", certificateType))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700702 {
703 BMCWEB_LOG_ERROR << "Required parameters are missing";
704 messages::internalError(asyncResp->res);
705 return;
706 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800707
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700708 if (!certificateType)
709 {
710 // should never happen, but it never hurts to be paranoid.
711 return;
712 }
713 if (certificateType != "PEM")
714 {
715 messages::actionParameterNotSupported(
716 asyncResp->res, "CertificateType", "ReplaceCertificate");
717 return;
718 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600719
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700720 std::string certURI;
721 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
722 "@odata.id", certURI))
723 {
724 messages::actionParameterMissing(
725 asyncResp->res, "ReplaceCertificate", "CertificateUri");
726 return;
727 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600728
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700729 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
730 long id = getIDFromURL(certURI);
731 if (id < 0)
732 {
733 messages::actionParameterValueFormatError(
734 asyncResp->res, certURI, "CertificateUri",
735 "ReplaceCertificate");
736 return;
737 }
738 std::string objectPath;
739 std::string name;
740 std::string service;
George Liu0fda0f12021-11-16 10:06:17 +0800741 if (boost::starts_with(
742 certURI,
743 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700744 {
745 objectPath = std::string(certs::httpsObjectPath) + "/" +
746 std::to_string(id);
747 name = "HTTPS certificate";
748 service = certs::httpsServiceName;
749 }
750 else if (boost::starts_with(
751 certURI,
752 "/redfish/v1/AccountService/LDAP/Certificates/"))
753 {
754 objectPath = std::string(certs::ldapObjectPath) + "/" +
755 std::to_string(id);
756 name = "LDAP certificate";
757 service = certs::ldapServiceName;
758 }
759 else if (boost::starts_with(
760 certURI,
761 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
762 {
763 objectPath = std::string(certs::authorityObjectPath) + "/" +
764 std::to_string(id);
765 name = "TrustStore certificate";
766 service = certs::authorityServiceName;
767 }
768 else
769 {
770 messages::actionParameterNotSupported(
771 asyncResp->res, "CertificateUri", "ReplaceCertificate");
772 return;
773 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600774
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700775 std::shared_ptr<CertificateFile> certFile =
776 std::make_shared<CertificateFile>(certificate);
777 crow::connections::systemBus->async_method_call(
778 [asyncResp, certFile, objectPath, service, certURI, id,
779 name](const boost::system::error_code ec) {
780 if (ec)
781 {
782 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
783 messages::resourceNotFound(asyncResp->res, name,
784 std::to_string(id));
785 return;
786 }
787 getCertificateProperties(asyncResp, objectPath, service, id,
788 certURI, name);
789 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
790 << certFile->getCertFilePath();
791 },
792 service, objectPath, certs::certReplaceIntf, "Replace",
793 certFile->getCertFilePath());
794 });
795} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600796
797/**
798 * Certificate resource describes a certificate used to prove the identity
799 * of a component, account or service.
800 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700801
802inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600803{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700804 BMCWEB_ROUTE(
805 app,
806 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700807 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700808 .methods(
809 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700810 get)([&app](const crow::Request& req,
811 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
812 const std::string& param) -> void {
813 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
814 {
815 return;
816 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700817 if (param.empty())
818 {
819 messages::internalError(asyncResp->res);
820 return;
821 }
822 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600823
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700824 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
825 << std::to_string(id);
826 std::string certURL =
827 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
828 std::to_string(id);
829 std::string objectPath = certs::httpsObjectPath;
830 objectPath += "/";
831 objectPath += std::to_string(id);
832 getCertificateProperties(asyncResp, objectPath,
833 certs::httpsServiceName, id, certURL,
834 "HTTPS Certificate");
835 });
836}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600837
838/**
839 * Collection of HTTPS certificates
840 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700841inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600842{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700843 BMCWEB_ROUTE(app,
844 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700845 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700846 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
847 const std::shared_ptr<
848 bmcweb::AsyncResp>&
849 asyncResp) {
850 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
851 {
852 return;
853 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700854 asyncResp->res.jsonValue = {
855 {"@odata.id",
856 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
857 {"@odata.type", "#CertificateCollection.CertificateCollection"},
858 {"Name", "HTTPS Certificates Collection"},
859 {"Description", "A Collection of HTTPS certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +0800860
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700861 crow::connections::systemBus->async_method_call(
862 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800863 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700864 if (ec)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600865 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700866 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
867 messages::internalError(asyncResp->res);
868 return;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600869 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700870 nlohmann::json& members =
871 asyncResp->res.jsonValue["Members"];
872 members = nlohmann::json::array();
873 for (const auto& cert : certs)
874 {
875 long id = getIDFromURL(cert.first.str);
876 if (id >= 0)
877 {
878 members.push_back(
879 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +0800880 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700881 std::to_string(id)}});
882 }
883 }
884 asyncResp->res.jsonValue["Members@odata.count"] =
885 members.size();
886 },
887 certs::httpsServiceName, certs::httpsObjectPath,
888 certs::dbusObjManagerIntf, "GetManagedObjects");
889 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600890
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700891 BMCWEB_ROUTE(app,
892 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700893 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +0800894 .methods(
895 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700896 post)([&app](
897 const crow::Request& req,
898 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
899 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
900 {
901 return;
902 }
George Liu0fda0f12021-11-16 10:06:17 +0800903 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
zhanghch058d1b46d2021-04-01 11:18:24 +0800904
George Liu0fda0f12021-11-16 10:06:17 +0800905 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
906 {"Description", "HTTPS Certificate"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600907
George Liu0fda0f12021-11-16 10:06:17 +0800908 std::string certFileBody =
909 getCertificateFromReqBody(asyncResp, req);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200910
George Liu0fda0f12021-11-16 10:06:17 +0800911 if (certFileBody.empty())
912 {
913 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
914 messages::unrecognizedRequestBody(asyncResp->res);
915 return;
916 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700917
George Liu0fda0f12021-11-16 10:06:17 +0800918 std::shared_ptr<CertificateFile> certFile =
919 std::make_shared<CertificateFile>(certFileBody);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700920
George Liu0fda0f12021-11-16 10:06:17 +0800921 crow::connections::systemBus->async_method_call(
922 [asyncResp, certFile](const boost::system::error_code ec,
923 const std::string& objectPath) {
924 if (ec)
925 {
926 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
927 messages::internalError(asyncResp->res);
928 return;
929 }
930 long certId = getIDFromURL(objectPath);
931 if (certId < 0)
932 {
933 BMCWEB_LOG_ERROR << "Invalid objectPath value"
934 << objectPath;
935 messages::internalError(asyncResp->res);
936 return;
937 }
938 std::string certURL =
939 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
940 std::to_string(certId);
941 getCertificateProperties(asyncResp, objectPath,
942 certs::httpsServiceName, certId,
943 certURL, "HTTPS Certificate");
944 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
945 << certFile->getCertFilePath();
946 },
947 certs::httpsServiceName, certs::httpsObjectPath,
948 certs::certInstallIntf, "Install", certFile->getCertFilePath());
949 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700950} // requestRoutesHTTPSCertificateCollection
951
952/**
953 * @brief Retrieve the certificates installed list and append to the
954 * response
955 *
956 * @param[in] asyncResp Shared pointer to the response message
957 * @param[in] certURL Path of the certificate object
958 * @param[in] path Path of the D-Bus service object
959 * @return None
960 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700961inline void
962 getCertificateLocations(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
963 const std::string& certURL, const std::string& path,
964 const std::string& service)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700965{
966 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
967 << " Path=" << path << " service= " << service;
968 crow::connections::systemBus->async_method_call(
969 [asyncResp, certURL](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800970 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700971 if (ec)
972 {
973 BMCWEB_LOG_WARNING
974 << "Certificate collection query failed: " << ec
975 << ", skipping " << certURL;
976 return;
977 }
978 nlohmann::json& links =
979 asyncResp->res.jsonValue["Links"]["Certificates"];
Ed Tanous9eb808c2022-01-25 10:19:23 -0800980 for (const auto& cert : certs)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700981 {
982 long id = getIDFromURL(cert.first.str);
983 if (id >= 0)
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200984 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700985 links.push_back(
986 {{"@odata.id", certURL + std::to_string(id)}});
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200987 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700988 }
989 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
990 links.size();
991 },
992 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
993}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600994
995/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600996 * The certificate location schema defines a resource that an administrator
997 * can use in order to locate all certificates installed on a given service.
998 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700999inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001000{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001001 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -07001002 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001003 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1004 const std::shared_ptr<
1005 bmcweb::AsyncResp>&
1006 asyncResp) {
1007 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1008 {
1009 return;
1010 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001011 asyncResp->res.jsonValue = {
1012 {"@odata.id",
1013 "/redfish/v1/CertificateService/CertificateLocations"},
1014 {"@odata.type",
1015 "#CertificateLocations.v1_0_0.CertificateLocations"},
1016 {"Name", "Certificate Locations"},
1017 {"Id", "CertificateLocations"},
1018 {"Description",
1019 "Defines a resource that an administrator can use in order to "
1020 "locate all certificates installed on a given service"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001021
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001022 nlohmann::json& links =
1023 asyncResp->res.jsonValue["Links"]["Certificates"];
1024 links = nlohmann::json::array();
1025 getCertificateLocations(
1026 asyncResp,
1027 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
1028 certs::httpsObjectPath, certs::httpsServiceName);
1029 getCertificateLocations(
1030 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/",
1031 certs::ldapObjectPath, certs::ldapServiceName);
1032 getCertificateLocations(
1033 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1034 certs::authorityObjectPath, certs::authorityServiceName);
1035 });
1036}
1037// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001038
1039/**
1040 * Collection of LDAP certificates
1041 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001042inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001043{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001044 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001045 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001046 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1047 const std::shared_ptr<
1048 bmcweb::AsyncResp>&
1049 asyncResp) {
1050 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1051 {
1052 return;
1053 }
George Liu0fda0f12021-11-16 10:06:17 +08001054 asyncResp->res.jsonValue = {
1055 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1056 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1057 {"Name", "LDAP Certificates Collection"},
1058 {"Description", "A Collection of LDAP certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001059
George Liu0fda0f12021-11-16 10:06:17 +08001060 crow::connections::systemBus->async_method_call(
1061 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001062 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001063 nlohmann::json& members =
1064 asyncResp->res.jsonValue["Members"];
1065 nlohmann::json& count =
1066 asyncResp->res.jsonValue["Members@odata.count"];
1067 members = nlohmann::json::array();
1068 count = 0;
1069 if (ec)
1070 {
1071 BMCWEB_LOG_WARNING << "LDAP certificate query failed: "
1072 << ec;
1073 return;
1074 }
1075 for (const auto& cert : certs)
1076 {
1077 long id = getIDFromURL(cert.first.str);
1078 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001079 {
George Liu0fda0f12021-11-16 10:06:17 +08001080 members.push_back(
1081 {{"@odata.id",
1082 "/redfish/v1/AccountService/LDAP/Certificates/" +
1083 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001084 }
George Liu0fda0f12021-11-16 10:06:17 +08001085 }
1086 count = members.size();
1087 },
1088 certs::ldapServiceName, certs::ldapObjectPath,
1089 certs::dbusObjManagerIntf, "GetManagedObjects");
1090 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001091
1092 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001093 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001094 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001095 [&app](const crow::Request& req,
1096 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1097 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1098 {
1099 return;
1100 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001101 std::string certFileBody =
1102 getCertificateFromReqBody(asyncResp, req);
1103
1104 if (certFileBody.empty())
Marri Devender Rao37cce912019-02-20 01:05:22 -06001105 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001106 BMCWEB_LOG_ERROR
1107 << "Cannot get certificate from request body.";
1108 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001109 return;
1110 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001111
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001112 std::shared_ptr<CertificateFile> certFile =
1113 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001114
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001115 crow::connections::systemBus->async_method_call(
1116 [asyncResp, certFile](const boost::system::error_code ec,
1117 const std::string& objectPath) {
1118 if (ec)
1119 {
1120 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1121 messages::internalError(asyncResp->res);
1122 return;
1123 }
1124 long certId = getIDFromURL(objectPath);
1125 if (certId < 0)
1126 {
1127 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1128 << objectPath;
1129 messages::internalError(asyncResp->res);
1130 return;
1131 }
1132 std::string certURL =
1133 "/redfish/v1/AccountService/LDAP/Certificates/" +
1134 std::to_string(certId);
1135 getCertificateProperties(asyncResp, objectPath,
1136 certs::ldapServiceName, certId,
1137 certURL, "LDAP Certificate");
1138 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1139 << certFile->getCertFilePath();
1140 },
1141 certs::ldapServiceName, certs::ldapObjectPath,
1142 certs::certInstallIntf, "Install",
1143 certFile->getCertFilePath());
1144 });
1145} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001146
1147/**
1148 * Certificate resource describes a certificate used to prove the identity
1149 * of a component, account or service.
1150 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001151inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001152{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001153 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001154 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001155 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001156 [&app](const crow::Request& req,
1157 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1158 const std::string&) {
1159 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1160 {
1161 return;
1162 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001163 long id = getIDFromURL(req.url);
1164 if (id < 0)
1165 {
1166 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1167 messages::internalError(asyncResp->res);
1168 return;
1169 }
1170 BMCWEB_LOG_DEBUG << "LDAP Certificate ID="
1171 << std::to_string(id);
1172 std::string certURL =
1173 "/redfish/v1/AccountService/LDAP/Certificates/" +
1174 std::to_string(id);
1175 std::string objectPath = certs::ldapObjectPath;
1176 objectPath += "/";
1177 objectPath += std::to_string(id);
1178 getCertificateProperties(asyncResp, objectPath,
1179 certs::ldapServiceName, id, certURL,
1180 "LDAP Certificate");
1181 });
1182} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001183/**
1184 * Collection of TrustStoreCertificate certificates
1185 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001186inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001187{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001188 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001189 .privileges(redfish::privileges::getCertificate)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001190 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1191 const std::shared_ptr<
1192 bmcweb::AsyncResp>&
1193 asyncResp) {
1194 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1195 {
1196 return;
1197 }
George Liu0fda0f12021-11-16 10:06:17 +08001198 asyncResp->res.jsonValue = {
1199 {"@odata.id",
1200 "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1201 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1202 {"Name", "TrustStore Certificates Collection"},
1203 {"Description",
1204 "A Collection of TrustStore certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001205
George Liu0fda0f12021-11-16 10:06:17 +08001206 crow::connections::systemBus->async_method_call(
1207 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001208 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001209 if (ec)
1210 {
1211 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1212 messages::internalError(asyncResp->res);
1213 return;
1214 }
1215 nlohmann::json& members =
1216 asyncResp->res.jsonValue["Members"];
1217 members = nlohmann::json::array();
1218 for (const auto& cert : certs)
1219 {
1220 long id = getIDFromURL(cert.first.str);
1221 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001222 {
George Liu0fda0f12021-11-16 10:06:17 +08001223 members.push_back(
1224 {{"@odata.id",
1225 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1226 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001227 }
George Liu0fda0f12021-11-16 10:06:17 +08001228 }
1229 asyncResp->res.jsonValue["Members@odata.count"] =
1230 members.size();
1231 },
1232 certs::authorityServiceName, certs::authorityObjectPath,
1233 certs::dbusObjManagerIntf, "GetManagedObjects");
1234 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001235
1236 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001237 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +08001238 .methods(
1239 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -07001240 post)([&app](
1241 const crow::Request& req,
1242 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1243 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1244 {
1245 return;
1246 }
George Liu0fda0f12021-11-16 10:06:17 +08001247 std::string certFileBody =
1248 getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001249
George Liu0fda0f12021-11-16 10:06:17 +08001250 if (certFileBody.empty())
1251 {
1252 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1253 messages::unrecognizedRequestBody(asyncResp->res);
1254 return;
1255 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001256
George Liu0fda0f12021-11-16 10:06:17 +08001257 std::shared_ptr<CertificateFile> certFile =
1258 std::make_shared<CertificateFile>(certFileBody);
1259 crow::connections::systemBus->async_method_call(
1260 [asyncResp, certFile](const boost::system::error_code ec,
1261 const std::string& objectPath) {
1262 if (ec)
1263 {
1264 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1265 messages::internalError(asyncResp->res);
1266 return;
1267 }
1268 long certId = getIDFromURL(objectPath);
1269 if (certId < 0)
1270 {
1271 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1272 << objectPath;
1273 messages::internalError(asyncResp->res);
1274 return;
1275 }
1276 std::string certURL =
1277 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1278 std::to_string(certId);
zhanghch058d1b46d2021-04-01 11:18:24 +08001279
George Liu0fda0f12021-11-16 10:06:17 +08001280 getCertificateProperties(
1281 asyncResp, objectPath, certs::authorityServiceName,
1282 certId, certURL, "TrustStore Certificate");
1283 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1284 << certFile->getCertFilePath();
1285 },
1286 certs::authorityServiceName, certs::authorityObjectPath,
1287 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1288 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001289} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001290
1291/**
1292 * Certificate resource describes a certificate used to prove the identity
1293 * of a component, account or service.
1294 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001295inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001296{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001297 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001298 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001299 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001300 [&app](const crow::Request& req,
1301 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1302 const std::string&) {
1303 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1304 {
1305 return;
1306 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001307 long id = getIDFromURL(req.url);
1308 if (id < 0)
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001309 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001310 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1311 messages::internalError(asyncResp->res);
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001312 return;
1313 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001314 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1315 << std::to_string(id);
1316 std::string certURL =
1317 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1318 std::to_string(id);
1319 std::string objectPath = certs::authorityObjectPath;
1320 objectPath += "/";
1321 objectPath += std::to_string(id);
1322 getCertificateProperties(asyncResp, objectPath,
1323 certs::authorityServiceName, id,
1324 certURL, "TrustStore Certificate");
1325 });
1326
1327 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001328 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001329 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001330 [&app](const crow::Request& req,
1331 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1332 const std::string& param) {
1333 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1334 {
1335 return;
1336 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001337 if (param.empty())
1338 {
1339 messages::internalError(asyncResp->res);
1340 return;
1341 }
1342
1343 long id = getIDFromURL(req.url);
1344 if (id < 0)
1345 {
1346 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1347 messages::resourceNotFound(asyncResp->res,
1348 "TrustStore Certificate",
1349 std::string(req.url));
1350 return;
1351 }
1352 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1353 << std::to_string(id);
1354 std::string certPath = certs::authorityObjectPath;
1355 certPath += "/";
1356 certPath += std::to_string(id);
1357
1358 crow::connections::systemBus->async_method_call(
1359 [asyncResp, id](const boost::system::error_code ec) {
1360 if (ec)
1361 {
1362 messages::resourceNotFound(asyncResp->res,
1363 "TrustStore Certificate",
1364 std::to_string(id));
1365 return;
1366 }
1367 BMCWEB_LOG_INFO << "Certificate deleted";
1368 asyncResp->res.result(
1369 boost::beast::http::status::no_content);
1370 },
1371 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1372 "Delete");
1373 });
1374} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001375} // namespace redfish