blob: 4882ec30f8d45d5e3a2219a328884fd5dc05c910 [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 Tanousb9d36b42022-02-26 21:42:46 -0800455
456 dbus::utility::DBusInteracesMap interfacesProperties;
457
George Liu0fda0f12021-11-16 10:06:17 +0800458 sdbusplus::message::object_path csrObjectPath;
459 m.read(csrObjectPath, interfacesProperties);
460 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
461 for (auto& interface : interfacesProperties)
462 {
463 if (interface.first == "xyz.openbmc_project.Certs.CSR")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700464 {
George Liu0fda0f12021-11-16 10:06:17 +0800465 getCSR(asyncResp, certURI, service, objectPath,
466 csrObjectPath.str);
467 break;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700468 }
George Liu0fda0f12021-11-16 10:06:17 +0800469 }
470 });
471 crow::connections::systemBus->async_method_call(
Ed Tanous914e2d52022-01-07 11:38:34 -0800472 [asyncResp](const boost::system::error_code ec,
George Liu0fda0f12021-11-16 10:06:17 +0800473 const std::string&) {
474 if (ec)
475 {
476 BMCWEB_LOG_ERROR << "DBUS response error: "
477 << ec.message();
478 messages::internalError(asyncResp->res);
479 return;
480 }
481 },
482 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
483 "GenerateCSR", *optAlternativeNames, *optChallengePassword,
484 city, commonName, *optContactPerson, country, *optEmail,
485 *optGivenName, *optInitials, *optKeyBitLength, *optKeyCurveId,
486 *optKeyPairAlgorithm, *optKeyUsage, organization,
487 organizationalUnit, state, *optSurname, *optUnstructuredName);
488 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700489} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500490
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600491/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500492 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600493 *
494 * @param[in] asyncResp Shared pointer to the response message
495 * @param[in] str Issuer/Subject value in key=value pairs
496 * @param[in] type Issuer/Subject
497 * @return None
498 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500499static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600500 const std::string_view value)
501{
502 // example: O=openbmc-project.xyz,CN=localhost
503 std::string_view::iterator i = value.begin();
504 while (i != value.end())
505 {
506 std::string_view::iterator tokenBegin = i;
507 while (i != value.end() && *i != '=')
508 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530509 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600510 }
511 if (i == value.end())
512 {
513 break;
514 }
Ed Tanous271584a2019-07-09 16:24:22 -0700515 const std::string_view key(tokenBegin,
516 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530517 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600518 tokenBegin = i;
519 while (i != value.end() && *i != ',')
520 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530521 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600522 }
Ed Tanous271584a2019-07-09 16:24:22 -0700523 const std::string_view val(tokenBegin,
524 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600525 if (key == "L")
526 {
527 out["City"] = val;
528 }
529 else if (key == "CN")
530 {
531 out["CommonName"] = val;
532 }
533 else if (key == "C")
534 {
535 out["Country"] = val;
536 }
537 else if (key == "O")
538 {
539 out["Organization"] = val;
540 }
541 else if (key == "OU")
542 {
543 out["OrganizationalUnit"] = val;
544 }
545 else if (key == "ST")
546 {
547 out["State"] = val;
548 }
549 // skip comma character
550 if (i != value.end())
551 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530552 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600553 }
554 }
555}
556
557/**
558 * @brief Retrieve the certificates properties and append to the response
559 * message
560 *
561 * @param[in] asyncResp Shared pointer to the response message
562 * @param[in] objectPath Path of the D-Bus service object
563 * @param[in] certId Id of the certificate
564 * @param[in] certURL URL of the certificate object
565 * @param[in] name name of the certificate
566 * @return None
567 */
568static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800569 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
570 const std::string& objectPath, const std::string& service, long certId,
571 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600572{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600573 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
574 << " certId=" << certId << " certURl=" << certURL;
575 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800576 [asyncResp, certURL, certId,
577 name](const boost::system::error_code ec,
578 const dbus::utility::DBusPropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600579 if (ec)
580 {
581 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500582 messages::resourceNotFound(asyncResp->res, name,
583 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600584 return;
585 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600586 asyncResp->res.jsonValue = {
587 {"@odata.id", certURL},
588 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600589 {"Id", std::to_string(certId)},
590 {"Name", name},
591 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500592 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600593 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600594 if (property.first == "CertificateString")
595 {
596 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500597 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600598 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800599 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600600 {
601 asyncResp->res.jsonValue["CertificateString"] = *value;
602 }
603 }
604 else if (property.first == "KeyUsage")
605 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500606 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600607 asyncResp->res.jsonValue["KeyUsage"];
608 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500609 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600610 std::get_if<std::vector<std::string>>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800611 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600612 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500613 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600614 {
615 keyUsage.push_back(usage);
616 }
617 }
618 }
619 else if (property.first == "Issuer")
620 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500621 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600622 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800623 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600624 {
625 updateCertIssuerOrSubject(
626 asyncResp->res.jsonValue["Issuer"], *value);
627 }
628 }
629 else if (property.first == "Subject")
630 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500631 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600632 std::get_if<std::string>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800633 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600634 {
635 updateCertIssuerOrSubject(
636 asyncResp->res.jsonValue["Subject"], *value);
637 }
638 }
639 else if (property.first == "ValidNotAfter")
640 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500641 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600642 std::get_if<uint64_t>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800643 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600644 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600645 asyncResp->res.jsonValue["ValidNotAfter"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800646 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600647 }
648 }
649 else if (property.first == "ValidNotBefore")
650 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500651 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600652 std::get_if<uint64_t>(&property.second);
Ed Tanouse662eae2022-01-25 10:39:19 -0800653 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600654 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600655 asyncResp->res.jsonValue["ValidNotBefore"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800656 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600657 }
658 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600659 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600660 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600661 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600662 service, objectPath, certs::dbusPropIntf, "GetAll",
663 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600664}
665
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600666/**
667 * Action to replace an existing certificate
668 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700669inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600670{
George Liu0fda0f12021-11-16 10:06:17 +0800671 BMCWEB_ROUTE(
672 app,
673 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700674 .privileges(redfish::privileges::postCertificateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700675 .methods(
676 boost::beast::http::verb::
677 post)([](const crow::Request& req,
678 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
679 std::string certificate;
680 nlohmann::json certificateUri;
681 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600682
Willy Tu15ed6782021-12-14 11:03:16 -0800683 if (!json_util::readJsonAction(req, asyncResp->res,
684 "CertificateString", certificate,
685 "CertificateUri", certificateUri,
686 "CertificateType", certificateType))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700687 {
688 BMCWEB_LOG_ERROR << "Required parameters are missing";
689 messages::internalError(asyncResp->res);
690 return;
691 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800692
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700693 if (!certificateType)
694 {
695 // should never happen, but it never hurts to be paranoid.
696 return;
697 }
698 if (certificateType != "PEM")
699 {
700 messages::actionParameterNotSupported(
701 asyncResp->res, "CertificateType", "ReplaceCertificate");
702 return;
703 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600704
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700705 std::string certURI;
706 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
707 "@odata.id", certURI))
708 {
709 messages::actionParameterMissing(
710 asyncResp->res, "ReplaceCertificate", "CertificateUri");
711 return;
712 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600713
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700714 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
715 long id = getIDFromURL(certURI);
716 if (id < 0)
717 {
718 messages::actionParameterValueFormatError(
719 asyncResp->res, certURI, "CertificateUri",
720 "ReplaceCertificate");
721 return;
722 }
723 std::string objectPath;
724 std::string name;
725 std::string service;
George Liu0fda0f12021-11-16 10:06:17 +0800726 if (boost::starts_with(
727 certURI,
728 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700729 {
730 objectPath = std::string(certs::httpsObjectPath) + "/" +
731 std::to_string(id);
732 name = "HTTPS certificate";
733 service = certs::httpsServiceName;
734 }
735 else if (boost::starts_with(
736 certURI,
737 "/redfish/v1/AccountService/LDAP/Certificates/"))
738 {
739 objectPath = std::string(certs::ldapObjectPath) + "/" +
740 std::to_string(id);
741 name = "LDAP certificate";
742 service = certs::ldapServiceName;
743 }
744 else if (boost::starts_with(
745 certURI,
746 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
747 {
748 objectPath = std::string(certs::authorityObjectPath) + "/" +
749 std::to_string(id);
750 name = "TrustStore certificate";
751 service = certs::authorityServiceName;
752 }
753 else
754 {
755 messages::actionParameterNotSupported(
756 asyncResp->res, "CertificateUri", "ReplaceCertificate");
757 return;
758 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600759
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700760 std::shared_ptr<CertificateFile> certFile =
761 std::make_shared<CertificateFile>(certificate);
762 crow::connections::systemBus->async_method_call(
763 [asyncResp, certFile, objectPath, service, certURI, id,
764 name](const boost::system::error_code ec) {
765 if (ec)
766 {
767 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
768 messages::resourceNotFound(asyncResp->res, name,
769 std::to_string(id));
770 return;
771 }
772 getCertificateProperties(asyncResp, objectPath, service, id,
773 certURI, name);
774 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
775 << certFile->getCertFilePath();
776 },
777 service, objectPath, certs::certReplaceIntf, "Replace",
778 certFile->getCertFilePath());
779 });
780} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600781
782/**
783 * Certificate resource describes a certificate used to prove the identity
784 * of a component, account or service.
785 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700786
787inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600788{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700789 BMCWEB_ROUTE(
790 app,
791 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700792 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700793 .methods(
794 boost::beast::http::verb::
795 get)([](const crow::Request& req,
796 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
797 const std::string& param) -> void {
798 if (param.empty())
799 {
800 messages::internalError(asyncResp->res);
801 return;
802 }
803 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600804
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700805 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
806 << std::to_string(id);
807 std::string certURL =
808 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
809 std::to_string(id);
810 std::string objectPath = certs::httpsObjectPath;
811 objectPath += "/";
812 objectPath += std::to_string(id);
813 getCertificateProperties(asyncResp, objectPath,
814 certs::httpsServiceName, id, certURL,
815 "HTTPS Certificate");
816 });
817}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600818
819/**
820 * Collection of HTTPS certificates
821 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700822inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600823{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700824 BMCWEB_ROUTE(app,
825 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700826 .privileges(redfish::privileges::getCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700827 .methods(
828 boost::beast::http::verb::
829 get)([](const crow::Request&,
830 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
831 asyncResp->res.jsonValue = {
832 {"@odata.id",
833 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
834 {"@odata.type", "#CertificateCollection.CertificateCollection"},
835 {"Name", "HTTPS Certificates Collection"},
836 {"Description", "A Collection of HTTPS certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +0800837
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700838 crow::connections::systemBus->async_method_call(
839 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800840 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700841 if (ec)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600842 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700843 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
844 messages::internalError(asyncResp->res);
845 return;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600846 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700847 nlohmann::json& members =
848 asyncResp->res.jsonValue["Members"];
849 members = nlohmann::json::array();
850 for (const auto& cert : certs)
851 {
852 long id = getIDFromURL(cert.first.str);
853 if (id >= 0)
854 {
855 members.push_back(
856 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +0800857 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700858 std::to_string(id)}});
859 }
860 }
861 asyncResp->res.jsonValue["Members@odata.count"] =
862 members.size();
863 },
864 certs::httpsServiceName, certs::httpsObjectPath,
865 certs::dbusObjManagerIntf, "GetManagedObjects");
866 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600867
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700868 BMCWEB_ROUTE(app,
869 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700870 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +0800871 .methods(
872 boost::beast::http::verb::
873 post)([](const crow::Request& req,
874 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
875 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
zhanghch058d1b46d2021-04-01 11:18:24 +0800876
George Liu0fda0f12021-11-16 10:06:17 +0800877 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
878 {"Description", "HTTPS Certificate"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600879
George Liu0fda0f12021-11-16 10:06:17 +0800880 std::string certFileBody =
881 getCertificateFromReqBody(asyncResp, req);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200882
George Liu0fda0f12021-11-16 10:06:17 +0800883 if (certFileBody.empty())
884 {
885 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
886 messages::unrecognizedRequestBody(asyncResp->res);
887 return;
888 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700889
George Liu0fda0f12021-11-16 10:06:17 +0800890 std::shared_ptr<CertificateFile> certFile =
891 std::make_shared<CertificateFile>(certFileBody);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700892
George Liu0fda0f12021-11-16 10:06:17 +0800893 crow::connections::systemBus->async_method_call(
894 [asyncResp, certFile](const boost::system::error_code ec,
895 const std::string& objectPath) {
896 if (ec)
897 {
898 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
899 messages::internalError(asyncResp->res);
900 return;
901 }
902 long certId = getIDFromURL(objectPath);
903 if (certId < 0)
904 {
905 BMCWEB_LOG_ERROR << "Invalid objectPath value"
906 << objectPath;
907 messages::internalError(asyncResp->res);
908 return;
909 }
910 std::string certURL =
911 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
912 std::to_string(certId);
913 getCertificateProperties(asyncResp, objectPath,
914 certs::httpsServiceName, certId,
915 certURL, "HTTPS Certificate");
916 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
917 << certFile->getCertFilePath();
918 },
919 certs::httpsServiceName, certs::httpsObjectPath,
920 certs::certInstallIntf, "Install", certFile->getCertFilePath());
921 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700922} // requestRoutesHTTPSCertificateCollection
923
924/**
925 * @brief Retrieve the certificates installed list and append to the
926 * response
927 *
928 * @param[in] asyncResp Shared pointer to the response message
929 * @param[in] certURL Path of the certificate object
930 * @param[in] path Path of the D-Bus service object
931 * @return None
932 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700933inline void
934 getCertificateLocations(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
935 const std::string& certURL, const std::string& path,
936 const std::string& service)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700937{
938 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
939 << " Path=" << path << " service= " << service;
940 crow::connections::systemBus->async_method_call(
941 [asyncResp, certURL](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800942 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700943 if (ec)
944 {
945 BMCWEB_LOG_WARNING
946 << "Certificate collection query failed: " << ec
947 << ", skipping " << certURL;
948 return;
949 }
950 nlohmann::json& links =
951 asyncResp->res.jsonValue["Links"]["Certificates"];
Ed Tanous9eb808c2022-01-25 10:19:23 -0800952 for (const auto& cert : certs)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700953 {
954 long id = getIDFromURL(cert.first.str);
955 if (id >= 0)
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200956 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700957 links.push_back(
958 {{"@odata.id", certURL + std::to_string(id)}});
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200959 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700960 }
961 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
962 links.size();
963 },
964 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
965}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600966
967/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600968 * The certificate location schema defines a resource that an administrator
969 * can use in order to locate all certificates installed on a given service.
970 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700971inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600972{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700973 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700974 .privileges(redfish::privileges::getCertificateLocations)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700975 .methods(
976 boost::beast::http::verb::
977 get)([](const crow::Request&,
978 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
979 asyncResp->res.jsonValue = {
980 {"@odata.id",
981 "/redfish/v1/CertificateService/CertificateLocations"},
982 {"@odata.type",
983 "#CertificateLocations.v1_0_0.CertificateLocations"},
984 {"Name", "Certificate Locations"},
985 {"Id", "CertificateLocations"},
986 {"Description",
987 "Defines a resource that an administrator can use in order to "
988 "locate all certificates installed on a given service"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600989
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700990 nlohmann::json& links =
991 asyncResp->res.jsonValue["Links"]["Certificates"];
992 links = nlohmann::json::array();
993 getCertificateLocations(
994 asyncResp,
995 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
996 certs::httpsObjectPath, certs::httpsServiceName);
997 getCertificateLocations(
998 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/",
999 certs::ldapObjectPath, certs::ldapServiceName);
1000 getCertificateLocations(
1001 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1002 certs::authorityObjectPath, certs::authorityServiceName);
1003 });
1004}
1005// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001006
1007/**
1008 * Collection of LDAP certificates
1009 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001010inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001011{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001012 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001013 .privileges(redfish::privileges::getCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +08001014 .methods(
1015 boost::beast::http::verb::
1016 get)([](const crow::Request&,
1017 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1018 asyncResp->res.jsonValue = {
1019 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1020 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1021 {"Name", "LDAP Certificates Collection"},
1022 {"Description", "A Collection of LDAP certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001023
George Liu0fda0f12021-11-16 10:06:17 +08001024 crow::connections::systemBus->async_method_call(
1025 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001026 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001027 nlohmann::json& members =
1028 asyncResp->res.jsonValue["Members"];
1029 nlohmann::json& count =
1030 asyncResp->res.jsonValue["Members@odata.count"];
1031 members = nlohmann::json::array();
1032 count = 0;
1033 if (ec)
1034 {
1035 BMCWEB_LOG_WARNING << "LDAP certificate query failed: "
1036 << ec;
1037 return;
1038 }
1039 for (const auto& cert : certs)
1040 {
1041 long id = getIDFromURL(cert.first.str);
1042 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001043 {
George Liu0fda0f12021-11-16 10:06:17 +08001044 members.push_back(
1045 {{"@odata.id",
1046 "/redfish/v1/AccountService/LDAP/Certificates/" +
1047 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001048 }
George Liu0fda0f12021-11-16 10:06:17 +08001049 }
1050 count = members.size();
1051 },
1052 certs::ldapServiceName, certs::ldapObjectPath,
1053 certs::dbusObjManagerIntf, "GetManagedObjects");
1054 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001055
1056 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001057 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001058 .methods(boost::beast::http::verb::post)(
1059 [](const crow::Request& req,
1060 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1061 std::string certFileBody =
1062 getCertificateFromReqBody(asyncResp, req);
1063
1064 if (certFileBody.empty())
Marri Devender Rao37cce912019-02-20 01:05:22 -06001065 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001066 BMCWEB_LOG_ERROR
1067 << "Cannot get certificate from request body.";
1068 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001069 return;
1070 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001071
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001072 std::shared_ptr<CertificateFile> certFile =
1073 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001074
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001075 crow::connections::systemBus->async_method_call(
1076 [asyncResp, certFile](const boost::system::error_code ec,
1077 const std::string& objectPath) {
1078 if (ec)
1079 {
1080 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1081 messages::internalError(asyncResp->res);
1082 return;
1083 }
1084 long certId = getIDFromURL(objectPath);
1085 if (certId < 0)
1086 {
1087 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1088 << objectPath;
1089 messages::internalError(asyncResp->res);
1090 return;
1091 }
1092 std::string certURL =
1093 "/redfish/v1/AccountService/LDAP/Certificates/" +
1094 std::to_string(certId);
1095 getCertificateProperties(asyncResp, objectPath,
1096 certs::ldapServiceName, certId,
1097 certURL, "LDAP Certificate");
1098 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1099 << certFile->getCertFilePath();
1100 },
1101 certs::ldapServiceName, certs::ldapObjectPath,
1102 certs::certInstallIntf, "Install",
1103 certFile->getCertFilePath());
1104 });
1105} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001106
1107/**
1108 * Certificate resource describes a certificate used to prove the identity
1109 * of a component, account or service.
1110 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001111inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001112{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001113 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001114 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001115 .methods(boost::beast::http::verb::get)(
1116 [](const crow::Request& req,
1117 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1118 const std::string&) {
1119 long id = getIDFromURL(req.url);
1120 if (id < 0)
1121 {
1122 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1123 messages::internalError(asyncResp->res);
1124 return;
1125 }
1126 BMCWEB_LOG_DEBUG << "LDAP Certificate ID="
1127 << std::to_string(id);
1128 std::string certURL =
1129 "/redfish/v1/AccountService/LDAP/Certificates/" +
1130 std::to_string(id);
1131 std::string objectPath = certs::ldapObjectPath;
1132 objectPath += "/";
1133 objectPath += std::to_string(id);
1134 getCertificateProperties(asyncResp, objectPath,
1135 certs::ldapServiceName, id, certURL,
1136 "LDAP Certificate");
1137 });
1138} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001139/**
1140 * Collection of TrustStoreCertificate certificates
1141 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001142inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001143{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001144 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001145 .privileges(redfish::privileges::getCertificate)
George Liu0fda0f12021-11-16 10:06:17 +08001146 .methods(
1147 boost::beast::http::verb::
1148 get)([](const crow::Request&,
1149 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1150 asyncResp->res.jsonValue = {
1151 {"@odata.id",
1152 "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1153 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1154 {"Name", "TrustStore Certificates Collection"},
1155 {"Description",
1156 "A Collection of TrustStore certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001157
George Liu0fda0f12021-11-16 10:06:17 +08001158 crow::connections::systemBus->async_method_call(
1159 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001160 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001161 if (ec)
1162 {
1163 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1164 messages::internalError(asyncResp->res);
1165 return;
1166 }
1167 nlohmann::json& members =
1168 asyncResp->res.jsonValue["Members"];
1169 members = nlohmann::json::array();
1170 for (const auto& cert : certs)
1171 {
1172 long id = getIDFromURL(cert.first.str);
1173 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001174 {
George Liu0fda0f12021-11-16 10:06:17 +08001175 members.push_back(
1176 {{"@odata.id",
1177 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1178 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001179 }
George Liu0fda0f12021-11-16 10:06:17 +08001180 }
1181 asyncResp->res.jsonValue["Members@odata.count"] =
1182 members.size();
1183 },
1184 certs::authorityServiceName, certs::authorityObjectPath,
1185 certs::dbusObjManagerIntf, "GetManagedObjects");
1186 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001187
1188 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001189 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +08001190 .methods(
1191 boost::beast::http::verb::
1192 post)([](const crow::Request& req,
1193 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1194 std::string certFileBody =
1195 getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001196
George Liu0fda0f12021-11-16 10:06:17 +08001197 if (certFileBody.empty())
1198 {
1199 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1200 messages::unrecognizedRequestBody(asyncResp->res);
1201 return;
1202 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001203
George Liu0fda0f12021-11-16 10:06:17 +08001204 std::shared_ptr<CertificateFile> certFile =
1205 std::make_shared<CertificateFile>(certFileBody);
1206 crow::connections::systemBus->async_method_call(
1207 [asyncResp, certFile](const boost::system::error_code ec,
1208 const std::string& objectPath) {
1209 if (ec)
1210 {
1211 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1212 messages::internalError(asyncResp->res);
1213 return;
1214 }
1215 long certId = getIDFromURL(objectPath);
1216 if (certId < 0)
1217 {
1218 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1219 << objectPath;
1220 messages::internalError(asyncResp->res);
1221 return;
1222 }
1223 std::string certURL =
1224 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1225 std::to_string(certId);
zhanghch058d1b46d2021-04-01 11:18:24 +08001226
George Liu0fda0f12021-11-16 10:06:17 +08001227 getCertificateProperties(
1228 asyncResp, objectPath, certs::authorityServiceName,
1229 certId, certURL, "TrustStore Certificate");
1230 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1231 << certFile->getCertFilePath();
1232 },
1233 certs::authorityServiceName, certs::authorityObjectPath,
1234 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1235 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001236} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001237
1238/**
1239 * Certificate resource describes a certificate used to prove the identity
1240 * of a component, account or service.
1241 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001242inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001243{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001244 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001245 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001246 .methods(boost::beast::http::verb::get)(
1247 [](const crow::Request& req,
1248 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1249 const std::string&) {
1250 long id = getIDFromURL(req.url);
1251 if (id < 0)
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001252 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001253 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1254 messages::internalError(asyncResp->res);
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001255 return;
1256 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001257 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1258 << std::to_string(id);
1259 std::string certURL =
1260 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1261 std::to_string(id);
1262 std::string objectPath = certs::authorityObjectPath;
1263 objectPath += "/";
1264 objectPath += std::to_string(id);
1265 getCertificateProperties(asyncResp, objectPath,
1266 certs::authorityServiceName, id,
1267 certURL, "TrustStore Certificate");
1268 });
1269
1270 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001271 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001272 .methods(boost::beast::http::verb::delete_)(
1273 [](const crow::Request& req,
1274 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1275 const std::string& param) {
1276 if (param.empty())
1277 {
1278 messages::internalError(asyncResp->res);
1279 return;
1280 }
1281
1282 long id = getIDFromURL(req.url);
1283 if (id < 0)
1284 {
1285 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1286 messages::resourceNotFound(asyncResp->res,
1287 "TrustStore Certificate",
1288 std::string(req.url));
1289 return;
1290 }
1291 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1292 << std::to_string(id);
1293 std::string certPath = certs::authorityObjectPath;
1294 certPath += "/";
1295 certPath += std::to_string(id);
1296
1297 crow::connections::systemBus->async_method_call(
1298 [asyncResp, id](const boost::system::error_code ec) {
1299 if (ec)
1300 {
1301 messages::resourceNotFound(asyncResp->res,
1302 "TrustStore Certificate",
1303 std::to_string(id));
1304 return;
1305 }
1306 BMCWEB_LOG_INFO << "Certificate deleted";
1307 asyncResp->res.result(
1308 boost::beast::http::status::no_content);
1309 },
1310 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1311 "Delete");
1312 });
1313} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001314} // namespace redfish