blob: c2c873d6b967eee300ff92d84b41db0f6913c19b [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001#pragma once
2
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003#include <app.hpp>
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +02004#include <boost/convert.hpp>
5#include <boost/convert/strtol.hpp>
Jiaqing Zhao90d2d1e2022-04-13 17:01:57 +08006#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -08007#include <dbus_utility.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -07008#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -07009#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050010
Marri Devender Rao5968cae2019-01-21 10:27:12 -060011namespace redfish
12{
13namespace certs
14{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050015constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
16constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
17constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
18constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
19constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
20constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050021constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060022 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050023constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060024 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050025constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050026 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +080027constexpr char const* baseObjectPath = "/xyz/openbmc_project/certs";
28constexpr char const* httpsObjectPath =
29 "/xyz/openbmc_project/certs/server/https";
30constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050031constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050032 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060033} // namespace certs
34
35/**
36 * The Certificate schema defines a Certificate Service which represents the
37 * actions available to manage certificates and links to where certificates
38 * are installed.
39 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060040
John Edward Broadbent7e860f12021-04-08 15:57:16 -070041// TODO: Issue#61 No entries are available for Certificate
42// service at https://www.dmtf.org/standards/redfish
43// "redfish standard registries". Need to modify after DMTF
44// publish Privilege details for certificate service
45
46inline void requestRoutesCertificateService(App& app)
47{
48 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
Ed Tanoused398212021-06-09 17:05:54 -070049 .privileges(redfish::privileges::getCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -070050 .methods(boost::beast::http::verb::get)(
51 [&app](const crow::Request& req,
52 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +000053 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -070054 {
55 return;
56 }
Ed Tanous14766872022-03-15 10:44:42 -070057
Ed Tanous002d39b2022-05-31 08:59:27 -070058 asyncResp->res.jsonValue["@odata.type"] =
59 "#CertificateService.v1_0_0.CertificateService";
60 asyncResp->res.jsonValue["@odata.id"] =
61 "/redfish/v1/CertificateService";
62 asyncResp->res.jsonValue["Id"] = "CertificateService";
63 asyncResp->res.jsonValue["Name"] = "Certificate Service";
64 asyncResp->res.jsonValue["Description"] =
65 "Actions available to manage certificates";
66 // /redfish/v1/CertificateService/CertificateLocations is something
67 // only ConfigureManager can access then only display when the user
68 // has permissions ConfigureManager
69 Privileges effectiveUserPrivileges =
70 redfish::getUserPrivileges(req.userRole);
71 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
72 effectiveUserPrivileges))
73 {
74 asyncResp->res.jsonValue["CertificateLocations"]["@odata.id"] =
75 "/redfish/v1/CertificateService/CertificateLocations";
76 }
77 asyncResp->res
78 .jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = {
79 {"target",
80 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"},
81 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
82 asyncResp->res
83 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
84 {"target",
85 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR"}};
Abhishek Patel72048782021-06-02 09:53:24 -050086 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070087} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060088
Marri Devender Rao5968cae2019-01-21 10:27:12 -060089/**
90 * @brief Find the ID specified in the URL
91 * Finds the numbers specified after the last "/" in the URL and returns.
92 * @param[in] path URL
93 * @return -1 on failure and number on success
94 */
Ed Tanous23a21a12020-07-25 04:45:05 +000095inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060096{
Ed Tanousf23b7292020-10-15 09:41:17 -070097 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -060098 if (found == std::string::npos)
99 {
100 return -1;
101 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200102
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600103 if ((found + 1) < url.length())
104 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600105 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200106
107 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600108 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200109
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600110 return -1;
111}
112
zhanghch058d1b46d2021-04-01 11:18:24 +0800113inline std::string getCertificateFromReqBody(
114 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
115 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200116{
117 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
118
119 if (reqJson.is_discarded())
120 {
121 // We did not receive JSON request, proceed as it is RAW data
122 return req.body;
123 }
124
125 std::string certificate;
126 std::optional<std::string> certificateType = "PEM";
127
Willy Tu15ed6782021-12-14 11:03:16 -0800128 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
129 certificate, "CertificateType",
130 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200131 {
132 BMCWEB_LOG_ERROR << "Required parameters are missing";
133 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700134 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200135 }
136
137 if (*certificateType != "PEM")
138 {
139 messages::propertyValueNotInList(asyncResp->res, *certificateType,
140 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700141 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200142 }
143
144 return certificate;
145}
146
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600147/**
148 * Class to create a temporary certificate file for uploading to system
149 */
150class CertificateFile
151{
152 public:
153 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500154 CertificateFile(const CertificateFile&) = delete;
155 CertificateFile& operator=(const CertificateFile&) = delete;
156 CertificateFile(CertificateFile&&) = delete;
157 CertificateFile& operator=(CertificateFile&&) = delete;
Ed Tanous4e23a442022-06-06 09:57:26 -0700158 explicit CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600159 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700160 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700161 'e', 'r', 't', 's', '.', 'X',
162 'X', 'X', 'X', 'X', 'X', '\0'};
163 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800164 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600165 {
166 certDirectory = tempDirectory;
167 certificateFile = certDirectory / "cert.pem";
168 std::ofstream out(certificateFile, std::ofstream::out |
169 std::ofstream::binary |
170 std::ofstream::trunc);
171 out << certString;
172 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800173 BMCWEB_LOG_DEBUG << "Creating certificate file"
174 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600175 }
176 }
177 ~CertificateFile()
178 {
179 if (std::filesystem::exists(certDirectory))
180 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800181 BMCWEB_LOG_DEBUG << "Removing certificate file"
182 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000183 std::error_code ec;
184 std::filesystem::remove_all(certDirectory, ec);
185 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600186 {
187 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800188 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600189 }
190 }
191 }
192 std::string getCertFilePath()
193 {
194 return certificateFile;
195 }
196
197 private:
198 std::filesystem::path certificateFile;
199 std::filesystem::path certDirectory;
200};
201
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) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700224 if (ec)
225 {
226 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
227 messages::internalError(asyncResp->res);
228 return;
229 }
230 if (csr.empty())
231 {
232 BMCWEB_LOG_ERROR << "CSR read is empty";
233 messages::internalError(asyncResp->res);
234 return;
235 }
236 asyncResp->res.jsonValue["CSRString"] = csr;
237 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
238 certURI;
Marri Devender Rao30215812019-03-18 08:59:21 -0500239 },
240 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
241}
242
243/**
244 * Action to Generate CSR
245 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700246inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500247{
George Liu0fda0f12021-11-16 10:06:17 +0800248 BMCWEB_ROUTE(
249 app,
250 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
Abhishek Patel5344ab82021-07-31 17:42:09 -0500251 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700252 .methods(boost::beast::http::verb::post)(
253 [&app](const crow::Request& req,
254 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000255 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700256 {
257 return;
258 }
259 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500260
Ed Tanous002d39b2022-05-31 08:59:27 -0700261 // Required parameters
262 std::string city;
263 std::string commonName;
264 std::string country;
265 std::string organization;
266 std::string organizationalUnit;
267 std::string state;
268 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800269
Ed Tanous002d39b2022-05-31 08:59:27 -0700270 // Optional parameters
271 std::optional<std::vector<std::string>> optAlternativeNames =
272 std::vector<std::string>();
273 std::optional<std::string> optContactPerson = "";
274 std::optional<std::string> optChallengePassword = "";
275 std::optional<std::string> optEmail = "";
276 std::optional<std::string> optGivenName = "";
277 std::optional<std::string> optInitials = "";
278 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
279 std::optional<std::string> optKeyCurveId = "secp384r1";
280 std::optional<std::string> optKeyPairAlgorithm = "EC";
281 std::optional<std::vector<std::string>> optKeyUsage =
282 std::vector<std::string>();
283 std::optional<std::string> optSurname = "";
284 std::optional<std::string> optUnstructuredName = "";
285 if (!json_util::readJsonAction(
286 req, asyncResp->res, "City", city, "CommonName", commonName,
287 "ContactPerson", optContactPerson, "Country", country,
288 "Organization", organization, "OrganizationalUnit",
289 organizationalUnit, "State", state, "CertificateCollection",
290 certificateCollection, "AlternativeNames", optAlternativeNames,
291 "ChallengePassword", optChallengePassword, "Email", optEmail,
292 "GivenName", optGivenName, "Initials", optInitials,
293 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
294 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
295 optKeyUsage, "Surname", optSurname, "UnstructuredName",
296 optUnstructuredName))
297 {
298 return;
299 }
George Liu0fda0f12021-11-16 10:06:17 +0800300
Ed Tanous002d39b2022-05-31 08:59:27 -0700301 // bmcweb has no way to store or decode a private key challenge
302 // password, which will likely cause bmcweb to crash on startup
303 // if this is not set on a post so not allowing the user to set
304 // value
305 if (!optChallengePassword->empty())
306 {
307 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
308 "ChallengePassword");
309 return;
310 }
George Liu0fda0f12021-11-16 10:06:17 +0800311
Ed Tanous002d39b2022-05-31 08:59:27 -0700312 std::string certURI;
313 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
314 "@odata.id", certURI))
315 {
316 return;
317 }
George Liu0fda0f12021-11-16 10:06:17 +0800318
Ed Tanous002d39b2022-05-31 08:59:27 -0700319 std::string objectPath;
320 std::string service;
Ed Tanous11ba3972022-07-11 09:50:41 -0700321 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700322 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
323 {
324 objectPath = certs::httpsObjectPath;
325 service = certs::httpsServiceName;
326 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700327 else if (certURI.starts_with(
328 "/redfish/v1/AccountService/LDAP/Certificates"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700329 {
330 objectPath = certs::ldapObjectPath;
331 service = certs::ldapServiceName;
332 }
333 else
334 {
335 messages::actionParameterNotSupported(
336 asyncResp->res, "CertificateCollection", "GenerateCSR");
337 return;
338 }
339
340 // supporting only EC and RSA algorithm
341 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
342 {
343 messages::actionParameterNotSupported(
344 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
345 return;
346 }
347
348 // supporting only 2048 key bit length for RSA algorithm due to
349 // time consumed in generating private key
350 if (*optKeyPairAlgorithm == "RSA" &&
351 *optKeyBitLength != rsaKeyBitLength)
352 {
353 messages::propertyValueNotInList(asyncResp->res,
354 std::to_string(*optKeyBitLength),
355 "KeyBitLength");
356 return;
357 }
358
359 // validate KeyUsage supporting only 1 type based on URL
Ed Tanous11ba3972022-07-11 09:50:41 -0700360 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700361 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
362 {
363 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800364 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700365 optKeyUsage->push_back("ServerAuthentication");
George Liu0fda0f12021-11-16 10:06:17 +0800366 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700367 else if (optKeyUsage->size() == 1)
George Liu0fda0f12021-11-16 10:06:17 +0800368 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700369 if ((*optKeyUsage)[0] != "ServerAuthentication")
370 {
371 messages::propertyValueNotInList(
372 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
373 return;
374 }
George Liu0fda0f12021-11-16 10:06:17 +0800375 }
376 else
377 {
378 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700379 asyncResp->res, "KeyUsage", "GenerateCSR");
George Liu0fda0f12021-11-16 10:06:17 +0800380 return;
381 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700382 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700383 else if (certURI.starts_with(
384 "/redfish/v1/AccountService/LDAP/Certificates"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700385 {
386 if (optKeyUsage->empty())
387 {
388 optKeyUsage->push_back("ClientAuthentication");
389 }
390 else if (optKeyUsage->size() == 1)
391 {
392 if ((*optKeyUsage)[0] != "ClientAuthentication")
393 {
394 messages::propertyValueNotInList(
395 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
396 return;
397 }
398 }
399 else
George Liu0fda0f12021-11-16 10:06:17 +0800400 {
401 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700402 asyncResp->res, "KeyUsage", "GenerateCSR");
403 return;
404 }
405 }
406
407 // Only allow one CSR matcher at a time so setting retry
408 // time-out and timer expiry to 10 seconds for now.
409 static const int timeOut = 10;
410 if (csrMatcher)
411 {
412 messages::serviceTemporarilyUnavailable(asyncResp->res,
413 std::to_string(timeOut));
414 return;
415 }
416
417 // Make this static so it survives outside this method
418 static boost::asio::steady_timer timeout(*req.ioService);
419 timeout.expires_after(std::chrono::seconds(timeOut));
420 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
421 csrMatcher = nullptr;
422 if (ec)
423 {
424 // operation_aborted is expected if timer is canceled
425 // before completion.
426 if (ec != boost::asio::error::operation_aborted)
427 {
428 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
429 }
430 return;
431 }
432 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
433 messages::internalError(asyncResp->res);
434 });
435
436 // create a matcher to wait on CSR object
437 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
438 std::string match("type='signal',"
439 "interface='org.freedesktop.DBus.ObjectManager',"
440 "path='" +
441 objectPath +
442 "',"
443 "member='InterfacesAdded'");
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);
George Liu0fda0f12021-11-16 10:06:17 +0800453 return;
454 }
455
Ed Tanous002d39b2022-05-31 08:59:27 -0700456 dbus::utility::DBusInteracesMap interfacesProperties;
457
458 sdbusplus::message::object_path csrObjectPath;
459 m.read(csrObjectPath, interfacesProperties);
460 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
Ed Tanous02cad962022-06-30 16:50:15 -0700461 for (const auto& interface : interfacesProperties)
George Liu0fda0f12021-11-16 10:06:17 +0800462 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700463 if (interface.first == "xyz.openbmc_project.Certs.CSR")
464 {
465 getCSR(asyncResp, certURI, service, objectPath,
466 csrObjectPath.str);
467 break;
468 }
469 }
470 });
471 crow::connections::systemBus->async_method_call(
472 [asyncResp](const boost::system::error_code ec,
473 const std::string&) {
474 if (ec)
475 {
476 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
477 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800478 return;
479 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700480 },
481 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
482 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
483 commonName, *optContactPerson, country, *optEmail, *optGivenName,
484 *optInitials, *optKeyBitLength, *optKeyCurveId,
485 *optKeyPairAlgorithm, *optKeyUsage, organization,
486 organizationalUnit, state, *optSurname, *optUnstructuredName);
George Liu0fda0f12021-11-16 10:06:17 +0800487 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700488} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500489
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600490/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500491 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600492 *
493 * @param[in] asyncResp Shared pointer to the response message
494 * @param[in] str Issuer/Subject value in key=value pairs
495 * @param[in] type Issuer/Subject
496 * @return None
497 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500498static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600499 const std::string_view value)
500{
501 // example: O=openbmc-project.xyz,CN=localhost
502 std::string_view::iterator i = value.begin();
503 while (i != value.end())
504 {
505 std::string_view::iterator tokenBegin = i;
506 while (i != value.end() && *i != '=')
507 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530508 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600509 }
510 if (i == value.end())
511 {
512 break;
513 }
Ed Tanous271584a2019-07-09 16:24:22 -0700514 const std::string_view key(tokenBegin,
515 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530516 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600517 tokenBegin = i;
518 while (i != value.end() && *i != ',')
519 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530520 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600521 }
Ed Tanous271584a2019-07-09 16:24:22 -0700522 const std::string_view val(tokenBegin,
523 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600524 if (key == "L")
525 {
526 out["City"] = val;
527 }
528 else if (key == "CN")
529 {
530 out["CommonName"] = val;
531 }
532 else if (key == "C")
533 {
534 out["Country"] = val;
535 }
536 else if (key == "O")
537 {
538 out["Organization"] = val;
539 }
540 else if (key == "OU")
541 {
542 out["OrganizationalUnit"] = val;
543 }
544 else if (key == "ST")
545 {
546 out["State"] = val;
547 }
548 // skip comma character
549 if (i != value.end())
550 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530551 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600552 }
553 }
554}
555
556/**
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800557 * @brief Retrieve the installed certificate list
558 *
559 * @param[in] asyncResp Shared pointer to the response message
560 * @param[in] basePath DBus object path to search
561 * @param[in] listPtr Json pointer to the list in asyncResp
562 * @param[in] countPtr Json pointer to the count in asyncResp
563 * @return None
564 */
565static void
566 getCertificateList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
567 const std::string& basePath,
568 const nlohmann::json::json_pointer& listPtr,
569 const nlohmann::json::json_pointer& countPtr)
570{
571 crow::connections::systemBus->async_method_call(
572 [asyncResp, listPtr, countPtr](
573 const boost::system::error_code ec,
574 const dbus::utility::MapperGetSubTreePathsResponse& certPaths) {
575 if (ec)
576 {
577 BMCWEB_LOG_ERROR << "Certificate collection query failed: " << ec;
578 messages::internalError(asyncResp->res);
579 return;
580 }
581
582 nlohmann::json& links = asyncResp->res.jsonValue[listPtr];
583 links = nlohmann::json::array();
584 for (const auto& certPath : certPaths)
585 {
586 sdbusplus::message::object_path objPath(certPath);
587 std::string certId = objPath.filename();
588 if (certId.empty())
589 {
590 BMCWEB_LOG_ERROR << "Invalid certificate objPath " << certPath;
591 continue;
592 }
593
594 boost::urls::url certURL;
595 if (objPath.parent_path() == certs::httpsObjectPath)
596 {
597 certURL = crow::utility::urlFromPieces(
598 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
599 "HTTPS", "Certificates", certId);
600 }
601 else if (objPath.parent_path() == certs::ldapObjectPath)
602 {
603 certURL = crow::utility::urlFromPieces("redfish", "v1",
604 "AccountService", "LDAP",
605 "Certificates", certId);
606 }
607 else if (objPath.parent_path() == certs::authorityObjectPath)
608 {
609 certURL = crow::utility::urlFromPieces(
610 "redfish", "v1", "Managers", "bmc", "Truststore",
611 "Certificates", certId);
612 }
613 else
614 {
615 continue;
616 }
617
618 nlohmann::json::object_t link;
619 link["@odata.id"] = certURL;
620 links.emplace_back(std::move(link));
621 }
622
623 asyncResp->res.jsonValue[countPtr] = links.size();
624 },
625 "xyz.openbmc_project.ObjectMapper",
626 "/xyz/openbmc_project/object_mapper",
627 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", basePath, 0,
628 std::array<const char*, 1>{certs::certPropIntf});
629}
630
631/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600632 * @brief Retrieve the certificates properties and append to the response
633 * message
634 *
635 * @param[in] asyncResp Shared pointer to the response message
636 * @param[in] objectPath Path of the D-Bus service object
637 * @param[in] certId Id of the certificate
638 * @param[in] certURL URL of the certificate object
639 * @param[in] name name of the certificate
640 * @return None
641 */
642static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800643 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
644 const std::string& objectPath, const std::string& service, long certId,
645 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600646{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600647 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
648 << " certId=" << certId << " certURl=" << certURL;
649 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800650 [asyncResp, certURL, certId,
651 name](const boost::system::error_code ec,
652 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700653 if (ec)
654 {
655 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
656 messages::resourceNotFound(asyncResp->res, name,
657 std::to_string(certId));
658 return;
659 }
660 asyncResp->res.jsonValue["@odata.id"] = certURL;
661 asyncResp->res.jsonValue["@odata.type"] =
662 "#Certificate.v1_0_0.Certificate";
663 asyncResp->res.jsonValue["Id"] = std::to_string(certId);
664 asyncResp->res.jsonValue["Name"] = name;
665 asyncResp->res.jsonValue["Description"] = name;
666 for (const auto& property : properties)
667 {
668 if (property.first == "CertificateString")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600669 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700670 asyncResp->res.jsonValue["CertificateString"] = "";
671 const std::string* value =
672 std::get_if<std::string>(&property.second);
673 if (value != nullptr)
674 {
675 asyncResp->res.jsonValue["CertificateString"] = *value;
676 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600677 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700678 else if (property.first == "KeyUsage")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600679 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700680 nlohmann::json& keyUsage = asyncResp->res.jsonValue["KeyUsage"];
681 keyUsage = nlohmann::json::array();
682 const std::vector<std::string>* value =
683 std::get_if<std::vector<std::string>>(&property.second);
684 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600685 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700686 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600687 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700688 keyUsage.push_back(usage);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600689 }
690 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600691 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700692 else if (property.first == "Issuer")
693 {
694 const std::string* value =
695 std::get_if<std::string>(&property.second);
696 if (value != nullptr)
697 {
698 updateCertIssuerOrSubject(
699 asyncResp->res.jsonValue["Issuer"], *value);
700 }
701 }
702 else if (property.first == "Subject")
703 {
704 const std::string* value =
705 std::get_if<std::string>(&property.second);
706 if (value != nullptr)
707 {
708 updateCertIssuerOrSubject(
709 asyncResp->res.jsonValue["Subject"], *value);
710 }
711 }
712 else if (property.first == "ValidNotAfter")
713 {
714 const uint64_t* value = std::get_if<uint64_t>(&property.second);
715 if (value != nullptr)
716 {
717 asyncResp->res.jsonValue["ValidNotAfter"] =
718 crow::utility::getDateTimeUint(*value);
719 }
720 }
721 else if (property.first == "ValidNotBefore")
722 {
723 const uint64_t* value = std::get_if<uint64_t>(&property.second);
724 if (value != nullptr)
725 {
726 asyncResp->res.jsonValue["ValidNotBefore"] =
727 crow::utility::getDateTimeUint(*value);
728 }
729 }
730 }
731 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600732 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600733 service, objectPath, certs::dbusPropIntf, "GetAll",
734 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600735}
736
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600737/**
738 * Action to replace an existing certificate
739 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700740inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600741{
George Liu0fda0f12021-11-16 10:06:17 +0800742 BMCWEB_ROUTE(
743 app,
744 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700745 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700746 .methods(boost::beast::http::verb::post)(
747 [&app](const crow::Request& req,
748 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000749 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700750 {
751 return;
752 }
753 std::string certificate;
754 nlohmann::json certificateUri;
755 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600756
Ed Tanous002d39b2022-05-31 08:59:27 -0700757 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
758 certificate, "CertificateUri",
759 certificateUri, "CertificateType",
760 certificateType))
761 {
762 BMCWEB_LOG_ERROR << "Required parameters are missing";
763 messages::internalError(asyncResp->res);
764 return;
765 }
766
767 if (!certificateType)
768 {
769 // should never happen, but it never hurts to be paranoid.
770 return;
771 }
772 if (certificateType != "PEM")
773 {
774 messages::actionParameterNotSupported(
775 asyncResp->res, "CertificateType", "ReplaceCertificate");
776 return;
777 }
778
779 std::string certURI;
780 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
781 "@odata.id", certURI))
782 {
783 messages::actionParameterMissing(
784 asyncResp->res, "ReplaceCertificate", "CertificateUri");
785 return;
786 }
787
788 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
789 long id = getIDFromURL(certURI);
790 if (id < 0)
791 {
792 messages::actionParameterValueFormatError(asyncResp->res, certURI,
793 "CertificateUri",
794 "ReplaceCertificate");
795 return;
796 }
797 std::string objectPath;
798 std::string name;
799 std::string service;
Ed Tanous11ba3972022-07-11 09:50:41 -0700800 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700801 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
802 {
803 objectPath =
804 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
805 name = "HTTPS certificate";
806 service = certs::httpsServiceName;
807 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700808 else if (certURI.starts_with(
809 "/redfish/v1/AccountService/LDAP/Certificates/"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700810 {
811 objectPath =
812 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
813 name = "LDAP certificate";
814 service = certs::ldapServiceName;
815 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700816 else if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700817 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
818 {
819 objectPath = std::string(certs::authorityObjectPath) + "/" +
820 std::to_string(id);
821 name = "TrustStore certificate";
822 service = certs::authorityServiceName;
823 }
824 else
825 {
826 messages::actionParameterNotSupported(
827 asyncResp->res, "CertificateUri", "ReplaceCertificate");
828 return;
829 }
830
831 std::shared_ptr<CertificateFile> certFile =
832 std::make_shared<CertificateFile>(certificate);
833 crow::connections::systemBus->async_method_call(
834 [asyncResp, certFile, objectPath, service, certURI, id,
835 name](const boost::system::error_code ec) {
836 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700837 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700838 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
839 if (ec.value() ==
840 boost::system::linux_error::bad_request_descriptor)
841 {
842 messages::resourceNotFound(asyncResp->res, name,
843 std::to_string(id));
844 return;
845 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700846 messages::internalError(asyncResp->res);
847 return;
848 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700849 getCertificateProperties(asyncResp, objectPath, service, id,
850 certURI, name);
851 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
852 << certFile->getCertFilePath();
853 },
854 service, objectPath, certs::certReplaceIntf, "Replace",
855 certFile->getCertFilePath());
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700856 });
857} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600858
859/**
860 * Certificate resource describes a certificate used to prove the identity
861 * of a component, account or service.
862 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700863
864inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600865{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700866 BMCWEB_ROUTE(
867 app,
868 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700869 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700870 .methods(
871 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700872 get)([&app](const crow::Request& req,
873 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
874 const std::string& param) -> void {
Carson Labrado3ba00072022-06-06 19:40:56 +0000875 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous45ca1b82022-03-25 13:07:27 -0700876 {
877 return;
878 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700879 if (param.empty())
880 {
881 messages::internalError(asyncResp->res);
882 return;
883 }
884 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600885
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700886 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
887 << std::to_string(id);
888 std::string certURL =
889 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
890 std::to_string(id);
891 std::string objectPath = certs::httpsObjectPath;
892 objectPath += "/";
893 objectPath += std::to_string(id);
894 getCertificateProperties(asyncResp, objectPath,
895 certs::httpsServiceName, id, certURL,
896 "HTTPS Certificate");
897 });
898}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600899
900/**
901 * Collection of HTTPS certificates
902 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700903inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600904{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700905 BMCWEB_ROUTE(app,
906 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700907 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700908 .methods(boost::beast::http::verb::get)(
909 [&app](const crow::Request& req,
910 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000911 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700912 {
913 return;
914 }
915
916 asyncResp->res.jsonValue["@odata.id"] =
917 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
918 asyncResp->res.jsonValue["@odata.type"] =
919 "#CertificateCollection.CertificateCollection";
920 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
921 asyncResp->res.jsonValue["Description"] =
922 "A Collection of HTTPS certificate instances";
923
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800924 getCertificateList(asyncResp, certs::httpsObjectPath,
925 "/Members"_json_pointer,
926 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700927 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600928
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700929 BMCWEB_ROUTE(app,
930 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700931 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700932 .methods(boost::beast::http::verb::post)(
933 [&app](const crow::Request& req,
934 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000935 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700936 {
937 return;
938 }
939 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
940
941 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
942 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
943
944 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
945
946 if (certFileBody.empty())
947 {
948 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
949 messages::unrecognizedRequestBody(asyncResp->res);
950 return;
951 }
952
953 std::shared_ptr<CertificateFile> certFile =
954 std::make_shared<CertificateFile>(certFileBody);
955
956 crow::connections::systemBus->async_method_call(
957 [asyncResp, certFile](const boost::system::error_code ec,
958 const std::string& objectPath) {
959 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700960 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700961 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
962 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700963 return;
964 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700965 long certId = getIDFromURL(objectPath);
966 if (certId < 0)
George Liu0fda0f12021-11-16 10:06:17 +0800967 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700968 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
969 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800970 return;
971 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700972 std::string certURL =
973 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
974 std::to_string(certId);
975 getCertificateProperties(asyncResp, objectPath,
976 certs::httpsServiceName, certId, certURL,
977 "HTTPS Certificate");
978 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
979 << certFile->getCertFilePath();
980 },
981 certs::httpsServiceName, certs::httpsObjectPath,
982 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +0800983 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700984} // requestRoutesHTTPSCertificateCollection
985
986/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600987 * The certificate location schema defines a resource that an administrator
988 * can use in order to locate all certificates installed on a given service.
989 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700990inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600991{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700992 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700993 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous002d39b2022-05-31 08:59:27 -0700994 .methods(boost::beast::http::verb::get)(
995 [&app](const crow::Request& req,
996 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000997 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700998 {
999 return;
1000 }
1001 asyncResp->res.jsonValue["@odata.id"] =
1002 "/redfish/v1/CertificateService/CertificateLocations";
1003 asyncResp->res.jsonValue["@odata.type"] =
1004 "#CertificateLocations.v1_0_0.CertificateLocations";
1005 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
1006 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
1007 asyncResp->res.jsonValue["Description"] =
1008 "Defines a resource that an administrator can use in order to "
1009 "locate all certificates installed on a given service";
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001010
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001011 getCertificateList(asyncResp, certs::baseObjectPath,
1012 "/Links/Certificates"_json_pointer,
1013 "/Links/Certificates@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001014 });
1015}
1016// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001017
1018/**
1019 * Collection of LDAP certificates
1020 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001021inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001022{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001023 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001024 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001025 .methods(boost::beast::http::verb::get)(
1026 [&app](const crow::Request& req,
1027 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001028 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001029 {
1030 return;
1031 }
1032
1033 asyncResp->res.jsonValue["@odata.id"] =
1034 "/redfish/v1/AccountService/LDAP/Certificates";
1035 asyncResp->res.jsonValue["@odata.type"] =
1036 "#CertificateCollection.CertificateCollection";
1037 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1038 asyncResp->res.jsonValue["Description"] =
1039 "A Collection of LDAP certificate instances";
1040
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001041 getCertificateList(asyncResp, certs::ldapObjectPath,
1042 "/Members"_json_pointer,
1043 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001044 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001045
1046 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001047 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001048 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001049 [&app](const crow::Request& req,
1050 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001051 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001052 {
1053 return;
1054 }
1055 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001056
Ed Tanous002d39b2022-05-31 08:59:27 -07001057 if (certFileBody.empty())
1058 {
1059 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1060 messages::unrecognizedRequestBody(asyncResp->res);
1061 return;
1062 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001063
Ed Tanous002d39b2022-05-31 08:59:27 -07001064 std::shared_ptr<CertificateFile> certFile =
1065 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001066
Ed Tanous002d39b2022-05-31 08:59:27 -07001067 crow::connections::systemBus->async_method_call(
1068 [asyncResp, certFile](const boost::system::error_code ec,
1069 const std::string& objectPath) {
1070 if (ec)
1071 {
1072 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1073 messages::internalError(asyncResp->res);
1074 return;
1075 }
1076 long certId = getIDFromURL(objectPath);
1077 if (certId < 0)
1078 {
1079 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
1080 messages::internalError(asyncResp->res);
1081 return;
1082 }
1083 std::string certURL =
1084 "/redfish/v1/AccountService/LDAP/Certificates/" +
1085 std::to_string(certId);
1086 getCertificateProperties(asyncResp, objectPath,
1087 certs::ldapServiceName, certId, certURL,
1088 "LDAP Certificate");
1089 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1090 << certFile->getCertFilePath();
1091 },
1092 certs::ldapServiceName, certs::ldapObjectPath,
1093 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1094 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001095} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001096
1097/**
1098 * Certificate resource describes a certificate used to prove the identity
1099 * of a component, account or service.
1100 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001101inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001102{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001103 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001104 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001105 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001106 [&app](const crow::Request& req,
1107 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1108 const std::string&) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001109 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001110 {
1111 return;
1112 }
1113 long id = getIDFromURL(req.url);
1114 if (id < 0)
1115 {
1116 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1117 messages::internalError(asyncResp->res);
1118 return;
1119 }
1120 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1121 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1122 std::to_string(id);
1123 std::string objectPath = certs::ldapObjectPath;
1124 objectPath += "/";
1125 objectPath += std::to_string(id);
1126 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1127 id, certURL, "LDAP Certificate");
1128 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001129} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001130/**
1131 * Collection of TrustStoreCertificate certificates
1132 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001133inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001134{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001135 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001136 .privileges(redfish::privileges::getCertificate)
Ed Tanous002d39b2022-05-31 08:59:27 -07001137 .methods(boost::beast::http::verb::get)(
1138 [&app](const crow::Request& req,
1139 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001140 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001141 {
1142 return;
1143 }
1144
1145 asyncResp->res.jsonValue["@odata.id"] =
1146 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1147 asyncResp->res.jsonValue["@odata.type"] =
1148 "#CertificateCollection.CertificateCollection";
1149 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1150 asyncResp->res.jsonValue["Description"] =
1151 "A Collection of TrustStore certificate instances";
1152
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001153 getCertificateList(asyncResp, certs::authorityObjectPath,
1154 "/Members"_json_pointer,
1155 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001156 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001157
1158 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001159 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001160 .methods(boost::beast::http::verb::post)(
1161 [&app](const crow::Request& req,
1162 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001163 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001164 {
1165 return;
1166 }
1167 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1168
1169 if (certFileBody.empty())
1170 {
1171 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1172 messages::unrecognizedRequestBody(asyncResp->res);
1173 return;
1174 }
1175
1176 std::shared_ptr<CertificateFile> certFile =
1177 std::make_shared<CertificateFile>(certFileBody);
1178 crow::connections::systemBus->async_method_call(
1179 [asyncResp, certFile](const boost::system::error_code ec,
1180 const std::string& objectPath) {
1181 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001182 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001183 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1184 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001185 return;
1186 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001187 long certId = getIDFromURL(objectPath);
1188 if (certId < 0)
George Liu0fda0f12021-11-16 10:06:17 +08001189 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001190 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
1191 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +08001192 return;
1193 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001194 std::string certURL =
1195 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1196 std::to_string(certId);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001197
Ed Tanous002d39b2022-05-31 08:59:27 -07001198 getCertificateProperties(asyncResp, objectPath,
1199 certs::authorityServiceName, certId,
1200 certURL, "TrustStore Certificate");
1201 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1202 << certFile->getCertFilePath();
1203 },
1204 certs::authorityServiceName, certs::authorityObjectPath,
1205 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +08001206 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001207} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001208
1209/**
1210 * Certificate resource describes a certificate used to prove the identity
1211 * of a component, account or service.
1212 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001213inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001214{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001215 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001216 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001217 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001218 [&app](const crow::Request& req,
1219 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1220 const std::string&) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001221 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001222 {
1223 return;
1224 }
1225 long id = getIDFromURL(req.url);
1226 if (id < 0)
1227 {
1228 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1229 messages::internalError(asyncResp->res);
1230 return;
1231 }
1232 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1233 << std::to_string(id);
1234 std::string certURL =
1235 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1236 std::to_string(id);
1237 std::string objectPath = certs::authorityObjectPath;
1238 objectPath += "/";
1239 objectPath += std::to_string(id);
1240 getCertificateProperties(asyncResp, objectPath,
1241 certs::authorityServiceName, id, certURL,
1242 "TrustStore Certificate");
1243 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001244
1245 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001246 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001247 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001248 [&app](const crow::Request& req,
1249 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1250 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001251 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001252 {
1253 return;
1254 }
1255 if (param.empty())
1256 {
1257 messages::internalError(asyncResp->res);
1258 return;
1259 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001260
Ed Tanous002d39b2022-05-31 08:59:27 -07001261 long id = getIDFromURL(req.url);
1262 if (id < 0)
1263 {
1264 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1265 messages::resourceNotFound(asyncResp->res, "TrustStore Certificate",
1266 std::string(req.url));
1267 return;
1268 }
1269 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1270 << std::to_string(id);
1271 std::string certPath = certs::authorityObjectPath;
1272 certPath += "/";
1273 certPath += std::to_string(id);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001274
Ed Tanous002d39b2022-05-31 08:59:27 -07001275 crow::connections::systemBus->async_method_call(
1276 [asyncResp, id](const boost::system::error_code ec) {
1277 if (ec)
1278 {
1279 messages::resourceNotFound(asyncResp->res,
1280 "TrustStore Certificate",
1281 std::to_string(id));
1282 return;
1283 }
1284 BMCWEB_LOG_INFO << "Certificate deleted";
1285 asyncResp->res.result(boost::beast::http::status::no_content);
1286 },
1287 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1288 "Delete");
1289 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001290} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001291} // namespace redfish