blob: c0ae4b34c8d42011d0c7396cd5f5a46d16516902 [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001#pragma once
2
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003#include <app.hpp>
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +02004#include <boost/convert.hpp>
5#include <boost/convert/strtol.hpp>
Jiaqing Zhao90d2d1e2022-04-13 17:01:57 +08006#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -08007#include <dbus_utility.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -07008#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -07009#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050010
Marri Devender Rao5968cae2019-01-21 10:27:12 -060011namespace redfish
12{
13namespace certs
14{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050015constexpr char const* httpsObjectPath =
Marri Devender Rao5968cae2019-01-21 10:27:12 -060016 "/xyz/openbmc_project/certs/server/https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050017constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
18constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
19constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
20constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
21constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
22constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
23constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
24constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060025 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060027 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050028constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050029 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050030constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050031 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060032} // namespace certs
33
34/**
35 * The Certificate schema defines a Certificate Service which represents the
36 * actions available to manage certificates and links to where certificates
37 * are installed.
38 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060039
John Edward Broadbent7e860f12021-04-08 15:57:16 -070040// TODO: Issue#61 No entries are available for Certificate
41// service at https://www.dmtf.org/standards/redfish
42// "redfish standard registries". Need to modify after DMTF
43// publish Privilege details for certificate service
44
45inline void requestRoutesCertificateService(App& app)
46{
47 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
Ed Tanoused398212021-06-09 17:05:54 -070048 .privileges(redfish::privileges::getCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -070049 .methods(boost::beast::http::verb::get)(
50 [&app](const crow::Request& req,
51 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +000052 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -070053 {
54 return;
55 }
Ed Tanous14766872022-03-15 10:44:42 -070056
Ed Tanous002d39b2022-05-31 08:59:27 -070057 asyncResp->res.jsonValue["@odata.type"] =
58 "#CertificateService.v1_0_0.CertificateService";
59 asyncResp->res.jsonValue["@odata.id"] =
60 "/redfish/v1/CertificateService";
61 asyncResp->res.jsonValue["Id"] = "CertificateService";
62 asyncResp->res.jsonValue["Name"] = "Certificate Service";
63 asyncResp->res.jsonValue["Description"] =
64 "Actions available to manage certificates";
65 // /redfish/v1/CertificateService/CertificateLocations is something
66 // only ConfigureManager can access then only display when the user
67 // has permissions ConfigureManager
68 Privileges effectiveUserPrivileges =
69 redfish::getUserPrivileges(req.userRole);
70 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
71 effectiveUserPrivileges))
72 {
73 asyncResp->res.jsonValue["CertificateLocations"]["@odata.id"] =
74 "/redfish/v1/CertificateService/CertificateLocations";
75 }
76 asyncResp->res
77 .jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = {
78 {"target",
79 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"},
80 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
81 asyncResp->res
82 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
83 {"target",
84 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR"}};
Abhishek Patel72048782021-06-02 09:53:24 -050085 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070086} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060087
Marri Devender Rao5968cae2019-01-21 10:27:12 -060088/**
89 * @brief Find the ID specified in the URL
90 * Finds the numbers specified after the last "/" in the URL and returns.
91 * @param[in] path URL
92 * @return -1 on failure and number on success
93 */
Ed Tanous23a21a12020-07-25 04:45:05 +000094inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060095{
Ed Tanousf23b7292020-10-15 09:41:17 -070096 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -060097 if (found == std::string::npos)
98 {
99 return -1;
100 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200101
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600102 if ((found + 1) < url.length())
103 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600104 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200105
106 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600107 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200108
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600109 return -1;
110}
111
zhanghch058d1b46d2021-04-01 11:18:24 +0800112inline std::string getCertificateFromReqBody(
113 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
114 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200115{
116 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
117
118 if (reqJson.is_discarded())
119 {
120 // We did not receive JSON request, proceed as it is RAW data
121 return req.body;
122 }
123
124 std::string certificate;
125 std::optional<std::string> certificateType = "PEM";
126
Willy Tu15ed6782021-12-14 11:03:16 -0800127 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
128 certificate, "CertificateType",
129 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200130 {
131 BMCWEB_LOG_ERROR << "Required parameters are missing";
132 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700133 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200134 }
135
136 if (*certificateType != "PEM")
137 {
138 messages::propertyValueNotInList(asyncResp->res, *certificateType,
139 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700140 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200141 }
142
143 return certificate;
144}
145
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600146/**
147 * Class to create a temporary certificate file for uploading to system
148 */
149class CertificateFile
150{
151 public:
152 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500153 CertificateFile(const CertificateFile&) = delete;
154 CertificateFile& operator=(const CertificateFile&) = delete;
155 CertificateFile(CertificateFile&&) = delete;
156 CertificateFile& operator=(CertificateFile&&) = delete;
157 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600158 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700159 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700160 'e', 'r', 't', 's', '.', 'X',
161 'X', 'X', 'X', 'X', 'X', '\0'};
162 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800163 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600164 {
165 certDirectory = tempDirectory;
166 certificateFile = certDirectory / "cert.pem";
167 std::ofstream out(certificateFile, std::ofstream::out |
168 std::ofstream::binary |
169 std::ofstream::trunc);
170 out << certString;
171 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800172 BMCWEB_LOG_DEBUG << "Creating certificate file"
173 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600174 }
175 }
176 ~CertificateFile()
177 {
178 if (std::filesystem::exists(certDirectory))
179 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800180 BMCWEB_LOG_DEBUG << "Removing certificate file"
181 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000182 std::error_code ec;
183 std::filesystem::remove_all(certDirectory, ec);
184 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600185 {
186 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800187 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600188 }
189 }
190 }
191 std::string getCertFilePath()
192 {
193 return certificateFile;
194 }
195
196 private:
197 std::filesystem::path certificateFile;
198 std::filesystem::path certDirectory;
199};
200
Marri Devender Rao30215812019-03-18 08:59:21 -0500201static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
202/**
203 * @brief Read data from CSR D-bus object and set to response
204 *
205 * @param[in] asyncResp Shared pointer to the response message
206 * @param[in] certURI Link to certifiate collection URI
207 * @param[in] service D-Bus service name
208 * @param[in] certObjPath certificate D-Bus object path
209 * @param[in] csrObjPath CSR D-Bus object path
210 * @return None
211 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800212static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500213 const std::string& certURI, const std::string& service,
214 const std::string& certObjPath,
215 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500216{
217 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
218 << " CSRObjectPath=" << csrObjPath
219 << " service=" << service;
220 crow::connections::systemBus->async_method_call(
221 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500222 const std::string& csr) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700223 if (ec)
224 {
225 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
226 messages::internalError(asyncResp->res);
227 return;
228 }
229 if (csr.empty())
230 {
231 BMCWEB_LOG_ERROR << "CSR read is empty";
232 messages::internalError(asyncResp->res);
233 return;
234 }
235 asyncResp->res.jsonValue["CSRString"] = csr;
236 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
237 certURI;
Marri Devender Rao30215812019-03-18 08:59:21 -0500238 },
239 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
240}
241
242/**
243 * Action to Generate CSR
244 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700245inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500246{
George Liu0fda0f12021-11-16 10:06:17 +0800247 BMCWEB_ROUTE(
248 app,
249 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
Abhishek Patel5344ab82021-07-31 17:42:09 -0500250 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700251 .methods(boost::beast::http::verb::post)(
252 [&app](const crow::Request& req,
253 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000254 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700255 {
256 return;
257 }
258 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500259
Ed Tanous002d39b2022-05-31 08:59:27 -0700260 // Required parameters
261 std::string city;
262 std::string commonName;
263 std::string country;
264 std::string organization;
265 std::string organizationalUnit;
266 std::string state;
267 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800268
Ed Tanous002d39b2022-05-31 08:59:27 -0700269 // Optional parameters
270 std::optional<std::vector<std::string>> optAlternativeNames =
271 std::vector<std::string>();
272 std::optional<std::string> optContactPerson = "";
273 std::optional<std::string> optChallengePassword = "";
274 std::optional<std::string> optEmail = "";
275 std::optional<std::string> optGivenName = "";
276 std::optional<std::string> optInitials = "";
277 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
278 std::optional<std::string> optKeyCurveId = "secp384r1";
279 std::optional<std::string> optKeyPairAlgorithm = "EC";
280 std::optional<std::vector<std::string>> optKeyUsage =
281 std::vector<std::string>();
282 std::optional<std::string> optSurname = "";
283 std::optional<std::string> optUnstructuredName = "";
284 if (!json_util::readJsonAction(
285 req, asyncResp->res, "City", city, "CommonName", commonName,
286 "ContactPerson", optContactPerson, "Country", country,
287 "Organization", organization, "OrganizationalUnit",
288 organizationalUnit, "State", state, "CertificateCollection",
289 certificateCollection, "AlternativeNames", optAlternativeNames,
290 "ChallengePassword", optChallengePassword, "Email", optEmail,
291 "GivenName", optGivenName, "Initials", optInitials,
292 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
293 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
294 optKeyUsage, "Surname", optSurname, "UnstructuredName",
295 optUnstructuredName))
296 {
297 return;
298 }
George Liu0fda0f12021-11-16 10:06:17 +0800299
Ed Tanous002d39b2022-05-31 08:59:27 -0700300 // bmcweb has no way to store or decode a private key challenge
301 // password, which will likely cause bmcweb to crash on startup
302 // if this is not set on a post so not allowing the user to set
303 // value
304 if (!optChallengePassword->empty())
305 {
306 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
307 "ChallengePassword");
308 return;
309 }
George Liu0fda0f12021-11-16 10:06:17 +0800310
Ed Tanous002d39b2022-05-31 08:59:27 -0700311 std::string certURI;
312 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
313 "@odata.id", certURI))
314 {
315 return;
316 }
George Liu0fda0f12021-11-16 10:06:17 +0800317
Ed Tanous002d39b2022-05-31 08:59:27 -0700318 std::string objectPath;
319 std::string service;
320 if (boost::starts_with(
321 certURI,
322 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
323 {
324 objectPath = certs::httpsObjectPath;
325 service = certs::httpsServiceName;
326 }
327 else if (boost::starts_with(
328 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
329 {
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
360 if (boost::starts_with(
361 certURI,
362 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
363 {
364 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800365 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700366 optKeyUsage->push_back("ServerAuthentication");
George Liu0fda0f12021-11-16 10:06:17 +0800367 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700368 else if (optKeyUsage->size() == 1)
George Liu0fda0f12021-11-16 10:06:17 +0800369 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700370 if ((*optKeyUsage)[0] != "ServerAuthentication")
371 {
372 messages::propertyValueNotInList(
373 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
374 return;
375 }
George Liu0fda0f12021-11-16 10:06:17 +0800376 }
377 else
378 {
379 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700380 asyncResp->res, "KeyUsage", "GenerateCSR");
George Liu0fda0f12021-11-16 10:06:17 +0800381 return;
382 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700383 }
384 else if (boost::starts_with(
385 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
386 {
387 if (optKeyUsage->empty())
388 {
389 optKeyUsage->push_back("ClientAuthentication");
390 }
391 else if (optKeyUsage->size() == 1)
392 {
393 if ((*optKeyUsage)[0] != "ClientAuthentication")
394 {
395 messages::propertyValueNotInList(
396 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
397 return;
398 }
399 }
400 else
George Liu0fda0f12021-11-16 10:06:17 +0800401 {
402 messages::actionParameterNotSupported(
Ed Tanous002d39b2022-05-31 08:59:27 -0700403 asyncResp->res, "KeyUsage", "GenerateCSR");
404 return;
405 }
406 }
407
408 // Only allow one CSR matcher at a time so setting retry
409 // time-out and timer expiry to 10 seconds for now.
410 static const int timeOut = 10;
411 if (csrMatcher)
412 {
413 messages::serviceTemporarilyUnavailable(asyncResp->res,
414 std::to_string(timeOut));
415 return;
416 }
417
418 // Make this static so it survives outside this method
419 static boost::asio::steady_timer timeout(*req.ioService);
420 timeout.expires_after(std::chrono::seconds(timeOut));
421 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
422 csrMatcher = nullptr;
423 if (ec)
424 {
425 // operation_aborted is expected if timer is canceled
426 // before completion.
427 if (ec != boost::asio::error::operation_aborted)
428 {
429 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
430 }
431 return;
432 }
433 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
434 messages::internalError(asyncResp->res);
435 });
436
437 // create a matcher to wait on CSR object
438 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
439 std::string match("type='signal',"
440 "interface='org.freedesktop.DBus.ObjectManager',"
441 "path='" +
442 objectPath +
443 "',"
444 "member='InterfacesAdded'");
445 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
446 *crow::connections::systemBus, match,
447 [asyncResp, service, objectPath,
448 certURI](sdbusplus::message::message& m) {
449 timeout.cancel();
450 if (m.is_method_error())
451 {
452 BMCWEB_LOG_ERROR << "Dbus method error!!!";
453 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800454 return;
455 }
456
Ed Tanous002d39b2022-05-31 08:59:27 -0700457 dbus::utility::DBusInteracesMap interfacesProperties;
458
459 sdbusplus::message::object_path csrObjectPath;
460 m.read(csrObjectPath, interfacesProperties);
461 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
462 for (auto& interface : interfacesProperties)
George Liu0fda0f12021-11-16 10:06:17 +0800463 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700464 if (interface.first == "xyz.openbmc_project.Certs.CSR")
465 {
466 getCSR(asyncResp, certURI, service, objectPath,
467 csrObjectPath.str);
468 break;
469 }
470 }
471 });
472 crow::connections::systemBus->async_method_call(
473 [asyncResp](const boost::system::error_code ec,
474 const std::string&) {
475 if (ec)
476 {
477 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
478 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800479 return;
480 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700481 },
482 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
483 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
484 commonName, *optContactPerson, country, *optEmail, *optGivenName,
485 *optInitials, *optKeyBitLength, *optKeyCurveId,
486 *optKeyPairAlgorithm, *optKeyUsage, organization,
487 organizationalUnit, state, *optSurname, *optUnstructuredName);
George Liu0fda0f12021-11-16 10:06:17 +0800488 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700489} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500490
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600491/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500492 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600493 *
494 * @param[in] asyncResp Shared pointer to the response message
495 * @param[in] str Issuer/Subject value in key=value pairs
496 * @param[in] type Issuer/Subject
497 * @return None
498 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500499static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600500 const std::string_view value)
501{
502 // example: O=openbmc-project.xyz,CN=localhost
503 std::string_view::iterator i = value.begin();
504 while (i != value.end())
505 {
506 std::string_view::iterator tokenBegin = i;
507 while (i != value.end() && *i != '=')
508 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530509 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600510 }
511 if (i == value.end())
512 {
513 break;
514 }
Ed Tanous271584a2019-07-09 16:24:22 -0700515 const std::string_view key(tokenBegin,
516 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530517 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600518 tokenBegin = i;
519 while (i != value.end() && *i != ',')
520 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530521 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600522 }
Ed Tanous271584a2019-07-09 16:24:22 -0700523 const std::string_view val(tokenBegin,
524 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600525 if (key == "L")
526 {
527 out["City"] = val;
528 }
529 else if (key == "CN")
530 {
531 out["CommonName"] = val;
532 }
533 else if (key == "C")
534 {
535 out["Country"] = val;
536 }
537 else if (key == "O")
538 {
539 out["Organization"] = val;
540 }
541 else if (key == "OU")
542 {
543 out["OrganizationalUnit"] = val;
544 }
545 else if (key == "ST")
546 {
547 out["State"] = val;
548 }
549 // skip comma character
550 if (i != value.end())
551 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530552 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600553 }
554 }
555}
556
557/**
558 * @brief Retrieve the certificates properties and append to the response
559 * message
560 *
561 * @param[in] asyncResp Shared pointer to the response message
562 * @param[in] objectPath Path of the D-Bus service object
563 * @param[in] certId Id of the certificate
564 * @param[in] certURL URL of the certificate object
565 * @param[in] name name of the certificate
566 * @return None
567 */
568static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800569 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
570 const std::string& objectPath, const std::string& service, long certId,
571 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600572{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600573 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
574 << " certId=" << certId << " certURl=" << certURL;
575 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800576 [asyncResp, certURL, certId,
577 name](const boost::system::error_code ec,
578 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700579 if (ec)
580 {
581 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
582 messages::resourceNotFound(asyncResp->res, name,
583 std::to_string(certId));
584 return;
585 }
586 asyncResp->res.jsonValue["@odata.id"] = certURL;
587 asyncResp->res.jsonValue["@odata.type"] =
588 "#Certificate.v1_0_0.Certificate";
589 asyncResp->res.jsonValue["Id"] = std::to_string(certId);
590 asyncResp->res.jsonValue["Name"] = name;
591 asyncResp->res.jsonValue["Description"] = name;
592 for (const auto& property : properties)
593 {
594 if (property.first == "CertificateString")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600595 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700596 asyncResp->res.jsonValue["CertificateString"] = "";
597 const std::string* value =
598 std::get_if<std::string>(&property.second);
599 if (value != nullptr)
600 {
601 asyncResp->res.jsonValue["CertificateString"] = *value;
602 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600603 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700604 else if (property.first == "KeyUsage")
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600605 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700606 nlohmann::json& keyUsage = asyncResp->res.jsonValue["KeyUsage"];
607 keyUsage = nlohmann::json::array();
608 const std::vector<std::string>* value =
609 std::get_if<std::vector<std::string>>(&property.second);
610 if (value != nullptr)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600611 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700612 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600613 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700614 keyUsage.push_back(usage);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600615 }
616 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600617 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700618 else if (property.first == "Issuer")
619 {
620 const std::string* value =
621 std::get_if<std::string>(&property.second);
622 if (value != nullptr)
623 {
624 updateCertIssuerOrSubject(
625 asyncResp->res.jsonValue["Issuer"], *value);
626 }
627 }
628 else if (property.first == "Subject")
629 {
630 const std::string* value =
631 std::get_if<std::string>(&property.second);
632 if (value != nullptr)
633 {
634 updateCertIssuerOrSubject(
635 asyncResp->res.jsonValue["Subject"], *value);
636 }
637 }
638 else if (property.first == "ValidNotAfter")
639 {
640 const uint64_t* value = std::get_if<uint64_t>(&property.second);
641 if (value != nullptr)
642 {
643 asyncResp->res.jsonValue["ValidNotAfter"] =
644 crow::utility::getDateTimeUint(*value);
645 }
646 }
647 else if (property.first == "ValidNotBefore")
648 {
649 const uint64_t* value = std::get_if<uint64_t>(&property.second);
650 if (value != nullptr)
651 {
652 asyncResp->res.jsonValue["ValidNotBefore"] =
653 crow::utility::getDateTimeUint(*value);
654 }
655 }
656 }
657 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600658 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600659 service, objectPath, certs::dbusPropIntf, "GetAll",
660 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600661}
662
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600663/**
664 * Action to replace an existing certificate
665 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700666inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600667{
George Liu0fda0f12021-11-16 10:06:17 +0800668 BMCWEB_ROUTE(
669 app,
670 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700671 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700672 .methods(boost::beast::http::verb::post)(
673 [&app](const crow::Request& req,
674 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000675 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700676 {
677 return;
678 }
679 std::string certificate;
680 nlohmann::json certificateUri;
681 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600682
Ed Tanous002d39b2022-05-31 08:59:27 -0700683 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
684 certificate, "CertificateUri",
685 certificateUri, "CertificateType",
686 certificateType))
687 {
688 BMCWEB_LOG_ERROR << "Required parameters are missing";
689 messages::internalError(asyncResp->res);
690 return;
691 }
692
693 if (!certificateType)
694 {
695 // should never happen, but it never hurts to be paranoid.
696 return;
697 }
698 if (certificateType != "PEM")
699 {
700 messages::actionParameterNotSupported(
701 asyncResp->res, "CertificateType", "ReplaceCertificate");
702 return;
703 }
704
705 std::string certURI;
706 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
707 "@odata.id", certURI))
708 {
709 messages::actionParameterMissing(
710 asyncResp->res, "ReplaceCertificate", "CertificateUri");
711 return;
712 }
713
714 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
715 long id = getIDFromURL(certURI);
716 if (id < 0)
717 {
718 messages::actionParameterValueFormatError(asyncResp->res, certURI,
719 "CertificateUri",
720 "ReplaceCertificate");
721 return;
722 }
723 std::string objectPath;
724 std::string name;
725 std::string service;
726 if (boost::starts_with(
727 certURI,
728 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
729 {
730 objectPath =
731 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
732 name = "HTTPS certificate";
733 service = certs::httpsServiceName;
734 }
735 else if (boost::starts_with(
736 certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
737 {
738 objectPath =
739 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
740 name = "LDAP certificate";
741 service = certs::ldapServiceName;
742 }
743 else if (boost::starts_with(
744 certURI,
745 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
746 {
747 objectPath = std::string(certs::authorityObjectPath) + "/" +
748 std::to_string(id);
749 name = "TrustStore certificate";
750 service = certs::authorityServiceName;
751 }
752 else
753 {
754 messages::actionParameterNotSupported(
755 asyncResp->res, "CertificateUri", "ReplaceCertificate");
756 return;
757 }
758
759 std::shared_ptr<CertificateFile> certFile =
760 std::make_shared<CertificateFile>(certificate);
761 crow::connections::systemBus->async_method_call(
762 [asyncResp, certFile, objectPath, service, certURI, id,
763 name](const boost::system::error_code ec) {
764 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700765 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700766 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
767 if (ec.value() ==
768 boost::system::linux_error::bad_request_descriptor)
769 {
770 messages::resourceNotFound(asyncResp->res, name,
771 std::to_string(id));
772 return;
773 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700774 messages::internalError(asyncResp->res);
775 return;
776 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700777 getCertificateProperties(asyncResp, objectPath, service, id,
778 certURI, name);
779 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
780 << certFile->getCertFilePath();
781 },
782 service, objectPath, certs::certReplaceIntf, "Replace",
783 certFile->getCertFilePath());
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700784 });
785} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600786
787/**
788 * Certificate resource describes a certificate used to prove the identity
789 * of a component, account or service.
790 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700791
792inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600793{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700794 BMCWEB_ROUTE(
795 app,
796 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700797 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700798 .methods(
799 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -0700800 get)([&app](const crow::Request& req,
801 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
802 const std::string& param) -> void {
Carson Labrado3ba00072022-06-06 19:40:56 +0000803 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous45ca1b82022-03-25 13:07:27 -0700804 {
805 return;
806 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700807 if (param.empty())
808 {
809 messages::internalError(asyncResp->res);
810 return;
811 }
812 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600813
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700814 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
815 << std::to_string(id);
816 std::string certURL =
817 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
818 std::to_string(id);
819 std::string objectPath = certs::httpsObjectPath;
820 objectPath += "/";
821 objectPath += std::to_string(id);
822 getCertificateProperties(asyncResp, objectPath,
823 certs::httpsServiceName, id, certURL,
824 "HTTPS Certificate");
825 });
826}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600827
828/**
829 * Collection of HTTPS certificates
830 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700831inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600832{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700833 BMCWEB_ROUTE(app,
834 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700835 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700836 .methods(boost::beast::http::verb::get)(
837 [&app](const crow::Request& req,
838 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000839 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700840 {
841 return;
842 }
843
844 asyncResp->res.jsonValue["@odata.id"] =
845 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
846 asyncResp->res.jsonValue["@odata.type"] =
847 "#CertificateCollection.CertificateCollection";
848 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
849 asyncResp->res.jsonValue["Description"] =
850 "A Collection of HTTPS certificate instances";
851
852 crow::connections::systemBus->async_method_call(
853 [asyncResp](const boost::system::error_code ec,
854 const dbus::utility::ManagedObjectType& certs) {
855 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700856 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700857 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
858 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700859 return;
860 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700861 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
862 members = nlohmann::json::array();
863 for (const auto& cert : certs)
864 {
865 long id = getIDFromURL(cert.first.str);
866 if (id >= 0)
867 {
868 nlohmann::json::object_t member;
869 member["@odata.id"] =
870 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
871 std::to_string(id);
872 members.push_back(std::move(member));
873 }
874 }
875 asyncResp->res.jsonValue["Members@odata.count"] = members.size();
876 },
877 certs::httpsServiceName, certs::httpsObjectPath,
878 certs::dbusObjManagerIntf, "GetManagedObjects");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700879 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600880
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700881 BMCWEB_ROUTE(app,
882 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700883 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700884 .methods(boost::beast::http::verb::post)(
885 [&app](const crow::Request& req,
886 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000887 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700888 {
889 return;
890 }
891 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
892
893 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
894 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
895
896 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
897
898 if (certFileBody.empty())
899 {
900 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
901 messages::unrecognizedRequestBody(asyncResp->res);
902 return;
903 }
904
905 std::shared_ptr<CertificateFile> certFile =
906 std::make_shared<CertificateFile>(certFileBody);
907
908 crow::connections::systemBus->async_method_call(
909 [asyncResp, certFile](const boost::system::error_code ec,
910 const std::string& objectPath) {
911 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700912 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700913 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
914 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -0700915 return;
916 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700917 long certId = getIDFromURL(objectPath);
918 if (certId < 0)
George Liu0fda0f12021-11-16 10:06:17 +0800919 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700920 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
921 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +0800922 return;
923 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700924 std::string certURL =
925 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
926 std::to_string(certId);
927 getCertificateProperties(asyncResp, objectPath,
928 certs::httpsServiceName, certId, certURL,
929 "HTTPS Certificate");
930 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
931 << certFile->getCertFilePath();
932 },
933 certs::httpsServiceName, certs::httpsObjectPath,
934 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +0800935 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700936} // requestRoutesHTTPSCertificateCollection
937
938/**
939 * @brief Retrieve the certificates installed list and append to the
940 * response
941 *
942 * @param[in] asyncResp Shared pointer to the response message
943 * @param[in] certURL Path of the certificate object
944 * @param[in] path Path of the D-Bus service object
945 * @return None
946 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700947inline void
948 getCertificateLocations(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
949 const std::string& certURL, const std::string& path,
950 const std::string& service)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700951{
952 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
953 << " Path=" << path << " service= " << service;
954 crow::connections::systemBus->async_method_call(
955 [asyncResp, certURL](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800956 const dbus::utility::ManagedObjectType& certs) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700957 if (ec)
958 {
959 BMCWEB_LOG_WARNING << "Certificate collection query failed: " << ec
960 << ", skipping " << certURL;
961 return;
962 }
963 nlohmann::json& links =
964 asyncResp->res.jsonValue["Links"]["Certificates"];
965 for (const auto& cert : certs)
966 {
967 long id = getIDFromURL(cert.first.str);
968 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700969 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700970 nlohmann::json::object_t link;
971 link["@odata.id"] = certURL + std::to_string(id);
972 links.push_back(std::move(link));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700973 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700974 }
975 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
976 links.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700977 },
978 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
979}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600980
981/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600982 * The certificate location schema defines a resource that an administrator
983 * can use in order to locate all certificates installed on a given service.
984 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700985inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600986{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700987 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700988 .privileges(redfish::privileges::getCertificateLocations)
Ed Tanous002d39b2022-05-31 08:59:27 -0700989 .methods(boost::beast::http::verb::get)(
990 [&app](const crow::Request& req,
991 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000992 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700993 {
994 return;
995 }
996 asyncResp->res.jsonValue["@odata.id"] =
997 "/redfish/v1/CertificateService/CertificateLocations";
998 asyncResp->res.jsonValue["@odata.type"] =
999 "#CertificateLocations.v1_0_0.CertificateLocations";
1000 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
1001 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
1002 asyncResp->res.jsonValue["Description"] =
1003 "Defines a resource that an administrator can use in order to "
1004 "locate all certificates installed on a given service";
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001005
Ed Tanous002d39b2022-05-31 08:59:27 -07001006 nlohmann::json& links =
1007 asyncResp->res.jsonValue["Links"]["Certificates"];
1008 links = nlohmann::json::array();
1009 getCertificateLocations(
1010 asyncResp,
1011 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
1012 certs::httpsObjectPath, certs::httpsServiceName);
1013 getCertificateLocations(asyncResp,
1014 "/redfish/v1/AccountService/LDAP/Certificates/",
1015 certs::ldapObjectPath, certs::ldapServiceName);
1016 getCertificateLocations(
1017 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1018 certs::authorityObjectPath, certs::authorityServiceName);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001019 });
1020}
1021// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001022
1023/**
1024 * Collection of LDAP certificates
1025 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001026inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001027{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001028 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001029 .privileges(redfish::privileges::getCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001030 .methods(boost::beast::http::verb::get)(
1031 [&app](const crow::Request& req,
1032 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001033 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001034 {
1035 return;
1036 }
1037
1038 asyncResp->res.jsonValue["@odata.id"] =
1039 "/redfish/v1/AccountService/LDAP/Certificates";
1040 asyncResp->res.jsonValue["@odata.type"] =
1041 "#CertificateCollection.CertificateCollection";
1042 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1043 asyncResp->res.jsonValue["Description"] =
1044 "A Collection of LDAP certificate instances";
1045
1046 crow::connections::systemBus->async_method_call(
1047 [asyncResp](const boost::system::error_code ec,
1048 const dbus::utility::ManagedObjectType& certs) {
1049 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
1050 nlohmann::json& count =
1051 asyncResp->res.jsonValue["Members@odata.count"];
1052 members = nlohmann::json::array();
1053 count = 0;
1054 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001055 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001056 BMCWEB_LOG_WARNING << "LDAP certificate query failed: " << ec;
Ed Tanous45ca1b82022-03-25 13:07:27 -07001057 return;
1058 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001059 for (const auto& cert : certs)
1060 {
1061 long id = getIDFromURL(cert.first.str);
1062 if (id >= 0)
1063 {
1064 nlohmann::json::object_t member;
1065 member["@odata.id"] =
1066 "/redfish/v1/AccountService/LDAP/Certificates/" +
1067 std::to_string(id);
1068 members.push_back(std::move(member));
1069 }
1070 }
1071 count = members.size();
1072 },
1073 certs::ldapServiceName, certs::ldapObjectPath,
1074 certs::dbusObjManagerIntf, "GetManagedObjects");
George Liu0fda0f12021-11-16 10:06:17 +08001075 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001076
1077 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001078 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001079 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001080 [&app](const crow::Request& req,
1081 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001082 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001083 {
1084 return;
1085 }
1086 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001087
Ed Tanous002d39b2022-05-31 08:59:27 -07001088 if (certFileBody.empty())
1089 {
1090 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1091 messages::unrecognizedRequestBody(asyncResp->res);
1092 return;
1093 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001094
Ed Tanous002d39b2022-05-31 08:59:27 -07001095 std::shared_ptr<CertificateFile> certFile =
1096 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001097
Ed Tanous002d39b2022-05-31 08:59:27 -07001098 crow::connections::systemBus->async_method_call(
1099 [asyncResp, certFile](const boost::system::error_code ec,
1100 const std::string& objectPath) {
1101 if (ec)
1102 {
1103 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1104 messages::internalError(asyncResp->res);
1105 return;
1106 }
1107 long certId = getIDFromURL(objectPath);
1108 if (certId < 0)
1109 {
1110 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
1111 messages::internalError(asyncResp->res);
1112 return;
1113 }
1114 std::string certURL =
1115 "/redfish/v1/AccountService/LDAP/Certificates/" +
1116 std::to_string(certId);
1117 getCertificateProperties(asyncResp, objectPath,
1118 certs::ldapServiceName, certId, certURL,
1119 "LDAP Certificate");
1120 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1121 << certFile->getCertFilePath();
1122 },
1123 certs::ldapServiceName, certs::ldapObjectPath,
1124 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1125 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001126} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001127
1128/**
1129 * Certificate resource describes a certificate used to prove the identity
1130 * of a component, account or service.
1131 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001132inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001133{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001134 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001135 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001136 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001137 [&app](const crow::Request& req,
1138 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1139 const std::string&) {
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 long id = getIDFromURL(req.url);
1145 if (id < 0)
1146 {
1147 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1148 messages::internalError(asyncResp->res);
1149 return;
1150 }
1151 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1152 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1153 std::to_string(id);
1154 std::string objectPath = certs::ldapObjectPath;
1155 objectPath += "/";
1156 objectPath += std::to_string(id);
1157 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1158 id, certURL, "LDAP Certificate");
1159 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001160} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001161/**
1162 * Collection of TrustStoreCertificate certificates
1163 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001164inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001165{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001166 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001167 .privileges(redfish::privileges::getCertificate)
Ed Tanous002d39b2022-05-31 08:59:27 -07001168 .methods(boost::beast::http::verb::get)(
1169 [&app](const crow::Request& req,
1170 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001171 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001172 {
1173 return;
1174 }
1175
1176 asyncResp->res.jsonValue["@odata.id"] =
1177 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1178 asyncResp->res.jsonValue["@odata.type"] =
1179 "#CertificateCollection.CertificateCollection";
1180 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1181 asyncResp->res.jsonValue["Description"] =
1182 "A Collection of TrustStore certificate instances";
1183
1184 crow::connections::systemBus->async_method_call(
1185 [asyncResp](const boost::system::error_code ec,
1186 const dbus::utility::ManagedObjectType& certs) {
1187 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001188 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001189 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1190 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001191 return;
1192 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001193 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
1194 members = nlohmann::json::array();
1195 for (const auto& cert : certs)
1196 {
1197 long id = getIDFromURL(cert.first.str);
1198 if (id >= 0)
1199 {
1200 nlohmann::json::object_t member;
1201 member["@odata.id"] =
1202 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1203 std::to_string(id);
1204 members.push_back(std::move(member));
1205 }
1206 }
1207 asyncResp->res.jsonValue["Members@odata.count"] = members.size();
1208 },
1209 certs::authorityServiceName, certs::authorityObjectPath,
1210 certs::dbusObjManagerIntf, "GetManagedObjects");
George Liu0fda0f12021-11-16 10:06:17 +08001211 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001212
1213 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001214 .privileges(redfish::privileges::postCertificateCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001215 .methods(boost::beast::http::verb::post)(
1216 [&app](const crow::Request& req,
1217 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001218 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001219 {
1220 return;
1221 }
1222 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1223
1224 if (certFileBody.empty())
1225 {
1226 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1227 messages::unrecognizedRequestBody(asyncResp->res);
1228 return;
1229 }
1230
1231 std::shared_ptr<CertificateFile> certFile =
1232 std::make_shared<CertificateFile>(certFileBody);
1233 crow::connections::systemBus->async_method_call(
1234 [asyncResp, certFile](const boost::system::error_code ec,
1235 const std::string& objectPath) {
1236 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001237 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001238 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1239 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001240 return;
1241 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001242 long certId = getIDFromURL(objectPath);
1243 if (certId < 0)
George Liu0fda0f12021-11-16 10:06:17 +08001244 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001245 BMCWEB_LOG_ERROR << "Invalid objectPath value" << objectPath;
1246 messages::internalError(asyncResp->res);
George Liu0fda0f12021-11-16 10:06:17 +08001247 return;
1248 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001249 std::string certURL =
1250 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1251 std::to_string(certId);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001252
Ed Tanous002d39b2022-05-31 08:59:27 -07001253 getCertificateProperties(asyncResp, objectPath,
1254 certs::authorityServiceName, certId,
1255 certURL, "TrustStore Certificate");
1256 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1257 << certFile->getCertFilePath();
1258 },
1259 certs::authorityServiceName, certs::authorityObjectPath,
1260 certs::certInstallIntf, "Install", certFile->getCertFilePath());
George Liu0fda0f12021-11-16 10:06:17 +08001261 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001262} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001263
1264/**
1265 * Certificate resource describes a certificate used to prove the identity
1266 * of a component, account or service.
1267 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001268inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001269{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001270 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001271 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001272 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001273 [&app](const crow::Request& req,
1274 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1275 const std::string&) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001276 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001277 {
1278 return;
1279 }
1280 long id = getIDFromURL(req.url);
1281 if (id < 0)
1282 {
1283 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1284 messages::internalError(asyncResp->res);
1285 return;
1286 }
1287 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1288 << std::to_string(id);
1289 std::string certURL =
1290 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1291 std::to_string(id);
1292 std::string objectPath = certs::authorityObjectPath;
1293 objectPath += "/";
1294 objectPath += std::to_string(id);
1295 getCertificateProperties(asyncResp, objectPath,
1296 certs::authorityServiceName, id, certURL,
1297 "TrustStore Certificate");
1298 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001299
1300 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001301 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001302 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001303 [&app](const crow::Request& req,
1304 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1305 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001306 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001307 {
1308 return;
1309 }
1310 if (param.empty())
1311 {
1312 messages::internalError(asyncResp->res);
1313 return;
1314 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001315
Ed Tanous002d39b2022-05-31 08:59:27 -07001316 long id = getIDFromURL(req.url);
1317 if (id < 0)
1318 {
1319 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1320 messages::resourceNotFound(asyncResp->res, "TrustStore Certificate",
1321 std::string(req.url));
1322 return;
1323 }
1324 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1325 << std::to_string(id);
1326 std::string certPath = certs::authorityObjectPath;
1327 certPath += "/";
1328 certPath += std::to_string(id);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001329
Ed Tanous002d39b2022-05-31 08:59:27 -07001330 crow::connections::systemBus->async_method_call(
1331 [asyncResp, id](const boost::system::error_code ec) {
1332 if (ec)
1333 {
1334 messages::resourceNotFound(asyncResp->res,
1335 "TrustStore Certificate",
1336 std::to_string(id));
1337 return;
1338 }
1339 BMCWEB_LOG_INFO << "Certificate deleted";
1340 asyncResp->res.result(boost::beast::http::status::no_content);
1341 },
1342 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1343 "Delete");
1344 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001345} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001346} // namespace redfish