blob: 23e3eadcebce81ecaed7bca8e88ed5b4508b8dd5 [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001#pragma once
2
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003#include <app.hpp>
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +02004#include <boost/convert.hpp>
5#include <boost/convert/strtol.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -08006#include <dbus_utility.hpp>
Ed Tanoused398212021-06-09 17:05:54 -07007#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05008
Marri Devender Rao5968cae2019-01-21 10:27:12 -06009namespace redfish
10{
11namespace certs
12{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050013constexpr char const* httpsObjectPath =
Marri Devender Rao5968cae2019-01-21 10:27:12 -060014 "/xyz/openbmc_project/certs/server/https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050015constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
16constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
17constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
18constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
19constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
20constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
21constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
22constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060023 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050024constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060025 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050026constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050027 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050028constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050029 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060030} // namespace certs
31
32/**
33 * The Certificate schema defines a Certificate Service which represents the
34 * actions available to manage certificates and links to where certificates
35 * are installed.
36 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060037
John Edward Broadbent7e860f12021-04-08 15:57:16 -070038// TODO: Issue#61 No entries are available for Certificate
39// service at https://www.dmtf.org/standards/redfish
40// "redfish standard registries". Need to modify after DMTF
41// publish Privilege details for certificate service
42
43inline void requestRoutesCertificateService(App& app)
44{
45 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
Ed Tanoused398212021-06-09 17:05:54 -070046 .privileges(redfish::privileges::getCertificateService)
Abhishek Patel72048782021-06-02 09:53:24 -050047 .methods(
48 boost::beast::http::verb::
49 get)([](const crow::Request& req,
50 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
51 asyncResp->res.jsonValue = {
52 {"@odata.type",
53 "#CertificateService.v1_0_0.CertificateService"},
54 {"@odata.id", "/redfish/v1/CertificateService"},
55 {"Id", "CertificateService"},
56 {"Name", "Certificate Service"},
57 {"Description", "Actions available to manage certificates"}};
58 // /redfish/v1/CertificateService/CertificateLocations is something
59 // only ConfigureManager can access then only display when the user
60 // has permissions ConfigureManager
61 Privileges effectiveUserPrivileges =
62 redfish::getUserPrivileges(req.userRole);
63 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
64 effectiveUserPrivileges))
65 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -070066 asyncResp->res.jsonValue["CertificateLocations"] = {
67 {"@odata.id",
68 "/redfish/v1/CertificateService/CertificateLocations"}};
Abhishek Patel72048782021-06-02 09:53:24 -050069 }
George Liu0fda0f12021-11-16 10:06:17 +080070 asyncResp->res
71 .jsonValue["Actions"]
72 ["#CertificateService.ReplaceCertificate"] = {
73 {"target",
74 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate"},
75 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
Abhishek Patel72048782021-06-02 09:53:24 -050076 asyncResp->res
77 .jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
George Liu0fda0f12021-11-16 10:06:17 +080078 {"target",
79 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR"}};
Abhishek Patel72048782021-06-02 09:53:24 -050080 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -070081} // requestRoutesCertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060082
Marri Devender Rao5968cae2019-01-21 10:27:12 -060083/**
84 * @brief Find the ID specified in the URL
85 * Finds the numbers specified after the last "/" in the URL and returns.
86 * @param[in] path URL
87 * @return -1 on failure and number on success
88 */
Ed Tanous23a21a12020-07-25 04:45:05 +000089inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060090{
Ed Tanousf23b7292020-10-15 09:41:17 -070091 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -060092 if (found == std::string::npos)
93 {
94 return -1;
95 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020096
Marri Devender Rao5968cae2019-01-21 10:27:12 -060097 if ((found + 1) < url.length())
98 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -060099 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200100
101 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600102 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200103
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600104 return -1;
105}
106
zhanghch058d1b46d2021-04-01 11:18:24 +0800107inline std::string getCertificateFromReqBody(
108 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
109 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200110{
111 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
112
113 if (reqJson.is_discarded())
114 {
115 // We did not receive JSON request, proceed as it is RAW data
116 return req.body;
117 }
118
119 std::string certificate;
120 std::optional<std::string> certificateType = "PEM";
121
122 if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
123 certificate, "CertificateType", certificateType))
124 {
125 BMCWEB_LOG_ERROR << "Required parameters are missing";
126 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -0700127 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200128 }
129
130 if (*certificateType != "PEM")
131 {
132 messages::propertyValueNotInList(asyncResp->res, *certificateType,
133 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -0700134 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200135 }
136
137 return certificate;
138}
139
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600140/**
141 * Class to create a temporary certificate file for uploading to system
142 */
143class CertificateFile
144{
145 public:
146 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500147 CertificateFile(const CertificateFile&) = delete;
148 CertificateFile& operator=(const CertificateFile&) = delete;
149 CertificateFile(CertificateFile&&) = delete;
150 CertificateFile& operator=(CertificateFile&&) = delete;
151 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600152 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700153 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700154 'e', 'r', 't', 's', '.', 'X',
155 'X', 'X', 'X', 'X', 'X', '\0'};
156 char* tempDirectory = mkdtemp(dirTemplate.data());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600157 if (tempDirectory)
158 {
159 certDirectory = tempDirectory;
160 certificateFile = certDirectory / "cert.pem";
161 std::ofstream out(certificateFile, std::ofstream::out |
162 std::ofstream::binary |
163 std::ofstream::trunc);
164 out << certString;
165 out.close();
166 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
167 }
168 }
169 ~CertificateFile()
170 {
171 if (std::filesystem::exists(certDirectory))
172 {
173 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
Ed Tanous23a21a12020-07-25 04:45:05 +0000174 std::error_code ec;
175 std::filesystem::remove_all(certDirectory, ec);
176 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600177 {
178 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
179 << certDirectory;
180 }
181 }
182 }
183 std::string getCertFilePath()
184 {
185 return certificateFile;
186 }
187
188 private:
189 std::filesystem::path certificateFile;
190 std::filesystem::path certDirectory;
191};
192
Marri Devender Rao30215812019-03-18 08:59:21 -0500193static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
194/**
195 * @brief Read data from CSR D-bus object and set to response
196 *
197 * @param[in] asyncResp Shared pointer to the response message
198 * @param[in] certURI Link to certifiate collection URI
199 * @param[in] service D-Bus service name
200 * @param[in] certObjPath certificate D-Bus object path
201 * @param[in] csrObjPath CSR D-Bus object path
202 * @return None
203 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800204static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500205 const std::string& certURI, const std::string& service,
206 const std::string& certObjPath,
207 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500208{
209 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
210 << " CSRObjectPath=" << csrObjPath
211 << " service=" << service;
212 crow::connections::systemBus->async_method_call(
213 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500214 const std::string& csr) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500215 if (ec)
216 {
217 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
218 messages::internalError(asyncResp->res);
219 return;
220 }
221 if (csr.empty())
222 {
223 BMCWEB_LOG_ERROR << "CSR read is empty";
224 messages::internalError(asyncResp->res);
225 return;
226 }
227 asyncResp->res.jsonValue["CSRString"] = csr;
228 asyncResp->res.jsonValue["CertificateCollection"] = {
229 {"@odata.id", certURI}};
230 },
231 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
232}
233
234/**
235 * Action to Generate CSR
236 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700237inline void requestRoutesCertificateActionGenerateCSR(App& app)
Marri Devender Rao30215812019-03-18 08:59:21 -0500238{
George Liu0fda0f12021-11-16 10:06:17 +0800239 BMCWEB_ROUTE(
240 app,
241 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
Ed Tanoused398212021-06-09 17:05:54 -0700242 // Incorrect Privilege; Should be ConfigureManager
243 //.privileges(redfish::privileges::postCertificateService)
Ed Tanous432a8902021-06-14 15:28:56 -0700244 .privileges({{"ConfigureComponents"}})
George Liu0fda0f12021-11-16 10:06:17 +0800245 .methods(
246 boost::beast::http::verb::
247 post)([](const crow::Request& req,
248 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
249 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500250
George Liu0fda0f12021-11-16 10:06:17 +0800251 // Required parameters
252 std::string city;
253 std::string commonName;
254 std::string country;
255 std::string organization;
256 std::string organizationalUnit;
257 std::string state;
258 nlohmann::json certificateCollection;
zhanghch058d1b46d2021-04-01 11:18:24 +0800259
George Liu0fda0f12021-11-16 10:06:17 +0800260 // Optional parameters
261 std::optional<std::vector<std::string>> optAlternativeNames =
262 std::vector<std::string>();
263 std::optional<std::string> optContactPerson = "";
264 std::optional<std::string> optChallengePassword = "";
265 std::optional<std::string> optEmail = "";
266 std::optional<std::string> optGivenName = "";
267 std::optional<std::string> optInitials = "";
268 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
269 std::optional<std::string> optKeyCurveId = "secp384r1";
270 std::optional<std::string> optKeyPairAlgorithm = "EC";
271 std::optional<std::vector<std::string>> optKeyUsage =
272 std::vector<std::string>();
273 std::optional<std::string> optSurname = "";
274 std::optional<std::string> optUnstructuredName = "";
275 if (!json_util::readJson(
276 req, asyncResp->res, "City", city, "CommonName", commonName,
277 "ContactPerson", optContactPerson, "Country", country,
278 "Organization", organization, "OrganizationalUnit",
279 organizationalUnit, "State", state, "CertificateCollection",
280 certificateCollection, "AlternativeNames",
281 optAlternativeNames, "ChallengePassword",
282 optChallengePassword, "Email", optEmail, "GivenName",
283 optGivenName, "Initials", optInitials, "KeyBitLength",
284 optKeyBitLength, "KeyCurveId", optKeyCurveId,
285 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
286 optKeyUsage, "Surname", optSurname, "UnstructuredName",
287 optUnstructuredName))
288 {
289 return;
290 }
291
292 // bmcweb has no way to store or decode a private key challenge
293 // password, which will likely cause bmcweb to crash on startup
294 // if this is not set on a post so not allowing the user to set
295 // value
Ed Tanous26f69762022-01-25 09:49:11 -0800296 if (!optChallengePassword->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800297 {
298 messages::actionParameterNotSupported(
299 asyncResp->res, "GenerateCSR", "ChallengePassword");
300 return;
301 }
302
303 std::string certURI;
304 if (!redfish::json_util::readJson(certificateCollection,
305 asyncResp->res, "@odata.id",
306 certURI))
307 {
308 return;
309 }
310
311 std::string objectPath;
312 std::string service;
313 if (boost::starts_with(
314 certURI,
315 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
316 {
317 objectPath = certs::httpsObjectPath;
318 service = certs::httpsServiceName;
319 }
320 else if (boost::starts_with(
321 certURI,
322 "/redfish/v1/AccountService/LDAP/Certificates"))
323 {
324 objectPath = certs::ldapObjectPath;
325 service = certs::ldapServiceName;
326 }
327 else
328 {
329 messages::actionParameterNotSupported(
330 asyncResp->res, "CertificateCollection", "GenerateCSR");
331 return;
332 }
333
334 // supporting only EC and RSA algorithm
335 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
336 {
337 messages::actionParameterNotSupported(
338 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
339 return;
340 }
341
342 // supporting only 2048 key bit length for RSA algorithm due to
343 // time consumed in generating private key
344 if (*optKeyPairAlgorithm == "RSA" &&
345 *optKeyBitLength != rsaKeyBitLength)
346 {
347 messages::propertyValueNotInList(
348 asyncResp->res, std::to_string(*optKeyBitLength),
349 "KeyBitLength");
350 return;
351 }
352
353 // validate KeyUsage supporting only 1 type based on URL
354 if (boost::starts_with(
355 certURI,
356 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
357 {
Ed Tanous26f69762022-01-25 09:49:11 -0800358 if (optKeyUsage->empty())
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700359 {
George Liu0fda0f12021-11-16 10:06:17 +0800360 optKeyUsage->push_back("ServerAuthentication");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700361 }
George Liu0fda0f12021-11-16 10:06:17 +0800362 else if (optKeyUsage->size() == 1)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700363 {
George Liu0fda0f12021-11-16 10:06:17 +0800364 if ((*optKeyUsage)[0] != "ServerAuthentication")
365 {
366 messages::propertyValueNotInList(
367 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
368 return;
369 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700370 }
371 else
372 {
373 messages::actionParameterNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800374 asyncResp->res, "KeyUsage", "GenerateCSR");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700375 return;
376 }
George Liu0fda0f12021-11-16 10:06:17 +0800377 }
378 else if (boost::starts_with(
379 certURI,
380 "/redfish/v1/AccountService/LDAP/Certificates"))
381 {
Ed Tanous26f69762022-01-25 09:49:11 -0800382 if (optKeyUsage->empty())
George Liu0fda0f12021-11-16 10:06:17 +0800383 {
384 optKeyUsage->push_back("ClientAuthentication");
385 }
386 else if (optKeyUsage->size() == 1)
387 {
388 if ((*optKeyUsage)[0] != "ClientAuthentication")
389 {
390 messages::propertyValueNotInList(
391 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
392 return;
393 }
394 }
395 else
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700396 {
397 messages::actionParameterNotSupported(
George Liu0fda0f12021-11-16 10:06:17 +0800398 asyncResp->res, "KeyUsage", "GenerateCSR");
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700399 return;
400 }
George Liu0fda0f12021-11-16 10:06:17 +0800401 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500402
George Liu0fda0f12021-11-16 10:06:17 +0800403 // Only allow one CSR matcher at a time so setting retry
404 // time-out and timer expiry to 10 seconds for now.
405 static const int timeOut = 10;
406 if (csrMatcher)
407 {
408 messages::serviceTemporarilyUnavailable(
409 asyncResp->res, std::to_string(timeOut));
410 return;
411 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500412
George Liu0fda0f12021-11-16 10:06:17 +0800413 // Make this static so it survives outside this method
414 static boost::asio::steady_timer timeout(*req.ioService);
415 timeout.expires_after(std::chrono::seconds(timeOut));
416 timeout.async_wait(
417 [asyncResp](const boost::system::error_code& ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700418 csrMatcher = nullptr;
419 if (ec)
420 {
421 // operation_aborted is expected if timer is canceled
422 // before completion.
423 if (ec != boost::asio::error::operation_aborted)
424 {
425 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
426 }
427 return;
428 }
429 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
430 messages::internalError(asyncResp->res);
431 });
432
George Liu0fda0f12021-11-16 10:06:17 +0800433 // create a matcher to wait on CSR object
434 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
435 std::string match("type='signal',"
436 "interface='org.freedesktop.DBus.ObjectManager',"
437 "path='" +
438 objectPath +
439 "',"
440 "member='InterfacesAdded'");
441 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
442 *crow::connections::systemBus, match,
443 [asyncResp, service, objectPath,
444 certURI](sdbusplus::message::message& m) {
445 timeout.cancel();
446 if (m.is_method_error())
447 {
448 BMCWEB_LOG_ERROR << "Dbus method error!!!";
449 messages::internalError(asyncResp->res);
450 return;
451 }
Ed Tanous168e20c2021-12-13 14:39:53 -0800452 std::vector<std::pair<
453 std::string,
454 std::vector<std::pair<std::string,
455 dbus::utility::DbusVariantType>>>>
George Liu0fda0f12021-11-16 10:06:17 +0800456 interfacesProperties;
457 sdbusplus::message::object_path csrObjectPath;
458 m.read(csrObjectPath, interfacesProperties);
459 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
460 for (auto& interface : interfacesProperties)
461 {
462 if (interface.first == "xyz.openbmc_project.Certs.CSR")
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700463 {
George Liu0fda0f12021-11-16 10:06:17 +0800464 getCSR(asyncResp, certURI, service, objectPath,
465 csrObjectPath.str);
466 break;
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700467 }
George Liu0fda0f12021-11-16 10:06:17 +0800468 }
469 });
470 crow::connections::systemBus->async_method_call(
Ed Tanous914e2d52022-01-07 11:38:34 -0800471 [asyncResp](const boost::system::error_code ec,
George Liu0fda0f12021-11-16 10:06:17 +0800472 const std::string&) {
473 if (ec)
474 {
475 BMCWEB_LOG_ERROR << "DBUS response error: "
476 << ec.message();
477 messages::internalError(asyncResp->res);
478 return;
479 }
480 },
481 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
482 "GenerateCSR", *optAlternativeNames, *optChallengePassword,
483 city, commonName, *optContactPerson, country, *optEmail,
484 *optGivenName, *optInitials, *optKeyBitLength, *optKeyCurveId,
485 *optKeyPairAlgorithm, *optKeyUsage, organization,
486 organizationalUnit, state, *optSurname, *optUnstructuredName);
487 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700488} // requestRoutesCertificateActionGenerateCSR
Marri Devender Rao30215812019-03-18 08:59:21 -0500489
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600490/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500491 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600492 *
493 * @param[in] asyncResp Shared pointer to the response message
494 * @param[in] str Issuer/Subject value in key=value pairs
495 * @param[in] type Issuer/Subject
496 * @return None
497 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500498static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600499 const std::string_view value)
500{
501 // example: O=openbmc-project.xyz,CN=localhost
502 std::string_view::iterator i = value.begin();
503 while (i != value.end())
504 {
505 std::string_view::iterator tokenBegin = i;
506 while (i != value.end() && *i != '=')
507 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530508 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600509 }
510 if (i == value.end())
511 {
512 break;
513 }
Ed Tanous271584a2019-07-09 16:24:22 -0700514 const std::string_view key(tokenBegin,
515 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530516 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600517 tokenBegin = i;
518 while (i != value.end() && *i != ',')
519 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530520 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600521 }
Ed Tanous271584a2019-07-09 16:24:22 -0700522 const std::string_view val(tokenBegin,
523 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600524 if (key == "L")
525 {
526 out["City"] = val;
527 }
528 else if (key == "CN")
529 {
530 out["CommonName"] = val;
531 }
532 else if (key == "C")
533 {
534 out["Country"] = val;
535 }
536 else if (key == "O")
537 {
538 out["Organization"] = val;
539 }
540 else if (key == "OU")
541 {
542 out["OrganizationalUnit"] = val;
543 }
544 else if (key == "ST")
545 {
546 out["State"] = val;
547 }
548 // skip comma character
549 if (i != value.end())
550 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530551 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600552 }
553 }
554}
555
556/**
557 * @brief Retrieve the certificates properties and append to the response
558 * message
559 *
560 * @param[in] asyncResp Shared pointer to the response message
561 * @param[in] objectPath Path of the D-Bus service object
562 * @param[in] certId Id of the certificate
563 * @param[in] certURL URL of the certificate object
564 * @param[in] name name of the certificate
565 * @return None
566 */
567static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800568 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
569 const std::string& objectPath, const std::string& service, long certId,
570 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600571{
Ed Tanous168e20c2021-12-13 14:39:53 -0800572 using PropertiesMap =
573 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600574 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
575 << " certId=" << certId << " certURl=" << certURL;
576 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600577 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500578 const PropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600579 if (ec)
580 {
581 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500582 messages::resourceNotFound(asyncResp->res, name,
583 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600584 return;
585 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600586 asyncResp->res.jsonValue = {
587 {"@odata.id", certURL},
588 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600589 {"Id", std::to_string(certId)},
590 {"Name", name},
591 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500592 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600593 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600594 if (property.first == "CertificateString")
595 {
596 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500597 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600598 std::get_if<std::string>(&property.second);
599 if (value)
600 {
601 asyncResp->res.jsonValue["CertificateString"] = *value;
602 }
603 }
604 else if (property.first == "KeyUsage")
605 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500606 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600607 asyncResp->res.jsonValue["KeyUsage"];
608 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500609 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600610 std::get_if<std::vector<std::string>>(&property.second);
611 if (value)
612 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500613 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600614 {
615 keyUsage.push_back(usage);
616 }
617 }
618 }
619 else if (property.first == "Issuer")
620 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500621 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600622 std::get_if<std::string>(&property.second);
623 if (value)
624 {
625 updateCertIssuerOrSubject(
626 asyncResp->res.jsonValue["Issuer"], *value);
627 }
628 }
629 else if (property.first == "Subject")
630 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500631 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600632 std::get_if<std::string>(&property.second);
633 if (value)
634 {
635 updateCertIssuerOrSubject(
636 asyncResp->res.jsonValue["Subject"], *value);
637 }
638 }
639 else if (property.first == "ValidNotAfter")
640 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500641 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600642 std::get_if<uint64_t>(&property.second);
643 if (value)
644 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600645 asyncResp->res.jsonValue["ValidNotAfter"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800646 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600647 }
648 }
649 else if (property.first == "ValidNotBefore")
650 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500651 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600652 std::get_if<uint64_t>(&property.second);
653 if (value)
654 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600655 asyncResp->res.jsonValue["ValidNotBefore"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800656 crow::utility::getDateTimeUint(*value);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600657 }
658 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600659 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600660 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600661 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600662 service, objectPath, certs::dbusPropIntf, "GetAll",
663 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600664}
665
666using GetObjectType =
667 std::vector<std::pair<std::string, std::vector<std::string>>>;
668
669/**
670 * Action to replace an existing certificate
671 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700672inline void requestRoutesCertificateActionsReplaceCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600673{
George Liu0fda0f12021-11-16 10:06:17 +0800674 BMCWEB_ROUTE(
675 app,
676 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700677 .privileges(redfish::privileges::postCertificateService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700678 .methods(
679 boost::beast::http::verb::
680 post)([](const crow::Request& req,
681 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
682 std::string certificate;
683 nlohmann::json certificateUri;
684 std::optional<std::string> certificateType = "PEM";
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600685
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700686 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
687 certificate, "CertificateUri",
688 certificateUri, "CertificateType",
689 certificateType))
690 {
691 BMCWEB_LOG_ERROR << "Required parameters are missing";
692 messages::internalError(asyncResp->res);
693 return;
694 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800695
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700696 if (!certificateType)
697 {
698 // should never happen, but it never hurts to be paranoid.
699 return;
700 }
701 if (certificateType != "PEM")
702 {
703 messages::actionParameterNotSupported(
704 asyncResp->res, "CertificateType", "ReplaceCertificate");
705 return;
706 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600707
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700708 std::string certURI;
709 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
710 "@odata.id", certURI))
711 {
712 messages::actionParameterMissing(
713 asyncResp->res, "ReplaceCertificate", "CertificateUri");
714 return;
715 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600716
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700717 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
718 long id = getIDFromURL(certURI);
719 if (id < 0)
720 {
721 messages::actionParameterValueFormatError(
722 asyncResp->res, certURI, "CertificateUri",
723 "ReplaceCertificate");
724 return;
725 }
726 std::string objectPath;
727 std::string name;
728 std::string service;
George Liu0fda0f12021-11-16 10:06:17 +0800729 if (boost::starts_with(
730 certURI,
731 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700732 {
733 objectPath = std::string(certs::httpsObjectPath) + "/" +
734 std::to_string(id);
735 name = "HTTPS certificate";
736 service = certs::httpsServiceName;
737 }
738 else if (boost::starts_with(
739 certURI,
740 "/redfish/v1/AccountService/LDAP/Certificates/"))
741 {
742 objectPath = std::string(certs::ldapObjectPath) + "/" +
743 std::to_string(id);
744 name = "LDAP certificate";
745 service = certs::ldapServiceName;
746 }
747 else if (boost::starts_with(
748 certURI,
749 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
750 {
751 objectPath = std::string(certs::authorityObjectPath) + "/" +
752 std::to_string(id);
753 name = "TrustStore certificate";
754 service = certs::authorityServiceName;
755 }
756 else
757 {
758 messages::actionParameterNotSupported(
759 asyncResp->res, "CertificateUri", "ReplaceCertificate");
760 return;
761 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600762
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700763 std::shared_ptr<CertificateFile> certFile =
764 std::make_shared<CertificateFile>(certificate);
765 crow::connections::systemBus->async_method_call(
766 [asyncResp, certFile, objectPath, service, certURI, id,
767 name](const boost::system::error_code ec) {
768 if (ec)
769 {
770 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
771 messages::resourceNotFound(asyncResp->res, name,
772 std::to_string(id));
773 return;
774 }
775 getCertificateProperties(asyncResp, objectPath, service, id,
776 certURI, name);
777 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
778 << certFile->getCertFilePath();
779 },
780 service, objectPath, certs::certReplaceIntf, "Replace",
781 certFile->getCertFilePath());
782 });
783} // requestRoutesCertificateActionsReplaceCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600784
785/**
786 * Certificate resource describes a certificate used to prove the identity
787 * of a component, account or service.
788 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700789
790inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600791{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700792 BMCWEB_ROUTE(
793 app,
794 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700795 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700796 .methods(
797 boost::beast::http::verb::
798 get)([](const crow::Request& req,
799 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
800 const std::string& param) -> void {
801 if (param.empty())
802 {
803 messages::internalError(asyncResp->res);
804 return;
805 }
806 long id = getIDFromURL(req.url);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600807
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700808 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID="
809 << std::to_string(id);
810 std::string certURL =
811 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
812 std::to_string(id);
813 std::string objectPath = certs::httpsObjectPath;
814 objectPath += "/";
815 objectPath += std::to_string(id);
816 getCertificateProperties(asyncResp, objectPath,
817 certs::httpsServiceName, id, certURL,
818 "HTTPS Certificate");
819 });
820}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600821
822/**
823 * Collection of HTTPS certificates
824 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700825inline void requestRoutesHTTPSCertificateCollection(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600826{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700827 BMCWEB_ROUTE(app,
828 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700829 .privileges(redfish::privileges::getCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700830 .methods(
831 boost::beast::http::verb::
832 get)([](const crow::Request&,
833 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
834 asyncResp->res.jsonValue = {
835 {"@odata.id",
836 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
837 {"@odata.type", "#CertificateCollection.CertificateCollection"},
838 {"Name", "HTTPS Certificates Collection"},
839 {"Description", "A Collection of HTTPS certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +0800840
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700841 crow::connections::systemBus->async_method_call(
842 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800843 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700844 if (ec)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600845 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700846 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
847 messages::internalError(asyncResp->res);
848 return;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600849 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700850 nlohmann::json& members =
851 asyncResp->res.jsonValue["Members"];
852 members = nlohmann::json::array();
853 for (const auto& cert : certs)
854 {
855 long id = getIDFromURL(cert.first.str);
856 if (id >= 0)
857 {
858 members.push_back(
859 {{"@odata.id",
George Liu0fda0f12021-11-16 10:06:17 +0800860 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700861 std::to_string(id)}});
862 }
863 }
864 asyncResp->res.jsonValue["Members@odata.count"] =
865 members.size();
866 },
867 certs::httpsServiceName, certs::httpsObjectPath,
868 certs::dbusObjManagerIntf, "GetManagedObjects");
869 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600870
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700871 BMCWEB_ROUTE(app,
872 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -0700873 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +0800874 .methods(
875 boost::beast::http::verb::
876 post)([](const crow::Request& req,
877 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
878 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
zhanghch058d1b46d2021-04-01 11:18:24 +0800879
George Liu0fda0f12021-11-16 10:06:17 +0800880 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
881 {"Description", "HTTPS Certificate"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600882
George Liu0fda0f12021-11-16 10:06:17 +0800883 std::string certFileBody =
884 getCertificateFromReqBody(asyncResp, req);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200885
George Liu0fda0f12021-11-16 10:06:17 +0800886 if (certFileBody.empty())
887 {
888 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
889 messages::unrecognizedRequestBody(asyncResp->res);
890 return;
891 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700892
George Liu0fda0f12021-11-16 10:06:17 +0800893 std::shared_ptr<CertificateFile> certFile =
894 std::make_shared<CertificateFile>(certFileBody);
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700895
George Liu0fda0f12021-11-16 10:06:17 +0800896 crow::connections::systemBus->async_method_call(
897 [asyncResp, certFile](const boost::system::error_code ec,
898 const std::string& objectPath) {
899 if (ec)
900 {
901 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
902 messages::internalError(asyncResp->res);
903 return;
904 }
905 long certId = getIDFromURL(objectPath);
906 if (certId < 0)
907 {
908 BMCWEB_LOG_ERROR << "Invalid objectPath value"
909 << objectPath;
910 messages::internalError(asyncResp->res);
911 return;
912 }
913 std::string certURL =
914 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
915 std::to_string(certId);
916 getCertificateProperties(asyncResp, objectPath,
917 certs::httpsServiceName, certId,
918 certURL, "HTTPS Certificate");
919 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
920 << certFile->getCertFilePath();
921 },
922 certs::httpsServiceName, certs::httpsObjectPath,
923 certs::certInstallIntf, "Install", certFile->getCertFilePath());
924 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700925} // requestRoutesHTTPSCertificateCollection
926
927/**
928 * @brief Retrieve the certificates installed list and append to the
929 * response
930 *
931 * @param[in] asyncResp Shared pointer to the response message
932 * @param[in] certURL Path of the certificate object
933 * @param[in] path Path of the D-Bus service object
934 * @return None
935 */
Ed Tanous4f48d5f2021-06-21 08:27:45 -0700936inline void
937 getCertificateLocations(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
938 const std::string& certURL, const std::string& path,
939 const std::string& service)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700940{
941 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
942 << " Path=" << path << " service= " << service;
943 crow::connections::systemBus->async_method_call(
944 [asyncResp, certURL](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800945 const dbus::utility::ManagedObjectType& certs) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700946 if (ec)
947 {
948 BMCWEB_LOG_WARNING
949 << "Certificate collection query failed: " << ec
950 << ", skipping " << certURL;
951 return;
952 }
953 nlohmann::json& links =
954 asyncResp->res.jsonValue["Links"]["Certificates"];
Ed Tanous9eb808c2022-01-25 10:19:23 -0800955 for (const auto& cert : certs)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700956 {
957 long id = getIDFromURL(cert.first.str);
958 if (id >= 0)
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200959 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700960 links.push_back(
961 {{"@odata.id", certURL + std::to_string(id)}});
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200962 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700963 }
964 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
965 links.size();
966 },
967 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
968}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600969
970/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600971 * The certificate location schema defines a resource that an administrator
972 * can use in order to locate all certificates installed on a given service.
973 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700974inline void requestRoutesCertificateLocations(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600975{
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700976 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
Ed Tanoused398212021-06-09 17:05:54 -0700977 .privileges(redfish::privileges::getCertificateLocations)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700978 .methods(
979 boost::beast::http::verb::
980 get)([](const crow::Request&,
981 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
982 asyncResp->res.jsonValue = {
983 {"@odata.id",
984 "/redfish/v1/CertificateService/CertificateLocations"},
985 {"@odata.type",
986 "#CertificateLocations.v1_0_0.CertificateLocations"},
987 {"Name", "Certificate Locations"},
988 {"Id", "CertificateLocations"},
989 {"Description",
990 "Defines a resource that an administrator can use in order to "
991 "locate all certificates installed on a given service"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600992
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700993 nlohmann::json& links =
994 asyncResp->res.jsonValue["Links"]["Certificates"];
995 links = nlohmann::json::array();
996 getCertificateLocations(
997 asyncResp,
998 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
999 certs::httpsObjectPath, certs::httpsServiceName);
1000 getCertificateLocations(
1001 asyncResp, "/redfish/v1/AccountService/LDAP/Certificates/",
1002 certs::ldapObjectPath, certs::ldapServiceName);
1003 getCertificateLocations(
1004 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1005 certs::authorityObjectPath, certs::authorityServiceName);
1006 });
1007}
1008// requestRoutesCertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001009
1010/**
1011 * Collection of LDAP certificates
1012 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001013inline void requestRoutesLDAPCertificateCollection(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001014{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001015 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001016 .privileges(redfish::privileges::getCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +08001017 .methods(
1018 boost::beast::http::verb::
1019 get)([](const crow::Request&,
1020 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1021 asyncResp->res.jsonValue = {
1022 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1023 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1024 {"Name", "LDAP Certificates Collection"},
1025 {"Description", "A Collection of LDAP certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001026
George Liu0fda0f12021-11-16 10:06:17 +08001027 crow::connections::systemBus->async_method_call(
1028 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001029 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001030 nlohmann::json& members =
1031 asyncResp->res.jsonValue["Members"];
1032 nlohmann::json& count =
1033 asyncResp->res.jsonValue["Members@odata.count"];
1034 members = nlohmann::json::array();
1035 count = 0;
1036 if (ec)
1037 {
1038 BMCWEB_LOG_WARNING << "LDAP certificate query failed: "
1039 << ec;
1040 return;
1041 }
1042 for (const auto& cert : certs)
1043 {
1044 long id = getIDFromURL(cert.first.str);
1045 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001046 {
George Liu0fda0f12021-11-16 10:06:17 +08001047 members.push_back(
1048 {{"@odata.id",
1049 "/redfish/v1/AccountService/LDAP/Certificates/" +
1050 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001051 }
George Liu0fda0f12021-11-16 10:06:17 +08001052 }
1053 count = members.size();
1054 },
1055 certs::ldapServiceName, certs::ldapObjectPath,
1056 certs::dbusObjManagerIntf, "GetManagedObjects");
1057 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001058
1059 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001060 .privileges(redfish::privileges::postCertificateCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001061 .methods(boost::beast::http::verb::post)(
1062 [](const crow::Request& req,
1063 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1064 std::string certFileBody =
1065 getCertificateFromReqBody(asyncResp, req);
1066
1067 if (certFileBody.empty())
Marri Devender Rao37cce912019-02-20 01:05:22 -06001068 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001069 BMCWEB_LOG_ERROR
1070 << "Cannot get certificate from request body.";
1071 messages::unrecognizedRequestBody(asyncResp->res);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001072 return;
1073 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001074
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001075 std::shared_ptr<CertificateFile> certFile =
1076 std::make_shared<CertificateFile>(certFileBody);
zhanghch058d1b46d2021-04-01 11:18:24 +08001077
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001078 crow::connections::systemBus->async_method_call(
1079 [asyncResp, certFile](const boost::system::error_code ec,
1080 const std::string& objectPath) {
1081 if (ec)
1082 {
1083 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1084 messages::internalError(asyncResp->res);
1085 return;
1086 }
1087 long certId = getIDFromURL(objectPath);
1088 if (certId < 0)
1089 {
1090 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1091 << objectPath;
1092 messages::internalError(asyncResp->res);
1093 return;
1094 }
1095 std::string certURL =
1096 "/redfish/v1/AccountService/LDAP/Certificates/" +
1097 std::to_string(certId);
1098 getCertificateProperties(asyncResp, objectPath,
1099 certs::ldapServiceName, certId,
1100 certURL, "LDAP Certificate");
1101 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1102 << certFile->getCertFilePath();
1103 },
1104 certs::ldapServiceName, certs::ldapObjectPath,
1105 certs::certInstallIntf, "Install",
1106 certFile->getCertFilePath());
1107 });
1108} // requestRoutesLDAPCertificateCollection
Marri Devender Rao37cce912019-02-20 01:05:22 -06001109
1110/**
1111 * Certificate resource describes a certificate used to prove the identity
1112 * of a component, account or service.
1113 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001114inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001115{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001116 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001117 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001118 .methods(boost::beast::http::verb::get)(
1119 [](const crow::Request& req,
1120 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1121 const std::string&) {
1122 long id = getIDFromURL(req.url);
1123 if (id < 0)
1124 {
1125 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1126 messages::internalError(asyncResp->res);
1127 return;
1128 }
1129 BMCWEB_LOG_DEBUG << "LDAP Certificate ID="
1130 << std::to_string(id);
1131 std::string certURL =
1132 "/redfish/v1/AccountService/LDAP/Certificates/" +
1133 std::to_string(id);
1134 std::string objectPath = certs::ldapObjectPath;
1135 objectPath += "/";
1136 objectPath += std::to_string(id);
1137 getCertificateProperties(asyncResp, objectPath,
1138 certs::ldapServiceName, id, certURL,
1139 "LDAP Certificate");
1140 });
1141} // requestRoutesLDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001142/**
1143 * Collection of TrustStoreCertificate certificates
1144 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001145inline void requestRoutesTrustStoreCertificateCollection(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001146{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001147 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001148 .privileges(redfish::privileges::getCertificate)
George Liu0fda0f12021-11-16 10:06:17 +08001149 .methods(
1150 boost::beast::http::verb::
1151 get)([](const crow::Request&,
1152 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1153 asyncResp->res.jsonValue = {
1154 {"@odata.id",
1155 "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1156 {"@odata.type", "#CertificateCollection.CertificateCollection"},
1157 {"Name", "TrustStore Certificates Collection"},
1158 {"Description",
1159 "A Collection of TrustStore certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001160
George Liu0fda0f12021-11-16 10:06:17 +08001161 crow::connections::systemBus->async_method_call(
1162 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001163 const dbus::utility::ManagedObjectType& certs) {
George Liu0fda0f12021-11-16 10:06:17 +08001164 if (ec)
1165 {
1166 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1167 messages::internalError(asyncResp->res);
1168 return;
1169 }
1170 nlohmann::json& members =
1171 asyncResp->res.jsonValue["Members"];
1172 members = nlohmann::json::array();
1173 for (const auto& cert : certs)
1174 {
1175 long id = getIDFromURL(cert.first.str);
1176 if (id >= 0)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001177 {
George Liu0fda0f12021-11-16 10:06:17 +08001178 members.push_back(
1179 {{"@odata.id",
1180 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1181 std::to_string(id)}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001182 }
George Liu0fda0f12021-11-16 10:06:17 +08001183 }
1184 asyncResp->res.jsonValue["Members@odata.count"] =
1185 members.size();
1186 },
1187 certs::authorityServiceName, certs::authorityObjectPath,
1188 certs::dbusObjManagerIntf, "GetManagedObjects");
1189 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001190
1191 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001192 .privileges(redfish::privileges::postCertificateCollection)
George Liu0fda0f12021-11-16 10:06:17 +08001193 .methods(
1194 boost::beast::http::verb::
1195 post)([](const crow::Request& req,
1196 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1197 std::string certFileBody =
1198 getCertificateFromReqBody(asyncResp, req);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001199
George Liu0fda0f12021-11-16 10:06:17 +08001200 if (certFileBody.empty())
1201 {
1202 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1203 messages::unrecognizedRequestBody(asyncResp->res);
1204 return;
1205 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001206
George Liu0fda0f12021-11-16 10:06:17 +08001207 std::shared_ptr<CertificateFile> certFile =
1208 std::make_shared<CertificateFile>(certFileBody);
1209 crow::connections::systemBus->async_method_call(
1210 [asyncResp, certFile](const boost::system::error_code ec,
1211 const std::string& objectPath) {
1212 if (ec)
1213 {
1214 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1215 messages::internalError(asyncResp->res);
1216 return;
1217 }
1218 long certId = getIDFromURL(objectPath);
1219 if (certId < 0)
1220 {
1221 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1222 << objectPath;
1223 messages::internalError(asyncResp->res);
1224 return;
1225 }
1226 std::string certURL =
1227 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1228 std::to_string(certId);
zhanghch058d1b46d2021-04-01 11:18:24 +08001229
George Liu0fda0f12021-11-16 10:06:17 +08001230 getCertificateProperties(
1231 asyncResp, objectPath, certs::authorityServiceName,
1232 certId, certURL, "TrustStore Certificate");
1233 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1234 << certFile->getCertFilePath();
1235 },
1236 certs::authorityServiceName, certs::authorityObjectPath,
1237 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1238 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001239} // requestRoutesTrustStoreCertificateCollection
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001240
1241/**
1242 * Certificate resource describes a certificate used to prove the identity
1243 * of a component, account or service.
1244 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001245inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001246{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001247 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001248 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001249 .methods(boost::beast::http::verb::get)(
1250 [](const crow::Request& req,
1251 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1252 const std::string&) {
1253 long id = getIDFromURL(req.url);
1254 if (id < 0)
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001255 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001256 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1257 messages::internalError(asyncResp->res);
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001258 return;
1259 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001260 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1261 << std::to_string(id);
1262 std::string certURL =
1263 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1264 std::to_string(id);
1265 std::string objectPath = certs::authorityObjectPath;
1266 objectPath += "/";
1267 objectPath += std::to_string(id);
1268 getCertificateProperties(asyncResp, objectPath,
1269 certs::authorityServiceName, id,
1270 certURL, "TrustStore Certificate");
1271 });
1272
1273 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001274 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001275 .methods(boost::beast::http::verb::delete_)(
1276 [](const crow::Request& req,
1277 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1278 const std::string& param) {
1279 if (param.empty())
1280 {
1281 messages::internalError(asyncResp->res);
1282 return;
1283 }
1284
1285 long id = getIDFromURL(req.url);
1286 if (id < 0)
1287 {
1288 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1289 messages::resourceNotFound(asyncResp->res,
1290 "TrustStore Certificate",
1291 std::string(req.url));
1292 return;
1293 }
1294 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1295 << std::to_string(id);
1296 std::string certPath = certs::authorityObjectPath;
1297 certPath += "/";
1298 certPath += std::to_string(id);
1299
1300 crow::connections::systemBus->async_method_call(
1301 [asyncResp, id](const boost::system::error_code ec) {
1302 if (ec)
1303 {
1304 messages::resourceNotFound(asyncResp->res,
1305 "TrustStore Certificate",
1306 std::to_string(id));
1307 return;
1308 }
1309 BMCWEB_LOG_INFO << "Certificate deleted";
1310 asyncResp->res.result(
1311 boost::beast::http::status::no_content);
1312 },
1313 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1314 "Delete");
1315 });
1316} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001317} // namespace redfish