blob: 6d17a6173b3ed9788a8df88722941883cba35a89 [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 Tanoused398212021-06-09 17:05:54 -07007#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05008
Marri Devender Rao5968cae2019-01-21 10:27:12 -06009namespace redfish
10{
11namespace certs
12{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050013constexpr char const* httpsObjectPath =
Marri Devender Rao5968cae2019-01-21 10:27:12 -060014 "/xyz/openbmc_project/certs/server/https";
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";
21constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
22constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060023 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050024constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060025 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050027 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050028constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050029 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060030} // namespace certs
31
32/**
33 * The Certificate schema defines a Certificate Service which represents the
34 * actions available to manage certificates and links to where certificates
35 * are installed.
36 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060037
John Edward Broadbent7e860f12021-04-08 15:57:16 -070038// TODO: Issue#61 No entries are available for Certificate
39// service at https://www.dmtf.org/standards/redfish
40// "redfish standard registries". Need to modify after DMTF
41// publish Privilege details for certificate service
42
43inline void requestRoutesCertificateService(App& app)
44{
45 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
Ed Tanoused398212021-06-09 17:05:54 -070046 .privileges(redfish::privileges::getCertificateService)
Abhishek Patel72048782021-06-02 09:53:24 -050047 .methods(
48 boost::beast::http::verb::
49 get)([](const crow::Request& req,
50 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
51 asyncResp->res.jsonValue = {
52 {"@odata.type",
53 "#CertificateService.v1_0_0.CertificateService"},
54 {"@odata.id", "/redfish/v1/CertificateService"},
55 {"Id", "CertificateService"},
56 {"Name", "Certificate Service"},
57 {"Description", "Actions available to manage certificates"}};
58 // /redfish/v1/CertificateService/CertificateLocations is something
59 // only ConfigureManager can access then only display when the user
60 // has permissions ConfigureManager
61 Privileges effectiveUserPrivileges =
62 redfish::getUserPrivileges(req.userRole);
63 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
64 effectiveUserPrivileges))
65 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -070066 asyncResp->res.jsonValue["CertificateLocations"] = {
67 {"@odata.id",
68 "/redfish/v1/CertificateService/CertificateLocations"}};
Abhishek Patel72048782021-06-02 09:53:24 -050069 }
George Liu0fda0f12021-11-16 10:06:17 +080070 asyncResp->res
71 .jsonValue["Actions"]
72 ["#CertificateService.ReplaceCertificate"] = {
73 {"target",
74 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"},
75 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
Abhishek Patel72048782021-06-02 09:53:24 -050076 asyncResp->res
77 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
George Liu0fda0f12021-11-16 10:06:17 +080078 {"target",
79 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR"}};
Abhishek Patel72048782021-06-02 09:53:24 -050080 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070081} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060082
Marri Devender Rao5968cae2019-01-21 10:27:12 -060083/**
84 * @brief Find the ID specified in the URL
85 * Finds the numbers specified after the last "/" in the URL and returns.
86 * @param[in] path URL
87 * @return -1 on failure and number on success
88 */
Ed Tanous23a21a12020-07-25 04:45:05 +000089inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060090{
Ed Tanousf23b7292020-10-15 09:41:17 -070091 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -060092 if (found == std::string::npos)
93 {
94 return -1;
95 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020096
Marri Devender Rao5968cae2019-01-21 10:27:12 -060097 if ((found + 1) < url.length())
98 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -060099 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200100
101 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600102 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200103
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600104 return -1;
105}
106
zhanghch058d1b46d2021-04-01 11:18:24 +0800107inline std::string getCertificateFromReqBody(
108 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
109 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200110{
111 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
112
113 if (reqJson.is_discarded())
114 {
115 // We did not receive JSON request, proceed as it is RAW data
116 return req.body;
117 }
118
119 std::string certificate;
120 std::optional<std::string> certificateType = "PEM";
121
Willy Tu15ed6782021-12-14 11:03:16 -0800122 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
123 certificate, "CertificateType",
124 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200125 {
126 BMCWEB_LOG_ERROR << "Required parameters are missing";
127 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700128 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200129 }
130
131 if (*certificateType != "PEM")
132 {
133 messages::propertyValueNotInList(asyncResp->res, *certificateType,
134 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700135 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200136 }
137
138 return certificate;
139}
140
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600141/**
142 * Class to create a temporary certificate file for uploading to system
143 */
144class CertificateFile
145{
146 public:
147 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500148 CertificateFile(const CertificateFile&) = delete;
149 CertificateFile& operator=(const CertificateFile&) = delete;
150 CertificateFile(CertificateFile&&) = delete;
151 CertificateFile& operator=(CertificateFile&&) = delete;
152 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600153 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700154 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700155 'e', 'r', 't', 's', '.', 'X',
156 'X', 'X', 'X', 'X', 'X', '\0'};
157 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800158 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600159 {
160 certDirectory = tempDirectory;
161 certificateFile = certDirectory / "cert.pem";
162 std::ofstream out(certificateFile, std::ofstream::out |
163 std::ofstream::binary |
164 std::ofstream::trunc);
165 out << certString;
166 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800167 BMCWEB_LOG_DEBUG << "Creating certificate file"
168 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600169 }
170 }
171 ~CertificateFile()
172 {
173 if (std::filesystem::exists(certDirectory))
174 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800175 BMCWEB_LOG_DEBUG << "Removing certificate file"
176 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000177 std::error_code ec;
178 std::filesystem::remove_all(certDirectory, ec);
179 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600180 {
181 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800182 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600183 }
184 }
185 }
186 std::string getCertFilePath()
187 {
188 return certificateFile;
189 }
190
191 private:
192 std::filesystem::path certificateFile;
193 std::filesystem::path certDirectory;
194};
195
Marri Devender Rao30215812019-03-18 08:59:21 -0500196static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
197/**
198 * @brief Read data from CSR D-bus object and set to response
199 *
200 * @param[in] asyncResp Shared pointer to the response message
201 * @param[in] certURI Link to certifiate collection URI
202 * @param[in] service D-Bus service name
203 * @param[in] certObjPath certificate D-Bus object path
204 * @param[in] csrObjPath CSR D-Bus object path
205 * @return None
206 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800207static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500208 const std::string& certURI, const std::string& service,
209 const std::string& certObjPath,
210 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500211{
212 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
213 << " CSRObjectPath=" << csrObjPath
214 << " service=" << service;
215 crow::connections::systemBus->async_method_call(
216 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500217 const std::string& csr) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500218 if (ec)
219 {
220 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
221 messages::internalError(asyncResp->res);
222 return;
223 }
224 if (csr.empty())
225 {
226 BMCWEB_LOG_ERROR << "CSR read is empty";
227 messages::internalError(asyncResp->res);
228 return;
229 }
230 asyncResp->res.jsonValue["CSRString"] = csr;
231 asyncResp->res.jsonValue["CertificateCollection"] = {
232 {"@odata.id", certURI}};
233 },
234 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
235}
236
237/**
238 * Action to Generate CSR
239 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700240inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500241{
George Liu0fda0f12021-11-16 10:06:17 +0800242 BMCWEB_ROUTE(
243 app,
244 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
Ed Tanoused398212021-06-09 17:05:54 -0700245 // Incorrect Privilege; Should be ConfigureManager
246 //.privileges(redfish::privileges::postCertificateService)
Ed Tanous432a8902021-06-14 15:28:56 -0700247 .privileges({{"ConfigureComponents"}})
George Liu0fda0f12021-11-16 10:06:17 +0800248 .methods(
249 boost::beast::http::verb::
250 post)([](const crow::Request& req,
251 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
252 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500253
George Liu0fda0f12021-11-16 10:06:17 +0800254 // Required parameters
255 std::string city;
256 std::string commonName;
257 std::string country;
258 std::string organization;
259 std::string organizationalUnit;
260 std::string state;
261 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800262
George Liu0fda0f12021-11-16 10:06:17 +0800263 // Optional parameters
264 std::optional<std::vector<std::string>> optAlternativeNames =
265 std::vector<std::string>();
266 std::optional<std::string> optContactPerson = "";
267 std::optional<std::string> optChallengePassword = "";
268 std::optional<std::string> optEmail = "";
269 std::optional<std::string> optGivenName = "";
270 std::optional<std::string> optInitials = "";
271 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
272 std::optional<std::string> optKeyCurveId = "secp384r1";
273 std::optional<std::string> optKeyPairAlgorithm = "EC";
274 std::optional<std::vector<std::string>> optKeyUsage =
275 std::vector<std::string>();
276 std::optional<std::string> optSurname = "";
277 std::optional<std::string> optUnstructuredName = "";
Willy Tu15ed6782021-12-14 11:03:16 -0800278 if (!json_util::readJsonAction(
George Liu0fda0f12021-11-16 10:06:17 +0800279 req, asyncResp->res, "City", city, "CommonName", commonName,
280 "ContactPerson", optContactPerson, "Country", country,
281 "Organization", organization, "OrganizationalUnit",
282 organizationalUnit, "State", state, "CertificateCollection",
283 certificateCollection, "AlternativeNames",
284 optAlternativeNames, "ChallengePassword",
285 optChallengePassword, "Email", optEmail, "GivenName",
286 optGivenName, "Initials", optInitials, "KeyBitLength",
287 optKeyBitLength, "KeyCurveId", optKeyCurveId,
288 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
289 optKeyUsage, "Surname", optSurname, "UnstructuredName",
290 optUnstructuredName))
291 {
292 return;
293 }
294
295 // bmcweb has no way to store or decode a private key challenge
296 // password, which will likely cause bmcweb to crash on startup
297 // if this is not set on a post so not allowing the user to set
298 // value
Ed Tanous26f69762022-01-25 09:49:11 -0800299 if (!optChallengePassword->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800300 {
301 messages::actionParameterNotSupported(
302 asyncResp->res, "GenerateCSR", "ChallengePassword");
303 return;
304 }
305
306 std::string certURI;
307 if (!redfish::json_util::readJson(certificateCollection,
308 asyncResp->res, "@odata.id",
309 certURI))
310 {
311 return;
312 }
313
314 std::string objectPath;
315 std::string service;
316 if (boost::starts_with(
317 certURI,
318 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
319 {
320 objectPath = certs::httpsObjectPath;
321 service = certs::httpsServiceName;
322 }
323 else if (boost::starts_with(
324 certURI,
325 "/redfish/v1/AccountService/LDAP/Certificates"))
326 {
327 objectPath = certs::ldapObjectPath;
328 service = certs::ldapServiceName;
329 }
330 else
331 {
332 messages::actionParameterNotSupported(
333 asyncResp->res, "CertificateCollection", "GenerateCSR");
334 return;
335 }
336
337 // supporting only EC and RSA algorithm
338 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
339 {
340 messages::actionParameterNotSupported(
341 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
342 return;
343 }
344
345 // supporting only 2048 key bit length for RSA algorithm due to
346 // time consumed in generating private key
347 if (*optKeyPairAlgorithm == "RSA" &&
348 *optKeyBitLength != rsaKeyBitLength)
349 {
350 messages::propertyValueNotInList(
351 asyncResp->res, std::to_string(*optKeyBitLength),
352 "KeyBitLength");
353 return;
354 }
355
356 // validate KeyUsage supporting only 1 type based on URL
357 if (boost::starts_with(
358 certURI,
359 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
360 {
Ed Tanous26f69762022-01-25 09:49:11 -0800361 if (optKeyUsage->empty())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700362 {
George Liu0fda0f12021-11-16 10:06:17 +0800363 optKeyUsage->push_back("ServerAuthentication");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700364 }
George Liu0fda0f12021-11-16 10:06:17 +0800365 else if (optKeyUsage->size() == 1)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700366 {
George Liu0fda0f12021-11-16 10:06:17 +0800367 if ((*optKeyUsage)[0] != "ServerAuthentication")
368 {
369 messages::propertyValueNotInList(
370 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
371 return;
372 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700373 }
374 else
375 {
376 messages::actionParameterNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800377 asyncResp->res, "KeyUsage", "GenerateCSR");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700378 return;
379 }
George Liu0fda0f12021-11-16 10:06:17 +0800380 }
381 else if (boost::starts_with(
382 certURI,
383 "/redfish/v1/AccountService/LDAP/Certificates"))
384 {
Ed Tanous26f69762022-01-25 09:49:11 -0800385 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800386 {
387 optKeyUsage->push_back("ClientAuthentication");
388 }
389 else if (optKeyUsage->size() == 1)
390 {
391 if ((*optKeyUsage)[0] != "ClientAuthentication")
392 {
393 messages::propertyValueNotInList(
394 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
395 return;
396 }
397 }
398 else
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700399 {
400 messages::actionParameterNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800401 asyncResp->res, "KeyUsage", "GenerateCSR");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700402 return;
403 }
George Liu0fda0f12021-11-16 10:06:17 +0800404 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500405
George Liu0fda0f12021-11-16 10:06:17 +0800406 // Only allow one CSR matcher at a time so setting retry
407 // time-out and timer expiry to 10 seconds for now.
408 static const int timeOut = 10;
409 if (csrMatcher)
410 {
411 messages::serviceTemporarilyUnavailable(
412 asyncResp->res, std::to_string(timeOut));
413 return;
414 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500415
George Liu0fda0f12021-11-16 10:06:17 +0800416 // Make this static so it survives outside this method
417 static boost::asio::steady_timer timeout(*req.ioService);
418 timeout.expires_after(std::chrono::seconds(timeOut));
419 timeout.async_wait(
420 [asyncResp](const boost::system::error_code& ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700421 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
George Liu0fda0f12021-11-16 10:06:17 +0800436 // 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'");
444 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
445 *crow::connections::systemBus, match,
446 [asyncResp, service, objectPath,
447 certURI](sdbusplus::message::message& m) {
448 timeout.cancel();
449 if (m.is_method_error())
450 {
451 BMCWEB_LOG_ERROR << "Dbus method error!!!";
452 messages::internalError(asyncResp->res);
453 return;
454 }
Ed Tanous168e20c2021-12-13 14:39:53 -0800455 std::vector<std::pair<
456 std::string,
457 std::vector<std::pair<std::string,
458 dbus::utility::DbusVariantType>>>>
George Liu0fda0f12021-11-16 10:06:17 +0800459 interfacesProperties;
460 sdbusplus::message::object_path csrObjectPath;
461 m.read(csrObjectPath, interfacesProperties);
462 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
463 for (auto& interface : interfacesProperties)
464 {
465 if (interface.first == "xyz.openbmc_project.Certs.CSR")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700466 {
George Liu0fda0f12021-11-16 10:06:17 +0800467 getCSR(asyncResp, certURI, service, objectPath,
468 csrObjectPath.str);
469 break;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700470 }
George Liu0fda0f12021-11-16 10:06:17 +0800471 }
472 });
473 crow::connections::systemBus->async_method_call(
Ed Tanous914e2d52022-01-07 11:38:34 -0800474 [asyncResp](const boost::system::error_code ec,
George Liu0fda0f12021-11-16 10:06:17 +0800475 const std::string&) {
476 if (ec)
477 {
478 BMCWEB_LOG_ERROR << "DBUS response error: "
479 << ec.message();
480 messages::internalError(asyncResp->res);
481 return;
482 }
483 },
484 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
485 "GenerateCSR", *optAlternativeNames, *optChallengePassword,
486 city, commonName, *optContactPerson, country, *optEmail,
487 *optGivenName, *optInitials, *optKeyBitLength, *optKeyCurveId,
488 *optKeyPairAlgorithm, *optKeyUsage, organization,
489 organizationalUnit, state, *optSurname, *optUnstructuredName);
490 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700491} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500492
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600493/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500494 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600495 *
496 * @param[in] asyncResp Shared pointer to the response message
497 * @param[in] str Issuer/Subject value in key=value pairs
498 * @param[in] type Issuer/Subject
499 * @return None
500 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500501static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600502 const std::string_view value)
503{
504 // example: O=openbmc-project.xyz,CN=localhost
505 std::string_view::iterator i = value.begin();
506 while (i != value.end())
507 {
508 std::string_view::iterator tokenBegin = i;
509 while (i != value.end() && *i != '=')
510 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530511 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600512 }
513 if (i == value.end())
514 {
515 break;
516 }
Ed Tanous271584a2019-07-09 16:24:22 -0700517 const std::string_view key(tokenBegin,
518 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530519 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600520 tokenBegin = i;
521 while (i != value.end() && *i != ',')
522 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530523 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600524 }
Ed Tanous271584a2019-07-09 16:24:22 -0700525 const std::string_view val(tokenBegin,
526 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600527 if (key == "L")
528 {
529 out["City"] = val;
530 }
531 else if (key == "CN")
532 {
533 out["CommonName"] = val;
534 }
535 else if (key == "C")
536 {
537 out["Country"] = val;
538 }
539 else if (key == "O")
540 {
541 out["Organization"] = val;
542 }
543 else if (key == "OU")
544 {
545 out["OrganizationalUnit"] = val;
546 }
547 else if (key == "ST")
548 {
549 out["State"] = val;
550 }
551 // skip comma character
552 if (i != value.end())
553 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530554 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600555 }
556 }
557}
558
559/**
560 * @brief Retrieve the certificates properties and append to the response
561 * message
562 *
563 * @param[in] asyncResp Shared pointer to the response message
564 * @param[in] objectPath Path of the D-Bus service object
565 * @param[in] certId Id of the certificate
566 * @param[in] certURL URL of the certificate object
567 * @param[in] name name of the certificate
568 * @return None
569 */
570static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800571 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
572 const std::string& objectPath, const std::string& service, long certId,
573 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600574{
Ed Tanous168e20c2021-12-13 14:39:53 -0800575 using PropertiesMap =
576 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600577 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
578 << " certId=" << certId << " certURl=" << certURL;
579 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600580 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500581 const PropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600582 if (ec)
583 {
584 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500585 messages::resourceNotFound(asyncResp->res, name,
586 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600587 return;
588 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600589 asyncResp->res.jsonValue = {
590 {"@odata.id", certURL},
591 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600592 {"Id", std::to_string(certId)},
593 {"Name", name},
594 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500595 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600596 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600597 if (property.first == "CertificateString")
598 {
599 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500600 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600601 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800602 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600603 {
604 asyncResp->res.jsonValue["CertificateString"] = *value;
605 }
606 }
607 else if (property.first == "KeyUsage")
608 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500609 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600610 asyncResp->res.jsonValue["KeyUsage"];
611 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500612 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600613 std::get_if<std::vector<std::string>>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800614 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600615 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500616 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600617 {
618 keyUsage.push_back(usage);
619 }
620 }
621 }
622 else if (property.first == "Issuer")
623 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500624 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600625 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800626 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600627 {
628 updateCertIssuerOrSubject(
629 asyncResp->res.jsonValue["Issuer"], *value);
630 }
631 }
632 else if (property.first == "Subject")
633 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500634 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600635 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800636 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600637 {
638 updateCertIssuerOrSubject(
639 asyncResp->res.jsonValue["Subject"], *value);
640 }
641 }
642 else if (property.first == "ValidNotAfter")
643 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500644 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600645 std::get_if<uint64_t>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800646 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600647 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600648 asyncResp->res.jsonValue["ValidNotAfter"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800649 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600650 }
651 }
652 else if (property.first == "ValidNotBefore")
653 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500654 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600655 std::get_if<uint64_t>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800656 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600657 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600658 asyncResp->res.jsonValue["ValidNotBefore"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800659 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600660 }
661 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600662 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600663 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600664 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600665 service, objectPath, certs::dbusPropIntf, "GetAll",
666 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600667}
668
669using GetObjectType =
670 std::vector<std::pair<std::string, std::vector<std::string>>>;
671
672/**
673 * Action to replace an existing certificate
674 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700675inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600676{
George Liu0fda0f12021-11-16 10:06:17 +0800677 BMCWEB_ROUTE(
678 app,
679 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700680 .privileges(redfish::privileges::postCertificateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700681 .methods(
682 boost::beast::http::verb::
683 post)([](const crow::Request& req,
684 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
685 std::string certificate;
686 nlohmann::json certificateUri;
687 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600688
Willy Tu15ed6782021-12-14 11:03:16 -0800689 if (!json_util::readJsonAction(req, asyncResp->res,
690 "CertificateString", certificate,
691 "CertificateUri", certificateUri,
692 "CertificateType", certificateType))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700693 {
694 BMCWEB_LOG_ERROR << "Required parameters are missing";
695 messages::internalError(asyncResp->res);
696 return;
697 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800698
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700699 if (!certificateType)
700 {
701 // should never happen, but it never hurts to be paranoid.
702 return;
703 }
704 if (certificateType != "PEM")
705 {
706 messages::actionParameterNotSupported(
707 asyncResp->res, "CertificateType", "ReplaceCertificate");
708 return;
709 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600710
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700711 std::string certURI;
712 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
713 "@odata.id", certURI))
714 {
715 messages::actionParameterMissing(
716 asyncResp->res, "ReplaceCertificate", "CertificateUri");
717 return;
718 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600719
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700720 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
721 long id = getIDFromURL(certURI);
722 if (id < 0)
723 {
724 messages::actionParameterValueFormatError(
725 asyncResp->res, certURI, "CertificateUri",
726 "ReplaceCertificate");
727 return;
728 }
729 std::string objectPath;
730 std::string name;
731 std::string service;
George Liu0fda0f12021-11-16 10:06:17 +0800732 if (boost::starts_with(
733 certURI,
734 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700735 {
736 objectPath = std::string(certs::httpsObjectPath) + "/" +
737 std::to_string(id);
738 name = "HTTPS certificate";
739 service = certs::httpsServiceName;
740 }
741 else if (boost::starts_with(
742 certURI,
743 "/redfish/v1/AccountService/LDAP/Certificates/"))
744 {
745 objectPath = std::string(certs::ldapObjectPath) + "/" +
746 std::to_string(id);
747 name = "LDAP certificate";
748 service = certs::ldapServiceName;
749 }
750 else if (boost::starts_with(
751 certURI,
752 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
753 {
754 objectPath = std::string(certs::authorityObjectPath) + "/" +
755 std::to_string(id);
756 name = "TrustStore certificate";
757 service = certs::authorityServiceName;
758 }
759 else
760 {
761 messages::actionParameterNotSupported(
762 asyncResp->res, "CertificateUri", "ReplaceCertificate");
763 return;
764 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600765
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700766 std::shared_ptr<CertificateFile> certFile =
767 std::make_shared<CertificateFile>(certificate);
768 crow::connections::systemBus->async_method_call(
769 [asyncResp, certFile, objectPath, service, certURI, id,
770 name](const boost::system::error_code ec) {
771 if (ec)
772 {
773 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
774 messages::resourceNotFound(asyncResp->res, name,
775 std::to_string(id));
776 return;
777 }
778 getCertificateProperties(asyncResp, objectPath, service, id,
779 certURI, name);
780 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
781 << certFile->getCertFilePath();
782 },
783 service, objectPath, certs::certReplaceIntf, "Replace",
784 certFile->getCertFilePath());
785 });
786} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600787
788/**
789 * Certificate resource describes a certificate used to prove the identity
790 * of a component, account or service.
791 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700792
793inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600794{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700795 BMCWEB_ROUTE(
796 app,
797 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700798 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700799 .methods(
800 boost::beast::http::verb::
801 get)([](const crow::Request& req,
802 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
803 const std::string& param) -> void {
804 if (param.empty())
805 {
806 messages::internalError(asyncResp->res);
807 return;
808 }
809 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600810
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700811 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
812 << std::to_string(id);
813 std::string certURL =
814 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
815 std::to_string(id);
816 std::string objectPath = certs::httpsObjectPath;
817 objectPath += "/";
818 objectPath += std::to_string(id);
819 getCertificateProperties(asyncResp, objectPath,
820 certs::httpsServiceName, id, certURL,
821 "HTTPS Certificate");
822 });
823}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600824
825/**
826 * Collection of HTTPS certificates
827 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700828inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600829{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700830 BMCWEB_ROUTE(app,
831 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700832 .privileges(redfish::privileges::getCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700833 .methods(
834 boost::beast::http::verb::
835 get)([](const crow::Request&,
836 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
837 asyncResp->res.jsonValue = {
838 {"@odata.id",
839 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
840 {"@odata.type", "#CertificateCollection.CertificateCollection"},
841 {"Name", "HTTPS Certificates Collection"},
842 {"Description", "A Collection of HTTPS certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +0800843
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700844 crow::connections::systemBus->async_method_call(
845 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800846 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700847 if (ec)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600848 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700849 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
850 messages::internalError(asyncResp->res);
851 return;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600852 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700853 nlohmann::json& members =
854 asyncResp->res.jsonValue["Members"];
855 members = nlohmann::json::array();
856 for (const auto& cert : certs)
857 {
858 long id = getIDFromURL(cert.first.str);
859 if (id >= 0)
860 {
861 members.push_back(
862 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +0800863 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700864 std::to_string(id)}});
865 }
866 }
867 asyncResp->res.jsonValue["Members@odata.count"] =
868 members.size();
869 },
870 certs::httpsServiceName, certs::httpsObjectPath,
871 certs::dbusObjManagerIntf, "GetManagedObjects");
872 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600873
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700874 BMCWEB_ROUTE(app,
875 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700876 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +0800877 .methods(
878 boost::beast::http::verb::
879 post)([](const crow::Request& req,
880 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
881 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
zhanghch058d1b46d2021-04-01 11:18:24 +0800882
George Liu0fda0f12021-11-16 10:06:17 +0800883 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
884 {"Description", "HTTPS Certificate"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600885
George Liu0fda0f12021-11-16 10:06:17 +0800886 std::string certFileBody =
887 getCertificateFromReqBody(asyncResp, req);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200888
George Liu0fda0f12021-11-16 10:06:17 +0800889 if (certFileBody.empty())
890 {
891 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
892 messages::unrecognizedRequestBody(asyncResp->res);
893 return;
894 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700895
George Liu0fda0f12021-11-16 10:06:17 +0800896 std::shared_ptr<CertificateFile> certFile =
897 std::make_shared<CertificateFile>(certFileBody);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700898
George Liu0fda0f12021-11-16 10:06:17 +0800899 crow::connections::systemBus->async_method_call(
900 [asyncResp, certFile](const boost::system::error_code ec,
901 const std::string& objectPath) {
902 if (ec)
903 {
904 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
905 messages::internalError(asyncResp->res);
906 return;
907 }
908 long certId = getIDFromURL(objectPath);
909 if (certId < 0)
910 {
911 BMCWEB_LOG_ERROR << "Invalid objectPath value"
912 << objectPath;
913 messages::internalError(asyncResp->res);
914 return;
915 }
916 std::string certURL =
917 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
918 std::to_string(certId);
919 getCertificateProperties(asyncResp, objectPath,
920 certs::httpsServiceName, certId,
921 certURL, "HTTPS Certificate");
922 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
923 << certFile->getCertFilePath();
924 },
925 certs::httpsServiceName, certs::httpsObjectPath,
926 certs::certInstallIntf, "Install", certFile->getCertFilePath());
927 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700928} // requestRoutesHTTPSCertificateCollection
929
930/**
931 * @brief Retrieve the certificates installed list and append to the
932 * response
933 *
934 * @param[in] asyncResp Shared pointer to the response message
935 * @param[in] certURL Path of the certificate object
936 * @param[in] path Path of the D-Bus service object
937 * @return None
938 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700939inline void
940 getCertificateLocations(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
941 const std::string& certURL, const std::string& path,
942 const std::string& service)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700943{
944 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
945 << " Path=" << path << " service= " << service;
946 crow::connections::systemBus->async_method_call(
947 [asyncResp, certURL](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800948 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700949 if (ec)
950 {
951 BMCWEB_LOG_WARNING
952 << "Certificate collection query failed: " << ec
953 << ", skipping " << certURL;
954 return;
955 }
956 nlohmann::json& links =
957 asyncResp->res.jsonValue["Links"]["Certificates"];
Ed Tanous9eb808c2022-01-25 10:19:23 -0800958 for (const auto& cert : certs)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700959 {
960 long id = getIDFromURL(cert.first.str);
961 if (id >= 0)
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200962 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700963 links.push_back(
964 {{"@odata.id", certURL + std::to_string(id)}});
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200965 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700966 }
967 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
968 links.size();
969 },
970 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
971}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600972
973/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600974 * The certificate location schema defines a resource that an administrator
975 * can use in order to locate all certificates installed on a given service.
976 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700977inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600978{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700979 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700980 .privileges(redfish::privileges::getCertificateLocations)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700981 .methods(
982 boost::beast::http::verb::
983 get)([](const crow::Request&,
984 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
985 asyncResp->res.jsonValue = {
986 {"@odata.id",
987 "/redfish/v1/CertificateService/CertificateLocations"},
988 {"@odata.type",
989 "#CertificateLocations.v1_0_0.CertificateLocations"},
990 {"Name", "Certificate Locations"},
991 {"Id", "CertificateLocations"},
992 {"Description",
993 "Defines a resource that an administrator can use in order to "
994 "locate all certificates installed on a given service"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600995
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700996 nlohmann::json& links =
997 asyncResp->res.jsonValue["Links"]["Certificates"];
998 links = nlohmann::json::array();
999 getCertificateLocations(
1000 asyncResp,
1001 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
1002 certs::httpsObjectPath, certs::httpsServiceName);
1003 getCertificateLocations(
1004 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/",
1005 certs::ldapObjectPath, certs::ldapServiceName);
1006 getCertificateLocations(
1007 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1008 certs::authorityObjectPath, certs::authorityServiceName);
1009 });
1010}
1011// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001012
1013/**
1014 * Collection of LDAP certificates
1015 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001016inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001017{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001018 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001019 .privileges(redfish::privileges::getCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +08001020 .methods(
1021 boost::beast::http::verb::
1022 get)([](const crow::Request&,
1023 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1024 asyncResp->res.jsonValue = {
1025 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1026 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1027 {"Name", "LDAP Certificates Collection"},
1028 {"Description", "A Collection of LDAP certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001029
George Liu0fda0f12021-11-16 10:06:17 +08001030 crow::connections::systemBus->async_method_call(
1031 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001032 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001033 nlohmann::json& members =
1034 asyncResp->res.jsonValue["Members"];
1035 nlohmann::json& count =
1036 asyncResp->res.jsonValue["Members@odata.count"];
1037 members = nlohmann::json::array();
1038 count = 0;
1039 if (ec)
1040 {
1041 BMCWEB_LOG_WARNING << "LDAP certificate query failed: "
1042 << ec;
1043 return;
1044 }
1045 for (const auto& cert : certs)
1046 {
1047 long id = getIDFromURL(cert.first.str);
1048 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001049 {
George Liu0fda0f12021-11-16 10:06:17 +08001050 members.push_back(
1051 {{"@odata.id",
1052 "/redfish/v1/AccountService/LDAP/Certificates/" +
1053 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001054 }
George Liu0fda0f12021-11-16 10:06:17 +08001055 }
1056 count = members.size();
1057 },
1058 certs::ldapServiceName, certs::ldapObjectPath,
1059 certs::dbusObjManagerIntf, "GetManagedObjects");
1060 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001061
1062 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001063 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001064 .methods(boost::beast::http::verb::post)(
1065 [](const crow::Request& req,
1066 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1067 std::string certFileBody =
1068 getCertificateFromReqBody(asyncResp, req);
1069
1070 if (certFileBody.empty())
Marri Devender Rao37cce912019-02-20 01:05:22 -06001071 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001072 BMCWEB_LOG_ERROR
1073 << "Cannot get certificate from request body.";
1074 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001075 return;
1076 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001077
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001078 std::shared_ptr<CertificateFile> certFile =
1079 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001080
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001081 crow::connections::systemBus->async_method_call(
1082 [asyncResp, certFile](const boost::system::error_code ec,
1083 const std::string& objectPath) {
1084 if (ec)
1085 {
1086 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1087 messages::internalError(asyncResp->res);
1088 return;
1089 }
1090 long certId = getIDFromURL(objectPath);
1091 if (certId < 0)
1092 {
1093 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1094 << objectPath;
1095 messages::internalError(asyncResp->res);
1096 return;
1097 }
1098 std::string certURL =
1099 "/redfish/v1/AccountService/LDAP/Certificates/" +
1100 std::to_string(certId);
1101 getCertificateProperties(asyncResp, objectPath,
1102 certs::ldapServiceName, certId,
1103 certURL, "LDAP Certificate");
1104 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1105 << certFile->getCertFilePath();
1106 },
1107 certs::ldapServiceName, certs::ldapObjectPath,
1108 certs::certInstallIntf, "Install",
1109 certFile->getCertFilePath());
1110 });
1111} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001112
1113/**
1114 * Certificate resource describes a certificate used to prove the identity
1115 * of a component, account or service.
1116 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001117inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001118{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001119 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001120 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001121 .methods(boost::beast::http::verb::get)(
1122 [](const crow::Request& req,
1123 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1124 const std::string&) {
1125 long id = getIDFromURL(req.url);
1126 if (id < 0)
1127 {
1128 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1129 messages::internalError(asyncResp->res);
1130 return;
1131 }
1132 BMCWEB_LOG_DEBUG << "LDAP Certificate ID="
1133 << std::to_string(id);
1134 std::string certURL =
1135 "/redfish/v1/AccountService/LDAP/Certificates/" +
1136 std::to_string(id);
1137 std::string objectPath = certs::ldapObjectPath;
1138 objectPath += "/";
1139 objectPath += std::to_string(id);
1140 getCertificateProperties(asyncResp, objectPath,
1141 certs::ldapServiceName, id, certURL,
1142 "LDAP Certificate");
1143 });
1144} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001145/**
1146 * Collection of TrustStoreCertificate certificates
1147 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001148inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001149{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001150 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001151 .privileges(redfish::privileges::getCertificate)
George Liu0fda0f12021-11-16 10:06:17 +08001152 .methods(
1153 boost::beast::http::verb::
1154 get)([](const crow::Request&,
1155 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1156 asyncResp->res.jsonValue = {
1157 {"@odata.id",
1158 "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1159 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1160 {"Name", "TrustStore Certificates Collection"},
1161 {"Description",
1162 "A Collection of TrustStore certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001163
George Liu0fda0f12021-11-16 10:06:17 +08001164 crow::connections::systemBus->async_method_call(
1165 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001166 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001167 if (ec)
1168 {
1169 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1170 messages::internalError(asyncResp->res);
1171 return;
1172 }
1173 nlohmann::json& members =
1174 asyncResp->res.jsonValue["Members"];
1175 members = nlohmann::json::array();
1176 for (const auto& cert : certs)
1177 {
1178 long id = getIDFromURL(cert.first.str);
1179 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001180 {
George Liu0fda0f12021-11-16 10:06:17 +08001181 members.push_back(
1182 {{"@odata.id",
1183 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1184 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001185 }
George Liu0fda0f12021-11-16 10:06:17 +08001186 }
1187 asyncResp->res.jsonValue["Members@odata.count"] =
1188 members.size();
1189 },
1190 certs::authorityServiceName, certs::authorityObjectPath,
1191 certs::dbusObjManagerIntf, "GetManagedObjects");
1192 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001193
1194 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001195 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +08001196 .methods(
1197 boost::beast::http::verb::
1198 post)([](const crow::Request& req,
1199 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1200 std::string certFileBody =
1201 getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001202
George Liu0fda0f12021-11-16 10:06:17 +08001203 if (certFileBody.empty())
1204 {
1205 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1206 messages::unrecognizedRequestBody(asyncResp->res);
1207 return;
1208 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001209
George Liu0fda0f12021-11-16 10:06:17 +08001210 std::shared_ptr<CertificateFile> certFile =
1211 std::make_shared<CertificateFile>(certFileBody);
1212 crow::connections::systemBus->async_method_call(
1213 [asyncResp, certFile](const boost::system::error_code ec,
1214 const std::string& objectPath) {
1215 if (ec)
1216 {
1217 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1218 messages::internalError(asyncResp->res);
1219 return;
1220 }
1221 long certId = getIDFromURL(objectPath);
1222 if (certId < 0)
1223 {
1224 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1225 << objectPath;
1226 messages::internalError(asyncResp->res);
1227 return;
1228 }
1229 std::string certURL =
1230 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1231 std::to_string(certId);
zhanghch058d1b46d2021-04-01 11:18:24 +08001232
George Liu0fda0f12021-11-16 10:06:17 +08001233 getCertificateProperties(
1234 asyncResp, objectPath, certs::authorityServiceName,
1235 certId, certURL, "TrustStore Certificate");
1236 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1237 << certFile->getCertFilePath();
1238 },
1239 certs::authorityServiceName, certs::authorityObjectPath,
1240 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1241 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001242} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001243
1244/**
1245 * Certificate resource describes a certificate used to prove the identity
1246 * of a component, account or service.
1247 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001248inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001249{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001250 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001251 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001252 .methods(boost::beast::http::verb::get)(
1253 [](const crow::Request& req,
1254 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1255 const std::string&) {
1256 long id = getIDFromURL(req.url);
1257 if (id < 0)
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001258 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001259 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1260 messages::internalError(asyncResp->res);
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001261 return;
1262 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001263 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1264 << std::to_string(id);
1265 std::string certURL =
1266 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1267 std::to_string(id);
1268 std::string objectPath = certs::authorityObjectPath;
1269 objectPath += "/";
1270 objectPath += std::to_string(id);
1271 getCertificateProperties(asyncResp, objectPath,
1272 certs::authorityServiceName, id,
1273 certURL, "TrustStore Certificate");
1274 });
1275
1276 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001277 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001278 .methods(boost::beast::http::verb::delete_)(
1279 [](const crow::Request& req,
1280 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1281 const std::string& param) {
1282 if (param.empty())
1283 {
1284 messages::internalError(asyncResp->res);
1285 return;
1286 }
1287
1288 long id = getIDFromURL(req.url);
1289 if (id < 0)
1290 {
1291 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1292 messages::resourceNotFound(asyncResp->res,
1293 "TrustStore Certificate",
1294 std::string(req.url));
1295 return;
1296 }
1297 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1298 << std::to_string(id);
1299 std::string certPath = certs::authorityObjectPath;
1300 certPath += "/";
1301 certPath += std::to_string(id);
1302
1303 crow::connections::systemBus->async_method_call(
1304 [asyncResp, id](const boost::system::error_code ec) {
1305 if (ec)
1306 {
1307 messages::resourceNotFound(asyncResp->res,
1308 "TrustStore Certificate",
1309 std::to_string(id));
1310 return;
1311 }
1312 BMCWEB_LOG_INFO << "Certificate deleted";
1313 asyncResp->res.result(
1314 boost::beast::http::status::no_content);
1315 },
1316 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1317 "Delete");
1318 });
1319} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001320} // namespace redfish