blob: 934f901a43d6cd4d6133bc43a5fd58d80a527a59 [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001#pragma once
2
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003#include <app.hpp>
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +02004#include <boost/convert.hpp>
5#include <boost/convert/strtol.hpp>
Jiaqing Zhao90d2d1e2022-04-13 17:01:57 +08006#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -08007#include <dbus_utility.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -07008#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -07009#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050010
Marri Devender Rao5968cae2019-01-21 10:27:12 -060011namespace redfish
12{
13namespace certs
14{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050015constexpr char const* httpsObjectPath =
Marri Devender Rao5968cae2019-01-21 10:27:12 -060016 "/xyz/openbmc_project/certs/server/https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050017constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
18constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
19constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
20constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
21constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
22constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
23constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
24constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060025 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060027 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050028constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050029 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050030constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050031 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060032} // namespace certs
33
34/**
35 * The Certificate schema defines a Certificate Service which represents the
36 * actions available to manage certificates and links to where certificates
37 * are installed.
38 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060039
John Edward Broadbent7e860f12021-04-08 15:57:16 -070040// TODO: Issue#61 No entries are available for Certificate
41// service at https://www.dmtf.org/standards/redfish
42// "redfish standard registries". Need to modify after DMTF
43// publish Privilege details for certificate service
44
45inline void requestRoutesCertificateService(App& app)
46{
47 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
Ed Tanoused398212021-06-09 17:05:54 -070048 .privileges(redfish::privileges::getCertificateService)
Ed Tanous45ca1b82022-03-25 13:07:27 -070049 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
50 const std::shared_ptr<
51 bmcweb::AsyncResp>&
52 asyncResp) {
53 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
54 {
55 return;
56 }
Abhishek Patel72048782021-06-02 09:53:24 -050057 asyncResp->res.jsonValue = {
58 {"@odata.type",
59 "#CertificateService.v1_0_0.CertificateService"},
60 {"@odata.id", "/redfish/v1/CertificateService"},
61 {"Id", "CertificateService"},
62 {"Name", "Certificate Service"},
63 {"Description", "Actions available to manage certificates"}};
64 // /redfish/v1/CertificateService/CertificateLocations is something
65 // only ConfigureManager can access then only display when the user
66 // has permissions ConfigureManager
67 Privileges effectiveUserPrivileges =
68 redfish::getUserPrivileges(req.userRole);
69 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
70 effectiveUserPrivileges))
71 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -070072 asyncResp->res.jsonValue["CertificateLocations"] = {
73 {"@odata.id",
74 "/redfish/v1/CertificateService/CertificateLocations"}};
Abhishek Patel72048782021-06-02 09:53:24 -050075 }
George Liu0fda0f12021-11-16 10:06:17 +080076 asyncResp->res
77 .jsonValue["Actions"]
78 ["#CertificateService.ReplaceCertificate"] = {
79 {"target",
80 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"},
81 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
Abhishek Patel72048782021-06-02 09:53:24 -050082 asyncResp->res
83 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
George Liu0fda0f12021-11-16 10:06:17 +080084 {"target",
85 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR"}};
Abhishek Patel72048782021-06-02 09:53:24 -050086 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070087} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060088
Marri Devender Rao5968cae2019-01-21 10:27:12 -060089/**
90 * @brief Find the ID specified in the URL
91 * Finds the numbers specified after the last "/" in the URL and returns.
92 * @param[in] path URL
93 * @return -1 on failure and number on success
94 */
Ed Tanous23a21a12020-07-25 04:45:05 +000095inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060096{
Ed Tanousf23b7292020-10-15 09:41:17 -070097 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -060098 if (found == std::string::npos)
99 {
100 return -1;
101 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200102
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600103 if ((found + 1) < url.length())
104 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600105 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200106
107 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600108 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200109
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600110 return -1;
111}
112
zhanghch058d1b46d2021-04-01 11:18:24 +0800113inline std::string getCertificateFromReqBody(
114 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
115 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200116{
117 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
118
119 if (reqJson.is_discarded())
120 {
121 // We did not receive JSON request, proceed as it is RAW data
122 return req.body;
123 }
124
125 std::string certificate;
126 std::optional<std::string> certificateType = "PEM";
127
Willy Tu15ed6782021-12-14 11:03:16 -0800128 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
129 certificate, "CertificateType",
130 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200131 {
132 BMCWEB_LOG_ERROR << "Required parameters are missing";
133 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700134 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200135 }
136
137 if (*certificateType != "PEM")
138 {
139 messages::propertyValueNotInList(asyncResp->res, *certificateType,
140 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700141 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200142 }
143
144 return certificate;
145}
146
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600147/**
148 * Class to create a temporary certificate file for uploading to system
149 */
150class CertificateFile
151{
152 public:
153 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500154 CertificateFile(const CertificateFile&) = delete;
155 CertificateFile& operator=(const CertificateFile&) = delete;
156 CertificateFile(CertificateFile&&) = delete;
157 CertificateFile& operator=(CertificateFile&&) = delete;
158 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600159 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700160 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700161 'e', 'r', 't', 's', '.', 'X',
162 'X', 'X', 'X', 'X', 'X', '\0'};
163 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800164 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600165 {
166 certDirectory = tempDirectory;
167 certificateFile = certDirectory / "cert.pem";
168 std::ofstream out(certificateFile, std::ofstream::out |
169 std::ofstream::binary |
170 std::ofstream::trunc);
171 out << certString;
172 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800173 BMCWEB_LOG_DEBUG << "Creating certificate file"
174 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600175 }
176 }
177 ~CertificateFile()
178 {
179 if (std::filesystem::exists(certDirectory))
180 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800181 BMCWEB_LOG_DEBUG << "Removing certificate file"
182 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000183 std::error_code ec;
184 std::filesystem::remove_all(certDirectory, ec);
185 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600186 {
187 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800188 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600189 }
190 }
191 }
192 std::string getCertFilePath()
193 {
194 return certificateFile;
195 }
196
197 private:
198 std::filesystem::path certificateFile;
199 std::filesystem::path certDirectory;
200};
201
Marri Devender Rao30215812019-03-18 08:59:21 -0500202static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
203/**
204 * @brief Read data from CSR D-bus object and set to response
205 *
206 * @param[in] asyncResp Shared pointer to the response message
207 * @param[in] certURI Link to certifiate collection URI
208 * @param[in] service D-Bus service name
209 * @param[in] certObjPath certificate D-Bus object path
210 * @param[in] csrObjPath CSR D-Bus object path
211 * @return None
212 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800213static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500214 const std::string& certURI, const std::string& service,
215 const std::string& certObjPath,
216 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500217{
218 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
219 << " CSRObjectPath=" << csrObjPath
220 << " service=" << service;
221 crow::connections::systemBus->async_method_call(
222 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500223 const std::string& csr) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500224 if (ec)
225 {
226 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
227 messages::internalError(asyncResp->res);
228 return;
229 }
230 if (csr.empty())
231 {
232 BMCWEB_LOG_ERROR << "CSR read is empty";
233 messages::internalError(asyncResp->res);
234 return;
235 }
236 asyncResp->res.jsonValue["CSRString"] = csr;
237 asyncResp->res.jsonValue["CertificateCollection"] = {
238 {"@odata.id", certURI}};
239 },
240 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
241}
242
243/**
244 * Action to Generate CSR
245 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700246inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500247{
George Liu0fda0f12021-11-16 10:06:17 +0800248 BMCWEB_ROUTE(
249 app,
250 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
Abhishek Patel5344ab82021-07-31 17:42:09 -0500251 .privileges(redfish::privileges::postCertificateService)
George Liu0fda0f12021-11-16 10:06:17 +0800252 .methods(
253 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700254 post)([&app](
255 const crow::Request& req,
256 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
257 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
258 {
259 return;
260 }
George Liu0fda0f12021-11-16 10:06:17 +0800261 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500262
George Liu0fda0f12021-11-16 10:06:17 +0800263 // Required parameters
264 std::string city;
265 std::string commonName;
266 std::string country;
267 std::string organization;
268 std::string organizationalUnit;
269 std::string state;
270 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800271
George Liu0fda0f12021-11-16 10:06:17 +0800272 // Optional parameters
273 std::optional<std::vector<std::string>> optAlternativeNames =
274 std::vector<std::string>();
275 std::optional<std::string> optContactPerson = "";
276 std::optional<std::string> optChallengePassword = "";
277 std::optional<std::string> optEmail = "";
278 std::optional<std::string> optGivenName = "";
279 std::optional<std::string> optInitials = "";
280 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
281 std::optional<std::string> optKeyCurveId = "secp384r1";
282 std::optional<std::string> optKeyPairAlgorithm = "EC";
283 std::optional<std::vector<std::string>> optKeyUsage =
284 std::vector<std::string>();
285 std::optional<std::string> optSurname = "";
286 std::optional<std::string> optUnstructuredName = "";
Willy Tu15ed6782021-12-14 11:03:16 -0800287 if (!json_util::readJsonAction(
George Liu0fda0f12021-11-16 10:06:17 +0800288 req, asyncResp->res, "City", city, "CommonName", commonName,
289 "ContactPerson", optContactPerson, "Country", country,
290 "Organization", organization, "OrganizationalUnit",
291 organizationalUnit, "State", state, "CertificateCollection",
292 certificateCollection, "AlternativeNames",
293 optAlternativeNames, "ChallengePassword",
294 optChallengePassword, "Email", optEmail, "GivenName",
295 optGivenName, "Initials", optInitials, "KeyBitLength",
296 optKeyBitLength, "KeyCurveId", optKeyCurveId,
297 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
298 optKeyUsage, "Surname", optSurname, "UnstructuredName",
299 optUnstructuredName))
300 {
301 return;
302 }
303
304 // bmcweb has no way to store or decode a private key challenge
305 // password, which will likely cause bmcweb to crash on startup
306 // if this is not set on a post so not allowing the user to set
307 // value
Ed Tanous26f69762022-01-25 09:49:11 -0800308 if (!optChallengePassword->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800309 {
310 messages::actionParameterNotSupported(
311 asyncResp->res, "GenerateCSR", "ChallengePassword");
312 return;
313 }
314
315 std::string certURI;
316 if (!redfish::json_util::readJson(certificateCollection,
317 asyncResp->res, "@odata.id",
318 certURI))
319 {
320 return;
321 }
322
323 std::string objectPath;
324 std::string service;
325 if (boost::starts_with(
326 certURI,
327 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
328 {
329 objectPath = certs::httpsObjectPath;
330 service = certs::httpsServiceName;
331 }
332 else if (boost::starts_with(
333 certURI,
334 "/redfish/v1/AccountService/LDAP/Certificates"))
335 {
336 objectPath = certs::ldapObjectPath;
337 service = certs::ldapServiceName;
338 }
339 else
340 {
341 messages::actionParameterNotSupported(
342 asyncResp->res, "CertificateCollection", "GenerateCSR");
343 return;
344 }
345
346 // supporting only EC and RSA algorithm
347 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
348 {
349 messages::actionParameterNotSupported(
350 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
351 return;
352 }
353
354 // supporting only 2048 key bit length for RSA algorithm due to
355 // time consumed in generating private key
356 if (*optKeyPairAlgorithm == "RSA" &&
357 *optKeyBitLength != rsaKeyBitLength)
358 {
359 messages::propertyValueNotInList(
360 asyncResp->res, std::to_string(*optKeyBitLength),
361 "KeyBitLength");
362 return;
363 }
364
365 // validate KeyUsage supporting only 1 type based on URL
366 if (boost::starts_with(
367 certURI,
368 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
369 {
Ed Tanous26f69762022-01-25 09:49:11 -0800370 if (optKeyUsage->empty())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700371 {
George Liu0fda0f12021-11-16 10:06:17 +0800372 optKeyUsage->push_back("ServerAuthentication");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700373 }
George Liu0fda0f12021-11-16 10:06:17 +0800374 else if (optKeyUsage->size() == 1)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700375 {
George Liu0fda0f12021-11-16 10:06:17 +0800376 if ((*optKeyUsage)[0] != "ServerAuthentication")
377 {
378 messages::propertyValueNotInList(
379 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
380 return;
381 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700382 }
383 else
384 {
385 messages::actionParameterNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800386 asyncResp->res, "KeyUsage", "GenerateCSR");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700387 return;
388 }
George Liu0fda0f12021-11-16 10:06:17 +0800389 }
390 else if (boost::starts_with(
391 certURI,
392 "/redfish/v1/AccountService/LDAP/Certificates"))
393 {
Ed Tanous26f69762022-01-25 09:49:11 -0800394 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800395 {
396 optKeyUsage->push_back("ClientAuthentication");
397 }
398 else if (optKeyUsage->size() == 1)
399 {
400 if ((*optKeyUsage)[0] != "ClientAuthentication")
401 {
402 messages::propertyValueNotInList(
403 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
404 return;
405 }
406 }
407 else
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700408 {
409 messages::actionParameterNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800410 asyncResp->res, "KeyUsage", "GenerateCSR");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700411 return;
412 }
George Liu0fda0f12021-11-16 10:06:17 +0800413 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500414
George Liu0fda0f12021-11-16 10:06:17 +0800415 // Only allow one CSR matcher at a time so setting retry
416 // time-out and timer expiry to 10 seconds for now.
417 static const int timeOut = 10;
418 if (csrMatcher)
419 {
420 messages::serviceTemporarilyUnavailable(
421 asyncResp->res, std::to_string(timeOut));
422 return;
423 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500424
George Liu0fda0f12021-11-16 10:06:17 +0800425 // Make this static so it survives outside this method
426 static boost::asio::steady_timer timeout(*req.ioService);
427 timeout.expires_after(std::chrono::seconds(timeOut));
428 timeout.async_wait(
429 [asyncResp](const boost::system::error_code& ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700430 csrMatcher = nullptr;
431 if (ec)
432 {
433 // operation_aborted is expected if timer is canceled
434 // before completion.
435 if (ec != boost::asio::error::operation_aborted)
436 {
437 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
438 }
439 return;
440 }
441 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
442 messages::internalError(asyncResp->res);
443 });
444
George Liu0fda0f12021-11-16 10:06:17 +0800445 // create a matcher to wait on CSR object
446 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
447 std::string match("type='signal',"
448 "interface='org.freedesktop.DBus.ObjectManager',"
449 "path='" +
450 objectPath +
451 "',"
452 "member='InterfacesAdded'");
453 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
454 *crow::connections::systemBus, match,
455 [asyncResp, service, objectPath,
456 certURI](sdbusplus::message::message& m) {
457 timeout.cancel();
458 if (m.is_method_error())
459 {
460 BMCWEB_LOG_ERROR << "Dbus method error!!!";
461 messages::internalError(asyncResp->res);
462 return;
463 }
Ed Tanousb9d36b42022-02-26 21:42:46 -0800464
465 dbus::utility::DBusInteracesMap interfacesProperties;
466
George Liu0fda0f12021-11-16 10:06:17 +0800467 sdbusplus::message::object_path csrObjectPath;
468 m.read(csrObjectPath, interfacesProperties);
469 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
470 for (auto& interface : interfacesProperties)
471 {
472 if (interface.first == "xyz.openbmc_project.Certs.CSR")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700473 {
George Liu0fda0f12021-11-16 10:06:17 +0800474 getCSR(asyncResp, certURI, service, objectPath,
475 csrObjectPath.str);
476 break;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700477 }
George Liu0fda0f12021-11-16 10:06:17 +0800478 }
479 });
480 crow::connections::systemBus->async_method_call(
Ed Tanous914e2d52022-01-07 11:38:34 -0800481 [asyncResp](const boost::system::error_code ec,
George Liu0fda0f12021-11-16 10:06:17 +0800482 const std::string&) {
483 if (ec)
484 {
485 BMCWEB_LOG_ERROR << "DBUS response error: "
486 << ec.message();
487 messages::internalError(asyncResp->res);
488 return;
489 }
490 },
491 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
492 "GenerateCSR", *optAlternativeNames, *optChallengePassword,
493 city, commonName, *optContactPerson, country, *optEmail,
494 *optGivenName, *optInitials, *optKeyBitLength, *optKeyCurveId,
495 *optKeyPairAlgorithm, *optKeyUsage, organization,
496 organizationalUnit, state, *optSurname, *optUnstructuredName);
497 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700498} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500499
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600500/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500501 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600502 *
503 * @param[in] asyncResp Shared pointer to the response message
504 * @param[in] str Issuer/Subject value in key=value pairs
505 * @param[in] type Issuer/Subject
506 * @return None
507 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500508static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600509 const std::string_view value)
510{
511 // example: O=openbmc-project.xyz,CN=localhost
512 std::string_view::iterator i = value.begin();
513 while (i != value.end())
514 {
515 std::string_view::iterator tokenBegin = i;
516 while (i != value.end() && *i != '=')
517 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530518 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600519 }
520 if (i == value.end())
521 {
522 break;
523 }
Ed Tanous271584a2019-07-09 16:24:22 -0700524 const std::string_view key(tokenBegin,
525 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530526 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600527 tokenBegin = i;
528 while (i != value.end() && *i != ',')
529 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530530 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600531 }
Ed Tanous271584a2019-07-09 16:24:22 -0700532 const std::string_view val(tokenBegin,
533 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600534 if (key == "L")
535 {
536 out["City"] = val;
537 }
538 else if (key == "CN")
539 {
540 out["CommonName"] = val;
541 }
542 else if (key == "C")
543 {
544 out["Country"] = val;
545 }
546 else if (key == "O")
547 {
548 out["Organization"] = val;
549 }
550 else if (key == "OU")
551 {
552 out["OrganizationalUnit"] = val;
553 }
554 else if (key == "ST")
555 {
556 out["State"] = val;
557 }
558 // skip comma character
559 if (i != value.end())
560 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530561 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600562 }
563 }
564}
565
566/**
567 * @brief Retrieve the certificates properties and append to the response
568 * message
569 *
570 * @param[in] asyncResp Shared pointer to the response message
571 * @param[in] objectPath Path of the D-Bus service object
572 * @param[in] certId Id of the certificate
573 * @param[in] certURL URL of the certificate object
574 * @param[in] name name of the certificate
575 * @return None
576 */
577static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800578 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
579 const std::string& objectPath, const std::string& service, long certId,
580 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600581{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600582 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
583 << " certId=" << certId << " certURl=" << certURL;
584 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800585 [asyncResp, certURL, certId,
586 name](const boost::system::error_code ec,
587 const dbus::utility::DBusPropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600588 if (ec)
589 {
590 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500591 messages::resourceNotFound(asyncResp->res, name,
592 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600593 return;
594 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600595 asyncResp->res.jsonValue = {
596 {"@odata.id", certURL},
597 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600598 {"Id", std::to_string(certId)},
599 {"Name", name},
600 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500601 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600602 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600603 if (property.first == "CertificateString")
604 {
605 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500606 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600607 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800608 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600609 {
610 asyncResp->res.jsonValue["CertificateString"] = *value;
611 }
612 }
613 else if (property.first == "KeyUsage")
614 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500615 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600616 asyncResp->res.jsonValue["KeyUsage"];
617 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500618 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600619 std::get_if<std::vector<std::string>>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800620 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600621 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500622 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600623 {
624 keyUsage.push_back(usage);
625 }
626 }
627 }
628 else if (property.first == "Issuer")
629 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500630 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600631 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800632 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600633 {
634 updateCertIssuerOrSubject(
635 asyncResp->res.jsonValue["Issuer"], *value);
636 }
637 }
638 else if (property.first == "Subject")
639 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500640 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600641 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800642 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600643 {
644 updateCertIssuerOrSubject(
645 asyncResp->res.jsonValue["Subject"], *value);
646 }
647 }
648 else if (property.first == "ValidNotAfter")
649 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500650 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600651 std::get_if<uint64_t>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800652 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600653 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600654 asyncResp->res.jsonValue["ValidNotAfter"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800655 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600656 }
657 }
658 else if (property.first == "ValidNotBefore")
659 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500660 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600661 std::get_if<uint64_t>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800662 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600663 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600664 asyncResp->res.jsonValue["ValidNotBefore"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800665 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600666 }
667 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600668 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600669 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600670 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600671 service, objectPath, certs::dbusPropIntf, "GetAll",
672 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600673}
674
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600675/**
676 * Action to replace an existing certificate
677 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700678inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600679{
George Liu0fda0f12021-11-16 10:06:17 +0800680 BMCWEB_ROUTE(
681 app,
682 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700683 .privileges(redfish::privileges::postCertificateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700684 .methods(
685 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700686 post)([&app](
687 const crow::Request& req,
688 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
689 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
690 {
691 return;
692 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700693 std::string certificate;
694 nlohmann::json certificateUri;
695 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600696
Willy Tu15ed6782021-12-14 11:03:16 -0800697 if (!json_util::readJsonAction(req, asyncResp->res,
698 "CertificateString", certificate,
699 "CertificateUri", certificateUri,
700 "CertificateType", certificateType))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700701 {
702 BMCWEB_LOG_ERROR << "Required parameters are missing";
703 messages::internalError(asyncResp->res);
704 return;
705 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800706
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700707 if (!certificateType)
708 {
709 // should never happen, but it never hurts to be paranoid.
710 return;
711 }
712 if (certificateType != "PEM")
713 {
714 messages::actionParameterNotSupported(
715 asyncResp->res, "CertificateType", "ReplaceCertificate");
716 return;
717 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600718
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700719 std::string certURI;
720 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
721 "@odata.id", certURI))
722 {
723 messages::actionParameterMissing(
724 asyncResp->res, "ReplaceCertificate", "CertificateUri");
725 return;
726 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600727
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700728 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
729 long id = getIDFromURL(certURI);
730 if (id < 0)
731 {
732 messages::actionParameterValueFormatError(
733 asyncResp->res, certURI, "CertificateUri",
734 "ReplaceCertificate");
735 return;
736 }
737 std::string objectPath;
738 std::string name;
739 std::string service;
George Liu0fda0f12021-11-16 10:06:17 +0800740 if (boost::starts_with(
741 certURI,
742 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700743 {
744 objectPath = std::string(certs::httpsObjectPath) + "/" +
745 std::to_string(id);
746 name = "HTTPS certificate";
747 service = certs::httpsServiceName;
748 }
749 else if (boost::starts_with(
750 certURI,
751 "/redfish/v1/AccountService/LDAP/Certificates/"))
752 {
753 objectPath = std::string(certs::ldapObjectPath) + "/" +
754 std::to_string(id);
755 name = "LDAP certificate";
756 service = certs::ldapServiceName;
757 }
758 else if (boost::starts_with(
759 certURI,
760 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
761 {
762 objectPath = std::string(certs::authorityObjectPath) + "/" +
763 std::to_string(id);
764 name = "TrustStore certificate";
765 service = certs::authorityServiceName;
766 }
767 else
768 {
769 messages::actionParameterNotSupported(
770 asyncResp->res, "CertificateUri", "ReplaceCertificate");
771 return;
772 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600773
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700774 std::shared_ptr<CertificateFile> certFile =
775 std::make_shared<CertificateFile>(certificate);
776 crow::connections::systemBus->async_method_call(
777 [asyncResp, certFile, objectPath, service, certURI, id,
778 name](const boost::system::error_code ec) {
779 if (ec)
780 {
781 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Jiaqing Zhao90d2d1e2022-04-13 17:01:57 +0800782 if (ec.value() ==
783 boost::system::linux_error::bad_request_descriptor)
784 {
785 messages::resourceNotFound(asyncResp->res, name,
786 std::to_string(id));
787 return;
788 }
789 messages::internalError(asyncResp->res);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700790 return;
791 }
792 getCertificateProperties(asyncResp, objectPath, service, id,
793 certURI, name);
794 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
795 << certFile->getCertFilePath();
796 },
797 service, objectPath, certs::certReplaceIntf, "Replace",
798 certFile->getCertFilePath());
799 });
800} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600801
802/**
803 * Certificate resource describes a certificate used to prove the identity
804 * of a component, account or service.
805 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700806
807inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600808{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700809 BMCWEB_ROUTE(
810 app,
811 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700812 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700813 .methods(
814 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700815 get)([&app](const crow::Request& req,
816 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
817 const std::string& param) -> void {
818 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
819 {
820 return;
821 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700822 if (param.empty())
823 {
824 messages::internalError(asyncResp->res);
825 return;
826 }
827 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600828
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700829 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
830 << std::to_string(id);
831 std::string certURL =
832 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
833 std::to_string(id);
834 std::string objectPath = certs::httpsObjectPath;
835 objectPath += "/";
836 objectPath += std::to_string(id);
837 getCertificateProperties(asyncResp, objectPath,
838 certs::httpsServiceName, id, certURL,
839 "HTTPS Certificate");
840 });
841}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600842
843/**
844 * Collection of HTTPS certificates
845 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700846inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600847{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700848 BMCWEB_ROUTE(app,
849 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700850 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700851 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
852 const std::shared_ptr<
853 bmcweb::AsyncResp>&
854 asyncResp) {
855 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
856 {
857 return;
858 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700859 asyncResp->res.jsonValue = {
860 {"@odata.id",
861 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
862 {"@odata.type", "#CertificateCollection.CertificateCollection"},
863 {"Name", "HTTPS Certificates Collection"},
864 {"Description", "A Collection of HTTPS certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +0800865
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700866 crow::connections::systemBus->async_method_call(
867 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800868 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700869 if (ec)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600870 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700871 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
872 messages::internalError(asyncResp->res);
873 return;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600874 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700875 nlohmann::json& members =
876 asyncResp->res.jsonValue["Members"];
877 members = nlohmann::json::array();
878 for (const auto& cert : certs)
879 {
880 long id = getIDFromURL(cert.first.str);
881 if (id >= 0)
882 {
883 members.push_back(
884 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +0800885 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700886 std::to_string(id)}});
887 }
888 }
889 asyncResp->res.jsonValue["Members@odata.count"] =
890 members.size();
891 },
892 certs::httpsServiceName, certs::httpsObjectPath,
893 certs::dbusObjManagerIntf, "GetManagedObjects");
894 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600895
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700896 BMCWEB_ROUTE(app,
897 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700898 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +0800899 .methods(
900 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700901 post)([&app](
902 const crow::Request& req,
903 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
904 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
905 {
906 return;
907 }
George Liu0fda0f12021-11-16 10:06:17 +0800908 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
zhanghch058d1b46d2021-04-01 11:18:24 +0800909
George Liu0fda0f12021-11-16 10:06:17 +0800910 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
911 {"Description", "HTTPS Certificate"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600912
George Liu0fda0f12021-11-16 10:06:17 +0800913 std::string certFileBody =
914 getCertificateFromReqBody(asyncResp, req);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200915
George Liu0fda0f12021-11-16 10:06:17 +0800916 if (certFileBody.empty())
917 {
918 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
919 messages::unrecognizedRequestBody(asyncResp->res);
920 return;
921 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700922
George Liu0fda0f12021-11-16 10:06:17 +0800923 std::shared_ptr<CertificateFile> certFile =
924 std::make_shared<CertificateFile>(certFileBody);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700925
George Liu0fda0f12021-11-16 10:06:17 +0800926 crow::connections::systemBus->async_method_call(
927 [asyncResp, certFile](const boost::system::error_code ec,
928 const std::string& objectPath) {
929 if (ec)
930 {
931 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
932 messages::internalError(asyncResp->res);
933 return;
934 }
935 long certId = getIDFromURL(objectPath);
936 if (certId < 0)
937 {
938 BMCWEB_LOG_ERROR << "Invalid objectPath value"
939 << objectPath;
940 messages::internalError(asyncResp->res);
941 return;
942 }
943 std::string certURL =
944 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
945 std::to_string(certId);
946 getCertificateProperties(asyncResp, objectPath,
947 certs::httpsServiceName, certId,
948 certURL, "HTTPS Certificate");
949 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
950 << certFile->getCertFilePath();
951 },
952 certs::httpsServiceName, certs::httpsObjectPath,
953 certs::certInstallIntf, "Install", certFile->getCertFilePath());
954 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700955} // requestRoutesHTTPSCertificateCollection
956
957/**
958 * @brief Retrieve the certificates installed list and append to the
959 * response
960 *
961 * @param[in] asyncResp Shared pointer to the response message
962 * @param[in] certURL Path of the certificate object
963 * @param[in] path Path of the D-Bus service object
964 * @return None
965 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700966inline void
967 getCertificateLocations(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
968 const std::string& certURL, const std::string& path,
969 const std::string& service)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700970{
971 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
972 << " Path=" << path << " service= " << service;
973 crow::connections::systemBus->async_method_call(
974 [asyncResp, certURL](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800975 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700976 if (ec)
977 {
978 BMCWEB_LOG_WARNING
979 << "Certificate collection query failed: " << ec
980 << ", skipping " << certURL;
981 return;
982 }
983 nlohmann::json& links =
984 asyncResp->res.jsonValue["Links"]["Certificates"];
Ed Tanous9eb808c2022-01-25 10:19:23 -0800985 for (const auto& cert : certs)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700986 {
987 long id = getIDFromURL(cert.first.str);
988 if (id >= 0)
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200989 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700990 links.push_back(
991 {{"@odata.id", certURL + std::to_string(id)}});
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200992 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700993 }
994 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
995 links.size();
996 },
997 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
998}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600999
1000/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001001 * The certificate location schema defines a resource that an administrator
1002 * can use in order to locate all certificates installed on a given service.
1003 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001004inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001005{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001006 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -07001007 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001008 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1009 const std::shared_ptr<
1010 bmcweb::AsyncResp>&
1011 asyncResp) {
1012 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1013 {
1014 return;
1015 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001016 asyncResp->res.jsonValue = {
1017 {"@odata.id",
1018 "/redfish/v1/CertificateService/CertificateLocations"},
1019 {"@odata.type",
1020 "#CertificateLocations.v1_0_0.CertificateLocations"},
1021 {"Name", "Certificate Locations"},
1022 {"Id", "CertificateLocations"},
1023 {"Description",
1024 "Defines a resource that an administrator can use in order to "
1025 "locate all certificates installed on a given service"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001026
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001027 nlohmann::json& links =
1028 asyncResp->res.jsonValue["Links"]["Certificates"];
1029 links = nlohmann::json::array();
1030 getCertificateLocations(
1031 asyncResp,
1032 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
1033 certs::httpsObjectPath, certs::httpsServiceName);
1034 getCertificateLocations(
1035 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/",
1036 certs::ldapObjectPath, certs::ldapServiceName);
1037 getCertificateLocations(
1038 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1039 certs::authorityObjectPath, certs::authorityServiceName);
1040 });
1041}
1042// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001043
1044/**
1045 * Collection of LDAP certificates
1046 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001047inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001048{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001049 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001050 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001051 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1052 const std::shared_ptr<
1053 bmcweb::AsyncResp>&
1054 asyncResp) {
1055 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1056 {
1057 return;
1058 }
George Liu0fda0f12021-11-16 10:06:17 +08001059 asyncResp->res.jsonValue = {
1060 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1061 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1062 {"Name", "LDAP Certificates Collection"},
1063 {"Description", "A Collection of LDAP certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001064
George Liu0fda0f12021-11-16 10:06:17 +08001065 crow::connections::systemBus->async_method_call(
1066 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001067 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001068 nlohmann::json& members =
1069 asyncResp->res.jsonValue["Members"];
1070 nlohmann::json& count =
1071 asyncResp->res.jsonValue["Members@odata.count"];
1072 members = nlohmann::json::array();
1073 count = 0;
1074 if (ec)
1075 {
1076 BMCWEB_LOG_WARNING << "LDAP certificate query failed: "
1077 << ec;
1078 return;
1079 }
1080 for (const auto& cert : certs)
1081 {
1082 long id = getIDFromURL(cert.first.str);
1083 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001084 {
George Liu0fda0f12021-11-16 10:06:17 +08001085 members.push_back(
1086 {{"@odata.id",
1087 "/redfish/v1/AccountService/LDAP/Certificates/" +
1088 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001089 }
George Liu0fda0f12021-11-16 10:06:17 +08001090 }
1091 count = members.size();
1092 },
1093 certs::ldapServiceName, certs::ldapObjectPath,
1094 certs::dbusObjManagerIntf, "GetManagedObjects");
1095 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001096
1097 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001098 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001099 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001100 [&app](const crow::Request& req,
1101 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1102 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1103 {
1104 return;
1105 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001106 std::string certFileBody =
1107 getCertificateFromReqBody(asyncResp, req);
1108
1109 if (certFileBody.empty())
Marri Devender Rao37cce912019-02-20 01:05:22 -06001110 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001111 BMCWEB_LOG_ERROR
1112 << "Cannot get certificate from request body.";
1113 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001114 return;
1115 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001116
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001117 std::shared_ptr<CertificateFile> certFile =
1118 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001119
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001120 crow::connections::systemBus->async_method_call(
1121 [asyncResp, certFile](const boost::system::error_code ec,
1122 const std::string& objectPath) {
1123 if (ec)
1124 {
1125 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1126 messages::internalError(asyncResp->res);
1127 return;
1128 }
1129 long certId = getIDFromURL(objectPath);
1130 if (certId < 0)
1131 {
1132 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1133 << objectPath;
1134 messages::internalError(asyncResp->res);
1135 return;
1136 }
1137 std::string certURL =
1138 "/redfish/v1/AccountService/LDAP/Certificates/" +
1139 std::to_string(certId);
1140 getCertificateProperties(asyncResp, objectPath,
1141 certs::ldapServiceName, certId,
1142 certURL, "LDAP Certificate");
1143 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1144 << certFile->getCertFilePath();
1145 },
1146 certs::ldapServiceName, certs::ldapObjectPath,
1147 certs::certInstallIntf, "Install",
1148 certFile->getCertFilePath());
1149 });
1150} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001151
1152/**
1153 * Certificate resource describes a certificate used to prove the identity
1154 * of a component, account or service.
1155 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001156inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001157{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001158 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001159 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001160 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001161 [&app](const crow::Request& req,
1162 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1163 const std::string&) {
1164 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1165 {
1166 return;
1167 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001168 long id = getIDFromURL(req.url);
1169 if (id < 0)
1170 {
1171 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1172 messages::internalError(asyncResp->res);
1173 return;
1174 }
1175 BMCWEB_LOG_DEBUG << "LDAP Certificate ID="
1176 << std::to_string(id);
1177 std::string certURL =
1178 "/redfish/v1/AccountService/LDAP/Certificates/" +
1179 std::to_string(id);
1180 std::string objectPath = certs::ldapObjectPath;
1181 objectPath += "/";
1182 objectPath += std::to_string(id);
1183 getCertificateProperties(asyncResp, objectPath,
1184 certs::ldapServiceName, id, certURL,
1185 "LDAP Certificate");
1186 });
1187} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001188/**
1189 * Collection of TrustStoreCertificate certificates
1190 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001191inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001192{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001193 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001194 .privileges(redfish::privileges::getCertificate)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001195 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1196 const std::shared_ptr<
1197 bmcweb::AsyncResp>&
1198 asyncResp) {
1199 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1200 {
1201 return;
1202 }
George Liu0fda0f12021-11-16 10:06:17 +08001203 asyncResp->res.jsonValue = {
1204 {"@odata.id",
1205 "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1206 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1207 {"Name", "TrustStore Certificates Collection"},
1208 {"Description",
1209 "A Collection of TrustStore certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001210
George Liu0fda0f12021-11-16 10:06:17 +08001211 crow::connections::systemBus->async_method_call(
1212 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001213 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001214 if (ec)
1215 {
1216 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1217 messages::internalError(asyncResp->res);
1218 return;
1219 }
1220 nlohmann::json& members =
1221 asyncResp->res.jsonValue["Members"];
1222 members = nlohmann::json::array();
1223 for (const auto& cert : certs)
1224 {
1225 long id = getIDFromURL(cert.first.str);
1226 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001227 {
George Liu0fda0f12021-11-16 10:06:17 +08001228 members.push_back(
1229 {{"@odata.id",
1230 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1231 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001232 }
George Liu0fda0f12021-11-16 10:06:17 +08001233 }
1234 asyncResp->res.jsonValue["Members@odata.count"] =
1235 members.size();
1236 },
1237 certs::authorityServiceName, certs::authorityObjectPath,
1238 certs::dbusObjManagerIntf, "GetManagedObjects");
1239 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001240
1241 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001242 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +08001243 .methods(
1244 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -07001245 post)([&app](
1246 const crow::Request& req,
1247 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1248 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1249 {
1250 return;
1251 }
George Liu0fda0f12021-11-16 10:06:17 +08001252 std::string certFileBody =
1253 getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001254
George Liu0fda0f12021-11-16 10:06:17 +08001255 if (certFileBody.empty())
1256 {
1257 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1258 messages::unrecognizedRequestBody(asyncResp->res);
1259 return;
1260 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001261
George Liu0fda0f12021-11-16 10:06:17 +08001262 std::shared_ptr<CertificateFile> certFile =
1263 std::make_shared<CertificateFile>(certFileBody);
1264 crow::connections::systemBus->async_method_call(
1265 [asyncResp, certFile](const boost::system::error_code ec,
1266 const std::string& objectPath) {
1267 if (ec)
1268 {
1269 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1270 messages::internalError(asyncResp->res);
1271 return;
1272 }
1273 long certId = getIDFromURL(objectPath);
1274 if (certId < 0)
1275 {
1276 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1277 << objectPath;
1278 messages::internalError(asyncResp->res);
1279 return;
1280 }
1281 std::string certURL =
1282 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1283 std::to_string(certId);
zhanghch058d1b46d2021-04-01 11:18:24 +08001284
George Liu0fda0f12021-11-16 10:06:17 +08001285 getCertificateProperties(
1286 asyncResp, objectPath, certs::authorityServiceName,
1287 certId, certURL, "TrustStore Certificate");
1288 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1289 << certFile->getCertFilePath();
1290 },
1291 certs::authorityServiceName, certs::authorityObjectPath,
1292 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1293 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001294} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001295
1296/**
1297 * Certificate resource describes a certificate used to prove the identity
1298 * of a component, account or service.
1299 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001300inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001301{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001302 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001303 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001304 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001305 [&app](const crow::Request& req,
1306 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1307 const std::string&) {
1308 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1309 {
1310 return;
1311 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001312 long id = getIDFromURL(req.url);
1313 if (id < 0)
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001314 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001315 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1316 messages::internalError(asyncResp->res);
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001317 return;
1318 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001319 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1320 << std::to_string(id);
1321 std::string certURL =
1322 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1323 std::to_string(id);
1324 std::string objectPath = certs::authorityObjectPath;
1325 objectPath += "/";
1326 objectPath += std::to_string(id);
1327 getCertificateProperties(asyncResp, objectPath,
1328 certs::authorityServiceName, id,
1329 certURL, "TrustStore Certificate");
1330 });
1331
1332 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001333 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001334 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001335 [&app](const crow::Request& req,
1336 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1337 const std::string& param) {
1338 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1339 {
1340 return;
1341 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001342 if (param.empty())
1343 {
1344 messages::internalError(asyncResp->res);
1345 return;
1346 }
1347
1348 long id = getIDFromURL(req.url);
1349 if (id < 0)
1350 {
1351 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1352 messages::resourceNotFound(asyncResp->res,
1353 "TrustStore Certificate",
1354 std::string(req.url));
1355 return;
1356 }
1357 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1358 << std::to_string(id);
1359 std::string certPath = certs::authorityObjectPath;
1360 certPath += "/";
1361 certPath += std::to_string(id);
1362
1363 crow::connections::systemBus->async_method_call(
1364 [asyncResp, id](const boost::system::error_code ec) {
1365 if (ec)
1366 {
1367 messages::resourceNotFound(asyncResp->res,
1368 "TrustStore Certificate",
1369 std::to_string(id));
1370 return;
1371 }
1372 BMCWEB_LOG_INFO << "Certificate deleted";
1373 asyncResp->res.result(
1374 boost::beast::http::status::no_content);
1375 },
1376 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1377 "Delete");
1378 });
1379} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001380} // namespace redfish