blob: fc347dcc3a5f5be585e64edb68124b7b5c1e435f [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>
Ed Tanousd9f6c622022-03-17 09:12:17 -07004#include <async_resp.hpp>
Jiaqing Zhao90d2d1e2022-04-13 17:01:57 +08005#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -08006#include <dbus_utility.hpp>
Ed Tanousd9f6c622022-03-17 09:12:17 -07007#include <http_response.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
zhanghch058d1b46d2021-04-01 11:18:24 +080089inline std::string getCertificateFromReqBody(
90 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
91 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020092{
93 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
94
95 if (reqJson.is_discarded())
96 {
97 // We did not receive JSON request, proceed as it is RAW data
98 return req.body;
99 }
100
101 std::string certificate;
102 std::optional<std::string> certificateType = "PEM";
103
Willy Tu15ed6782021-12-14 11:03:16 -0800104 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
105 certificate, "CertificateType",
106 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200107 {
108 BMCWEB_LOG_ERROR << "Required parameters are missing";
109 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700110 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200111 }
112
113 if (*certificateType != "PEM")
114 {
115 messages::propertyValueNotInList(asyncResp->res, *certificateType,
116 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700117 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200118 }
119
120 return certificate;
121}
122
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600123/**
124 * Class to create a temporary certificate file for uploading to system
125 */
126class CertificateFile
127{
128 public:
129 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500130 CertificateFile(const CertificateFile&) = delete;
131 CertificateFile& operator=(const CertificateFile&) = delete;
132 CertificateFile(CertificateFile&&) = delete;
133 CertificateFile& operator=(CertificateFile&&) = delete;
Ed Tanous4e23a442022-06-06 09:57:26 -0700134 explicit CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600135 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700136 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700137 'e', 'r', 't', 's', '.', 'X',
138 'X', 'X', 'X', 'X', 'X', '\0'};
139 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800140 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600141 {
142 certDirectory = tempDirectory;
143 certificateFile = certDirectory / "cert.pem";
144 std::ofstream out(certificateFile, std::ofstream::out |
145 std::ofstream::binary |
146 std::ofstream::trunc);
147 out << certString;
148 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800149 BMCWEB_LOG_DEBUG << "Creating certificate file"
150 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600151 }
152 }
153 ~CertificateFile()
154 {
155 if (std::filesystem::exists(certDirectory))
156 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800157 BMCWEB_LOG_DEBUG << "Removing certificate file"
158 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000159 std::error_code ec;
160 std::filesystem::remove_all(certDirectory, ec);
161 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600162 {
163 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800164 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600165 }
166 }
167 }
168 std::string getCertFilePath()
169 {
170 return certificateFile;
171 }
172
173 private:
174 std::filesystem::path certificateFile;
175 std::filesystem::path certDirectory;
176};
177
Patrick Williams59d494e2022-07-22 19:26:55 -0500178static std::unique_ptr<sdbusplus::bus::match_t> csrMatcher;
Marri Devender Rao30215812019-03-18 08:59:21 -0500179/**
180 * @brief Read data from CSR D-bus object and set to response
181 *
182 * @param[in] asyncResp Shared pointer to the response message
183 * @param[in] certURI Link to certifiate collection URI
184 * @param[in] service D-Bus service name
185 * @param[in] certObjPath certificate D-Bus object path
186 * @param[in] csrObjPath CSR D-Bus object path
187 * @return None
188 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800189static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500190 const std::string& certURI, const std::string& service,
191 const std::string& certObjPath,
192 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500193{
194 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
195 << " CSRObjectPath=" << csrObjPath
196 << " service=" << service;
197 crow::connections::systemBus->async_method_call(
198 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500199 const std::string& csr) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700200 if (ec)
201 {
202 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
203 messages::internalError(asyncResp->res);
204 return;
205 }
206 if (csr.empty())
207 {
208 BMCWEB_LOG_ERROR << "CSR read is empty";
209 messages::internalError(asyncResp->res);
210 return;
211 }
212 asyncResp->res.jsonValue["CSRString"] = csr;
213 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
214 certURI;
Marri Devender Rao30215812019-03-18 08:59:21 -0500215 },
216 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
217}
218
219/**
220 * Action to Generate CSR
221 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700222inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500223{
George Liu0fda0f12021-11-16 10:06:17 +0800224 BMCWEB_ROUTE(
225 app,
226 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
Abhishek Patel5344ab82021-07-31 17:42:09 -0500227 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700228 .methods(boost::beast::http::verb::post)(
229 [&app](const crow::Request& req,
230 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000231 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700232 {
233 return;
234 }
235 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500236
Ed Tanous002d39b2022-05-31 08:59:27 -0700237 // Required parameters
238 std::string city;
239 std::string commonName;
240 std::string country;
241 std::string organization;
242 std::string organizationalUnit;
243 std::string state;
244 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800245
Ed Tanous002d39b2022-05-31 08:59:27 -0700246 // Optional parameters
247 std::optional<std::vector<std::string>> optAlternativeNames =
248 std::vector<std::string>();
249 std::optional<std::string> optContactPerson = "";
250 std::optional<std::string> optChallengePassword = "";
251 std::optional<std::string> optEmail = "";
252 std::optional<std::string> optGivenName = "";
253 std::optional<std::string> optInitials = "";
254 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
255 std::optional<std::string> optKeyCurveId = "secp384r1";
256 std::optional<std::string> optKeyPairAlgorithm = "EC";
257 std::optional<std::vector<std::string>> optKeyUsage =
258 std::vector<std::string>();
259 std::optional<std::string> optSurname = "";
260 std::optional<std::string> optUnstructuredName = "";
261 if (!json_util::readJsonAction(
262 req, asyncResp->res, "City", city, "CommonName", commonName,
263 "ContactPerson", optContactPerson, "Country", country,
264 "Organization", organization, "OrganizationalUnit",
265 organizationalUnit, "State", state, "CertificateCollection",
266 certificateCollection, "AlternativeNames", optAlternativeNames,
267 "ChallengePassword", optChallengePassword, "Email", optEmail,
268 "GivenName", optGivenName, "Initials", optInitials,
269 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
270 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
271 optKeyUsage, "Surname", optSurname, "UnstructuredName",
272 optUnstructuredName))
273 {
274 return;
275 }
George Liu0fda0f12021-11-16 10:06:17 +0800276
Ed Tanous002d39b2022-05-31 08:59:27 -0700277 // bmcweb has no way to store or decode a private key challenge
278 // password, which will likely cause bmcweb to crash on startup
279 // if this is not set on a post so not allowing the user to set
280 // value
281 if (!optChallengePassword->empty())
282 {
283 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
284 "ChallengePassword");
285 return;
286 }
George Liu0fda0f12021-11-16 10:06:17 +0800287
Ed Tanous002d39b2022-05-31 08:59:27 -0700288 std::string certURI;
289 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
290 "@odata.id", certURI))
291 {
292 return;
293 }
George Liu0fda0f12021-11-16 10:06:17 +0800294
Ed Tanous002d39b2022-05-31 08:59:27 -0700295 std::string objectPath;
296 std::string service;
Ed Tanous11ba3972022-07-11 09:50:41 -0700297 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700298 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
299 {
300 objectPath = certs::httpsObjectPath;
301 service = certs::httpsServiceName;
302 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700303 else if (certURI.starts_with(
304 "/redfish/v1/AccountService/LDAP/Certificates"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700305 {
306 objectPath = certs::ldapObjectPath;
307 service = certs::ldapServiceName;
308 }
309 else
310 {
311 messages::actionParameterNotSupported(
312 asyncResp->res, "CertificateCollection", "GenerateCSR");
313 return;
314 }
315
316 // supporting only EC and RSA algorithm
317 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
318 {
319 messages::actionParameterNotSupported(
320 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
321 return;
322 }
323
324 // supporting only 2048 key bit length for RSA algorithm due to
325 // time consumed in generating private key
326 if (*optKeyPairAlgorithm == "RSA" &&
327 *optKeyBitLength != rsaKeyBitLength)
328 {
329 messages::propertyValueNotInList(asyncResp->res,
330 std::to_string(*optKeyBitLength),
331 "KeyBitLength");
332 return;
333 }
334
335 // validate KeyUsage supporting only 1 type based on URL
Ed Tanous11ba3972022-07-11 09:50:41 -0700336 if (certURI.starts_with(
Ed Tanous002d39b2022-05-31 08:59:27 -0700337 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
338 {
339 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800340 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700341 optKeyUsage->push_back("ServerAuthentication");
George Liu0fda0f12021-11-16 10:06:17 +0800342 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700343 else if (optKeyUsage->size() == 1)
George Liu0fda0f12021-11-16 10:06:17 +0800344 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700345 if ((*optKeyUsage)[0] != "ServerAuthentication")
346 {
347 messages::propertyValueNotInList(
348 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
349 return;
350 }
George Liu0fda0f12021-11-16 10:06:17 +0800351 }
352 else
353 {
354 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700355 asyncResp->res, "KeyUsage", "GenerateCSR");
George Liu0fda0f12021-11-16 10:06:17 +0800356 return;
357 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700358 }
Ed Tanous11ba3972022-07-11 09:50:41 -0700359 else if (certURI.starts_with(
360 "/redfish/v1/AccountService/LDAP/Certificates"))
Ed Tanous002d39b2022-05-31 08:59:27 -0700361 {
362 if (optKeyUsage->empty())
363 {
364 optKeyUsage->push_back("ClientAuthentication");
365 }
366 else if (optKeyUsage->size() == 1)
367 {
368 if ((*optKeyUsage)[0] != "ClientAuthentication")
369 {
370 messages::propertyValueNotInList(
371 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
372 return;
373 }
374 }
375 else
George Liu0fda0f12021-11-16 10:06:17 +0800376 {
377 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700378 asyncResp->res, "KeyUsage", "GenerateCSR");
379 return;
380 }
381 }
382
383 // Only allow one CSR matcher at a time so setting retry
384 // time-out and timer expiry to 10 seconds for now.
385 static const int timeOut = 10;
386 if (csrMatcher)
387 {
388 messages::serviceTemporarilyUnavailable(asyncResp->res,
389 std::to_string(timeOut));
390 return;
391 }
392
393 // Make this static so it survives outside this method
394 static boost::asio::steady_timer timeout(*req.ioService);
395 timeout.expires_after(std::chrono::seconds(timeOut));
396 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
397 csrMatcher = nullptr;
398 if (ec)
399 {
400 // operation_aborted is expected if timer is canceled
401 // before completion.
402 if (ec != boost::asio::error::operation_aborted)
403 {
404 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
405 }
406 return;
407 }
408 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
409 messages::internalError(asyncResp->res);
410 });
411
412 // create a matcher to wait on CSR object
413 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
414 std::string match("type='signal',"
415 "interface='org.freedesktop.DBus.ObjectManager',"
416 "path='" +
417 objectPath +
418 "',"
419 "member='InterfacesAdded'");
Patrick Williams59d494e2022-07-22 19:26:55 -0500420 csrMatcher = std::make_unique<sdbusplus::bus::match_t>(
Ed Tanous002d39b2022-05-31 08:59:27 -0700421 *crow::connections::systemBus, match,
Patrick Williams59d494e2022-07-22 19:26:55 -0500422 [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700423 timeout.cancel();
424 if (m.is_method_error())
425 {
426 BMCWEB_LOG_ERROR << "Dbus method error!!!";
427 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800428 return;
429 }
430
Ed Tanous002d39b2022-05-31 08:59:27 -0700431 dbus::utility::DBusInteracesMap interfacesProperties;
432
433 sdbusplus::message::object_path csrObjectPath;
434 m.read(csrObjectPath, interfacesProperties);
435 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
Ed Tanous02cad962022-06-30 16:50:15 -0700436 for (const auto& interface : interfacesProperties)
George Liu0fda0f12021-11-16 10:06:17 +0800437 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700438 if (interface.first == "xyz.openbmc_project.Certs.CSR")
439 {
440 getCSR(asyncResp, certURI, service, objectPath,
441 csrObjectPath.str);
442 break;
443 }
444 }
445 });
446 crow::connections::systemBus->async_method_call(
447 [asyncResp](const boost::system::error_code ec,
448 const std::string&) {
449 if (ec)
450 {
451 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
452 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800453 return;
454 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700455 },
456 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
457 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
458 commonName, *optContactPerson, country, *optEmail, *optGivenName,
459 *optInitials, *optKeyBitLength, *optKeyCurveId,
460 *optKeyPairAlgorithm, *optKeyUsage, organization,
461 organizationalUnit, state, *optSurname, *optUnstructuredName);
George Liu0fda0f12021-11-16 10:06:17 +0800462 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700463} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500464
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600465/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500466 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600467 *
468 * @param[in] asyncResp Shared pointer to the response message
469 * @param[in] str Issuer/Subject value in key=value pairs
470 * @param[in] type Issuer/Subject
471 * @return None
472 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500473static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600474 const std::string_view value)
475{
476 // example: O=openbmc-project.xyz,CN=localhost
477 std::string_view::iterator i = value.begin();
478 while (i != value.end())
479 {
480 std::string_view::iterator tokenBegin = i;
481 while (i != value.end() && *i != '=')
482 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530483 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600484 }
485 if (i == value.end())
486 {
487 break;
488 }
Ed Tanous271584a2019-07-09 16:24:22 -0700489 const std::string_view key(tokenBegin,
490 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530491 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600492 tokenBegin = i;
493 while (i != value.end() && *i != ',')
494 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530495 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600496 }
Ed Tanous271584a2019-07-09 16:24:22 -0700497 const std::string_view val(tokenBegin,
498 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600499 if (key == "L")
500 {
501 out["City"] = val;
502 }
503 else if (key == "CN")
504 {
505 out["CommonName"] = val;
506 }
507 else if (key == "C")
508 {
509 out["Country"] = val;
510 }
511 else if (key == "O")
512 {
513 out["Organization"] = val;
514 }
515 else if (key == "OU")
516 {
517 out["OrganizationalUnit"] = val;
518 }
519 else if (key == "ST")
520 {
521 out["State"] = val;
522 }
523 // skip comma character
524 if (i != value.end())
525 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530526 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600527 }
528 }
529}
530
531/**
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800532 * @brief Retrieve the installed certificate list
533 *
534 * @param[in] asyncResp Shared pointer to the response message
535 * @param[in] basePath DBus object path to search
536 * @param[in] listPtr Json pointer to the list in asyncResp
537 * @param[in] countPtr Json pointer to the count in asyncResp
538 * @return None
539 */
540static void
541 getCertificateList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
542 const std::string& basePath,
543 const nlohmann::json::json_pointer& listPtr,
544 const nlohmann::json::json_pointer& countPtr)
545{
546 crow::connections::systemBus->async_method_call(
547 [asyncResp, listPtr, countPtr](
548 const boost::system::error_code ec,
549 const dbus::utility::MapperGetSubTreePathsResponse& certPaths) {
550 if (ec)
551 {
552 BMCWEB_LOG_ERROR << "Certificate collection query failed: " << ec;
553 messages::internalError(asyncResp->res);
554 return;
555 }
556
557 nlohmann::json& links = asyncResp->res.jsonValue[listPtr];
558 links = nlohmann::json::array();
559 for (const auto& certPath : certPaths)
560 {
561 sdbusplus::message::object_path objPath(certPath);
562 std::string certId = objPath.filename();
563 if (certId.empty())
564 {
565 BMCWEB_LOG_ERROR << "Invalid certificate objPath " << certPath;
566 continue;
567 }
568
569 boost::urls::url certURL;
570 if (objPath.parent_path() == certs::httpsObjectPath)
571 {
572 certURL = crow::utility::urlFromPieces(
573 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
574 "HTTPS", "Certificates", certId);
575 }
576 else if (objPath.parent_path() == certs::ldapObjectPath)
577 {
578 certURL = crow::utility::urlFromPieces("redfish", "v1",
579 "AccountService", "LDAP",
580 "Certificates", certId);
581 }
582 else if (objPath.parent_path() == certs::authorityObjectPath)
583 {
584 certURL = crow::utility::urlFromPieces(
585 "redfish", "v1", "Managers", "bmc", "Truststore",
586 "Certificates", certId);
587 }
588 else
589 {
590 continue;
591 }
592
593 nlohmann::json::object_t link;
594 link["@odata.id"] = certURL;
595 links.emplace_back(std::move(link));
596 }
597
598 asyncResp->res.jsonValue[countPtr] = links.size();
599 },
600 "xyz.openbmc_project.ObjectMapper",
601 "/xyz/openbmc_project/object_mapper",
602 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", basePath, 0,
603 std::array<const char*, 1>{certs::certPropIntf});
604}
605
606/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600607 * @brief Retrieve the certificates properties and append to the response
608 * message
609 *
610 * @param[in] asyncResp Shared pointer to the response message
611 * @param[in] objectPath Path of the D-Bus service object
612 * @param[in] certId Id of the certificate
613 * @param[in] certURL URL of the certificate object
614 * @param[in] name name of the certificate
615 * @return None
616 */
617static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800618 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800619 const std::string& objectPath, const std::string& service,
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800620 const std::string& certId, const boost::urls::url& certURL,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800621 const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600622{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600623 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
624 << " certId=" << certId << " certURl=" << certURL;
625 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800626 [asyncResp, certURL, certId,
627 name](const boost::system::error_code ec,
628 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700629 if (ec)
630 {
631 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800632 messages::resourceNotFound(asyncResp->res, name, certId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700633 return;
634 }
635 asyncResp->res.jsonValue["@odata.id"] = certURL;
636 asyncResp->res.jsonValue["@odata.type"] =
637 "#Certificate.v1_0_0.Certificate";
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800638 asyncResp->res.jsonValue["Id"] = certId;
Ed Tanous002d39b2022-05-31 08:59:27 -0700639 asyncResp->res.jsonValue["Name"] = name;
640 asyncResp->res.jsonValue["Description"] = name;
641 for (const auto& property : properties)
642 {
643 if (property.first == "CertificateString")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600644 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700645 asyncResp->res.jsonValue["CertificateString"] = "";
646 const std::string* value =
647 std::get_if<std::string>(&property.second);
648 if (value != nullptr)
649 {
650 asyncResp->res.jsonValue["CertificateString"] = *value;
651 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600652 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700653 else if (property.first == "KeyUsage")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600654 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700655 nlohmann::json& keyUsage = asyncResp->res.jsonValue["KeyUsage"];
656 keyUsage = nlohmann::json::array();
657 const std::vector<std::string>* value =
658 std::get_if<std::vector<std::string>>(&property.second);
659 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600660 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700661 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600662 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700663 keyUsage.push_back(usage);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600664 }
665 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600666 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700667 else if (property.first == "Issuer")
668 {
669 const std::string* value =
670 std::get_if<std::string>(&property.second);
671 if (value != nullptr)
672 {
673 updateCertIssuerOrSubject(
674 asyncResp->res.jsonValue["Issuer"], *value);
675 }
676 }
677 else if (property.first == "Subject")
678 {
679 const std::string* value =
680 std::get_if<std::string>(&property.second);
681 if (value != nullptr)
682 {
683 updateCertIssuerOrSubject(
684 asyncResp->res.jsonValue["Subject"], *value);
685 }
686 }
687 else if (property.first == "ValidNotAfter")
688 {
689 const uint64_t* value = std::get_if<uint64_t>(&property.second);
690 if (value != nullptr)
691 {
692 asyncResp->res.jsonValue["ValidNotAfter"] =
693 crow::utility::getDateTimeUint(*value);
694 }
695 }
696 else if (property.first == "ValidNotBefore")
697 {
698 const uint64_t* value = std::get_if<uint64_t>(&property.second);
699 if (value != nullptr)
700 {
701 asyncResp->res.jsonValue["ValidNotBefore"] =
702 crow::utility::getDateTimeUint(*value);
703 }
704 }
705 }
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800706 asyncResp->res.addHeader(
Ed Tanousd9f6c622022-03-17 09:12:17 -0700707 boost::beast::http::field::location,
708 std::string_view(certURL.data(), certURL.size()));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600709 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600710 service, objectPath, certs::dbusPropIntf, "GetAll",
711 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600712}
713
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600714/**
715 * Action to replace an existing certificate
716 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700717inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600718{
George Liu0fda0f12021-11-16 10:06:17 +0800719 BMCWEB_ROUTE(
720 app,
721 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700722 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700723 .methods(boost::beast::http::verb::post)(
724 [&app](const crow::Request& req,
725 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000726 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700727 {
728 return;
729 }
730 std::string certificate;
731 nlohmann::json certificateUri;
732 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600733
Ed Tanous002d39b2022-05-31 08:59:27 -0700734 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
735 certificate, "CertificateUri",
736 certificateUri, "CertificateType",
737 certificateType))
738 {
739 BMCWEB_LOG_ERROR << "Required parameters are missing";
740 messages::internalError(asyncResp->res);
741 return;
742 }
743
744 if (!certificateType)
745 {
746 // should never happen, but it never hurts to be paranoid.
747 return;
748 }
749 if (certificateType != "PEM")
750 {
751 messages::actionParameterNotSupported(
752 asyncResp->res, "CertificateType", "ReplaceCertificate");
753 return;
754 }
755
756 std::string certURI;
757 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
758 "@odata.id", certURI))
759 {
760 messages::actionParameterMissing(
761 asyncResp->res, "ReplaceCertificate", "CertificateUri");
762 return;
763 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800764 BMCWEB_LOG_INFO << "Certificate URI to replace: " << certURI;
Ed Tanous002d39b2022-05-31 08:59:27 -0700765
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800766 boost::urls::result<boost::urls::url_view> parsedUrl =
767 boost::urls::parse_relative_ref(certURI);
768 if (!parsedUrl)
Ed Tanous002d39b2022-05-31 08:59:27 -0700769 {
770 messages::actionParameterValueFormatError(asyncResp->res, certURI,
771 "CertificateUri",
772 "ReplaceCertificate");
773 return;
774 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800775
776 std::string id;
777 sdbusplus::message::object_path objectPath;
Ed Tanous002d39b2022-05-31 08:59:27 -0700778 std::string name;
779 std::string service;
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800780 if (crow::utility::readUrlSegments(
781 *parsedUrl, "redfish", "v1", "Managers", "bmc",
782 "NetworkProtocol", "HTTPS", "Certificates", std::ref(id)))
Ed Tanous002d39b2022-05-31 08:59:27 -0700783 {
784 objectPath =
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800785 sdbusplus::message::object_path(certs::httpsObjectPath) / id;
Ed Tanous002d39b2022-05-31 08:59:27 -0700786 name = "HTTPS certificate";
787 service = certs::httpsServiceName;
788 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800789 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
790 "AccountService", "LDAP",
791 "Certificates", std::ref(id)))
Ed Tanous002d39b2022-05-31 08:59:27 -0700792 {
793 objectPath =
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800794 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
Ed Tanous002d39b2022-05-31 08:59:27 -0700795 name = "LDAP certificate";
796 service = certs::ldapServiceName;
797 }
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800798 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
799 "Managers", "bmc", "Truststore",
800 "Certificates", std::ref(id)))
Ed Tanous002d39b2022-05-31 08:59:27 -0700801 {
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800802 objectPath =
803 sdbusplus::message::object_path(certs::authorityObjectPath) /
804 id;
Ed Tanous002d39b2022-05-31 08:59:27 -0700805 name = "TrustStore certificate";
806 service = certs::authorityServiceName;
807 }
808 else
809 {
810 messages::actionParameterNotSupported(
811 asyncResp->res, "CertificateUri", "ReplaceCertificate");
812 return;
813 }
814
815 std::shared_ptr<CertificateFile> certFile =
816 std::make_shared<CertificateFile>(certificate);
817 crow::connections::systemBus->async_method_call(
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800818 [asyncResp, certFile, objectPath, service, url{*parsedUrl}, id,
Ed Tanous002d39b2022-05-31 08:59:27 -0700819 name](const boost::system::error_code ec) {
820 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700821 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700822 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
823 if (ec.value() ==
824 boost::system::linux_error::bad_request_descriptor)
825 {
Jiaqing Zhao75b63a22022-06-08 00:02:04 +0800826 messages::resourceNotFound(asyncResp->res, name, id);
Ed Tanous002d39b2022-05-31 08:59:27 -0700827 return;
828 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700829 messages::internalError(asyncResp->res);
830 return;
831 }
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800832 getCertificateProperties(asyncResp, objectPath, service, id, url,
833 name);
Ed Tanous002d39b2022-05-31 08:59:27 -0700834 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
835 << certFile->getCertFilePath();
836 },
837 service, objectPath, certs::certReplaceIntf, "Replace",
838 certFile->getCertFilePath());
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700839 });
840} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600841
842/**
843 * Certificate resource describes a certificate used to prove the identity
844 * of a component, account or service.
845 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700846
847inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600848{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700849 BMCWEB_ROUTE(
850 app,
851 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700852 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800853 .methods(boost::beast::http::verb::get)(
854 [&app](const crow::Request& req,
855 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
856 const std::string& id) -> void {
857 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
858 {
859 return;
860 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600861
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800862 BMCWEB_LOG_DEBUG << "HTTPS Certificate ID=" << id;
863 const boost::urls::url certURL = crow::utility::urlFromPieces(
864 "redfish", "v1", "Managers", "bmc", "NetworkProtocol",
865 "HTTPS", "Certificates", id);
866 std::string objPath =
867 sdbusplus::message::object_path(certs::httpsObjectPath) /
868 id;
869 getCertificateProperties(asyncResp, objPath,
870 certs::httpsServiceName, id, certURL,
871 "HTTPS Certificate");
872 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700873}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600874
875/**
876 * Collection of HTTPS certificates
877 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700878inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600879{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700880 BMCWEB_ROUTE(app,
881 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700882 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700883 .methods(boost::beast::http::verb::get)(
884 [&app](const crow::Request& req,
885 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000886 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700887 {
888 return;
889 }
890
891 asyncResp->res.jsonValue["@odata.id"] =
892 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
893 asyncResp->res.jsonValue["@odata.type"] =
894 "#CertificateCollection.CertificateCollection";
895 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
896 asyncResp->res.jsonValue["Description"] =
897 "A Collection of HTTPS certificate instances";
898
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800899 getCertificateList(asyncResp, certs::httpsObjectPath,
900 "/Members"_json_pointer,
901 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700902 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600903
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700904 BMCWEB_ROUTE(app,
905 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700906 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700907 .methods(boost::beast::http::verb::post)(
908 [&app](const crow::Request& req,
909 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000910 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700911 {
912 return;
913 }
914 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
915
916 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
917 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
918
919 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
920
921 if (certFileBody.empty())
922 {
923 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
924 messages::unrecognizedRequestBody(asyncResp->res);
925 return;
926 }
927
928 std::shared_ptr<CertificateFile> certFile =
929 std::make_shared<CertificateFile>(certFileBody);
930
931 crow::connections::systemBus->async_method_call(
932 [asyncResp, certFile](const boost::system::error_code ec,
933 const std::string& objectPath) {
934 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700935 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700936 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
937 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700938 return;
939 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800940
941 sdbusplus::message::object_path path(objectPath);
942 std::string certId = path.filename();
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800943 const boost::urls::url certURL = crow::utility::urlFromPieces(
944 "redfish", "v1", "Managers", "bmc", "NetworkProtocol", "HTTPS",
945 "Certificates", certId);
Jiaqing Zhao717b9802022-06-06 20:24:04 +0800946 getCertificateProperties(asyncResp, objectPath,
947 certs::httpsServiceName, certId, certURL,
948 "HTTPS Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -0700949 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
950 << certFile->getCertFilePath();
951 },
952 certs::httpsServiceName, certs::httpsObjectPath,
953 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +0800954 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700955} // requestRoutesHTTPSCertificateCollection
956
957/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600958 * The certificate location schema defines a resource that an administrator
959 * can use in order to locate all certificates installed on a given service.
960 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700961inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600962{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700963 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700964 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous002d39b2022-05-31 08:59:27 -0700965 .methods(boost::beast::http::verb::get)(
966 [&app](const crow::Request& req,
967 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000968 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700969 {
970 return;
971 }
972 asyncResp->res.jsonValue["@odata.id"] =
973 "/redfish/v1/CertificateService/CertificateLocations";
974 asyncResp->res.jsonValue["@odata.type"] =
975 "#CertificateLocations.v1_0_0.CertificateLocations";
976 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
977 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
978 asyncResp->res.jsonValue["Description"] =
979 "Defines a resource that an administrator can use in order to "
980 "locate all certificates installed on a given service";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600981
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800982 getCertificateList(asyncResp, certs::baseObjectPath,
983 "/Links/Certificates"_json_pointer,
984 "/Links/Certificates@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700985 });
986}
987// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -0600988
989/**
990 * Collection of LDAP certificates
991 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700992inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600993{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700994 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700995 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700996 .methods(boost::beast::http::verb::get)(
997 [&app](const crow::Request& req,
998 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000999 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001000 {
1001 return;
1002 }
1003
1004 asyncResp->res.jsonValue["@odata.id"] =
1005 "/redfish/v1/AccountService/LDAP/Certificates";
1006 asyncResp->res.jsonValue["@odata.type"] =
1007 "#CertificateCollection.CertificateCollection";
1008 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1009 asyncResp->res.jsonValue["Description"] =
1010 "A Collection of LDAP certificate instances";
1011
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001012 getCertificateList(asyncResp, certs::ldapObjectPath,
1013 "/Members"_json_pointer,
1014 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001015 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001016
1017 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001018 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001019 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001020 [&app](const crow::Request& req,
1021 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001022 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001023 {
1024 return;
1025 }
1026 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001027
Ed Tanous002d39b2022-05-31 08:59:27 -07001028 if (certFileBody.empty())
1029 {
1030 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1031 messages::unrecognizedRequestBody(asyncResp->res);
1032 return;
1033 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001034
Ed Tanous002d39b2022-05-31 08:59:27 -07001035 std::shared_ptr<CertificateFile> certFile =
1036 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001037
Ed Tanous002d39b2022-05-31 08:59:27 -07001038 crow::connections::systemBus->async_method_call(
1039 [asyncResp, certFile](const boost::system::error_code ec,
1040 const std::string& objectPath) {
1041 if (ec)
1042 {
1043 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1044 messages::internalError(asyncResp->res);
1045 return;
1046 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001047
1048 sdbusplus::message::object_path path(objectPath);
1049 std::string certId = path.filename();
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001050 const boost::urls::url certURL =
1051 crow::utility::urlFromPieces("redfish", "v1", "AccountService",
1052 "LDAP", "Certificates", certId);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001053 getCertificateProperties(asyncResp, objectPath,
1054 certs::ldapServiceName, certId, certURL,
1055 "LDAP Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001056 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1057 << certFile->getCertFilePath();
1058 },
1059 certs::ldapServiceName, certs::ldapObjectPath,
1060 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1061 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001062} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001063
1064/**
1065 * Certificate resource describes a certificate used to prove the identity
1066 * of a component, account or service.
1067 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001068inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001069{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001070 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001071 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001072 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001073 [&app](const crow::Request& req,
1074 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001075 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001076 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001077 {
1078 return;
1079 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001080
1081 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << id;
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001082 const boost::urls::url certURL = crow::utility::urlFromPieces(
1083 "redfish", "v1", "AccountService", "LDAP", "Certificates", id);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001084 std::string objPath =
1085 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1086 getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id,
1087 certURL, "LDAP Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001088 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001089} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001090/**
1091 * Collection of TrustStoreCertificate certificates
1092 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001093inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001094{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001095 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001096 .privileges(redfish::privileges::getCertificate)
Ed Tanous002d39b2022-05-31 08:59:27 -07001097 .methods(boost::beast::http::verb::get)(
1098 [&app](const crow::Request& req,
1099 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001100 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001101 {
1102 return;
1103 }
1104
1105 asyncResp->res.jsonValue["@odata.id"] =
1106 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1107 asyncResp->res.jsonValue["@odata.type"] =
1108 "#CertificateCollection.CertificateCollection";
1109 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1110 asyncResp->res.jsonValue["Description"] =
1111 "A Collection of TrustStore certificate instances";
1112
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +08001113 getCertificateList(asyncResp, certs::authorityObjectPath,
1114 "/Members"_json_pointer,
1115 "/Members@odata.count"_json_pointer);
George Liu0fda0f12021-11-16 10:06:17 +08001116 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001117
1118 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001119 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001120 .methods(boost::beast::http::verb::post)(
1121 [&app](const crow::Request& req,
1122 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001123 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001124 {
1125 return;
1126 }
1127 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1128
1129 if (certFileBody.empty())
1130 {
1131 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1132 messages::unrecognizedRequestBody(asyncResp->res);
1133 return;
1134 }
1135
1136 std::shared_ptr<CertificateFile> certFile =
1137 std::make_shared<CertificateFile>(certFileBody);
1138 crow::connections::systemBus->async_method_call(
1139 [asyncResp, certFile](const boost::system::error_code ec,
1140 const std::string& objectPath) {
1141 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001142 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001143 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1144 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001145 return;
1146 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001147
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001148 sdbusplus::message::object_path path(objectPath);
1149 std::string certId = path.filename();
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001150 const boost::urls::url certURL = crow::utility::urlFromPieces(
1151 "redfish", "v1", "Managers", "bmc", "Truststore",
1152 "Certificates", certId);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001153 getCertificateProperties(asyncResp, objectPath,
1154 certs::authorityServiceName, certId,
1155 certURL, "TrustStore Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001156 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1157 << certFile->getCertFilePath();
1158 },
1159 certs::authorityServiceName, certs::authorityObjectPath,
1160 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +08001161 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001162} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001163
1164/**
1165 * Certificate resource describes a certificate used to prove the identity
1166 * of a component, account or service.
1167 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001168inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001169{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001170 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001171 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001172 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001173 [&app](const crow::Request& req,
1174 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001175 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001176 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001177 {
1178 return;
1179 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001180
1181 BMCWEB_LOG_DEBUG << "Truststore Certificate ID=" << id;
Jiaqing Zhao1e312592022-06-14 12:58:09 +08001182 const boost::urls::url certURL =
1183 crow::utility::urlFromPieces("redfish", "v1", "Managers", "bmc",
1184 "Truststore", "Certificates", id);
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001185 std::string objPath =
1186 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1187 getCertificateProperties(asyncResp, objPath,
1188 certs::authorityServiceName, id, certURL,
1189 "TrustStore Certificate");
Ed Tanous002d39b2022-05-31 08:59:27 -07001190 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001191
1192 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001193 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001194 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001195 [&app](const crow::Request& req,
1196 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001197 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001198 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001199 {
1200 return;
1201 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001202
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001203 BMCWEB_LOG_DEBUG << "Delete TrustStore Certificate ID=" << id;
1204 std::string objPath =
1205 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001206
Ed Tanous002d39b2022-05-31 08:59:27 -07001207 crow::connections::systemBus->async_method_call(
1208 [asyncResp, id](const boost::system::error_code ec) {
1209 if (ec)
1210 {
1211 messages::resourceNotFound(asyncResp->res,
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001212 "TrustStore Certificate", id);
Ed Tanous002d39b2022-05-31 08:59:27 -07001213 return;
1214 }
1215 BMCWEB_LOG_INFO << "Certificate deleted";
1216 asyncResp->res.result(boost::beast::http::status::no_content);
1217 },
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001218 certs::authorityServiceName, objPath, certs::objDeleteIntf,
Ed Tanous002d39b2022-05-31 08:59:27 -07001219 "Delete");
1220 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001221} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001222} // namespace redfish