blob: 7c10d6d919a7502d7781046827d43ce02101a40e [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001#pragma once
2
3#include "node.hpp"
4
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +02005#include <boost/convert.hpp>
6#include <boost/convert/strtol.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05007
Marri Devender Rao5968cae2019-01-21 10:27:12 -06008#include <variant>
9namespace 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 */
37class CertificateService : public Node
38{
39 public:
Ed Tanous52cc1122020-07-18 13:51:21 -070040 CertificateService(App& app) : Node(app, "/redfish/v1/CertificateService/")
Marri Devender Rao5968cae2019-01-21 10:27:12 -060041 {
42 // TODO: Issue#61 No entries are available for Certificate
Gunnar Mills4e0453b2020-07-08 14:00:30 -050043 // service at https://www.dmtf.org/standards/redfish
Marri Devender Rao5968cae2019-01-21 10:27:12 -060044 // "redfish standard registries". Need to modify after DMTF
45 // publish Privilege details for certificate service
46 entityPrivileges = {
47 {boost::beast::http::verb::get, {{"Login"}}},
48 {boost::beast::http::verb::head, {{"Login"}}},
49 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
50 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
51 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
52 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
53 }
54
55 private:
Ed Tanouscb13a392020-07-25 19:02:03 +000056 void doGet(crow::Response& res, const crow::Request&,
57 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -060058 {
59 res.jsonValue = {
60 {"@odata.type", "#CertificateService.v1_0_0.CertificateService"},
61 {"@odata.id", "/redfish/v1/CertificateService"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -060062 {"Id", "CertificateService"},
63 {"Name", "Certificate Service"},
64 {"Description", "Actions available to manage certificates"}};
65 res.jsonValue["CertificateLocations"] = {
66 {"@odata.id",
67 "/redfish/v1/CertificateService/CertificateLocations"}};
68 res.jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = {
69 {"target", "/redfish/v1/CertificateService/Actions/"
70 "CertificateService.ReplaceCertificate"},
71 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
Marri Devender Rao30215812019-03-18 08:59:21 -050072 res.jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
73 {"target", "/redfish/v1/CertificateService/Actions/"
74 "CertificateService.GenerateCSR"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -060075 res.end();
76 }
77}; // CertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060078
Marri Devender Rao5968cae2019-01-21 10:27:12 -060079/**
80 * @brief Find the ID specified in the URL
81 * Finds the numbers specified after the last "/" in the URL and returns.
82 * @param[in] path URL
83 * @return -1 on failure and number on success
84 */
Ed Tanous23a21a12020-07-25 04:45:05 +000085inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -060086{
Ed Tanousf23b7292020-10-15 09:41:17 -070087 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -060088 if (found == std::string::npos)
89 {
90 return -1;
91 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020092
Marri Devender Rao5968cae2019-01-21 10:27:12 -060093 if ((found + 1) < url.length())
94 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -060095 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020096
97 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -060098 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020099
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600100 return -1;
101}
102
Ed Tanous23a21a12020-07-25 04:45:05 +0000103inline std::string
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500104 getCertificateFromReqBody(const std::shared_ptr<AsyncResp>& asyncResp,
105 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200106{
107 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
108
109 if (reqJson.is_discarded())
110 {
111 // We did not receive JSON request, proceed as it is RAW data
112 return req.body;
113 }
114
115 std::string certificate;
116 std::optional<std::string> certificateType = "PEM";
117
118 if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
119 certificate, "CertificateType", certificateType))
120 {
121 BMCWEB_LOG_ERROR << "Required parameters are missing";
122 messages::internalError(asyncResp->res);
123 return std::string();
124 }
125
126 if (*certificateType != "PEM")
127 {
128 messages::propertyValueNotInList(asyncResp->res, *certificateType,
129 "CertificateType");
130 return std::string();
131 }
132
133 return certificate;
134}
135
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600136/**
137 * Class to create a temporary certificate file for uploading to system
138 */
139class CertificateFile
140{
141 public:
142 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500143 CertificateFile(const CertificateFile&) = delete;
144 CertificateFile& operator=(const CertificateFile&) = delete;
145 CertificateFile(CertificateFile&&) = delete;
146 CertificateFile& operator=(CertificateFile&&) = delete;
147 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600148 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700149 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700150 'e', 'r', 't', 's', '.', 'X',
151 'X', 'X', 'X', 'X', 'X', '\0'};
152 char* tempDirectory = mkdtemp(dirTemplate.data());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600153 if (tempDirectory)
154 {
155 certDirectory = tempDirectory;
156 certificateFile = certDirectory / "cert.pem";
157 std::ofstream out(certificateFile, std::ofstream::out |
158 std::ofstream::binary |
159 std::ofstream::trunc);
160 out << certString;
161 out.close();
162 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
163 }
164 }
165 ~CertificateFile()
166 {
167 if (std::filesystem::exists(certDirectory))
168 {
169 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
Ed Tanous23a21a12020-07-25 04:45:05 +0000170 std::error_code ec;
171 std::filesystem::remove_all(certDirectory, ec);
172 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600173 {
174 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
175 << certDirectory;
176 }
177 }
178 }
179 std::string getCertFilePath()
180 {
181 return certificateFile;
182 }
183
184 private:
185 std::filesystem::path certificateFile;
186 std::filesystem::path certDirectory;
187};
188
Marri Devender Rao30215812019-03-18 08:59:21 -0500189static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
190/**
191 * @brief Read data from CSR D-bus object and set to response
192 *
193 * @param[in] asyncResp Shared pointer to the response message
194 * @param[in] certURI Link to certifiate collection URI
195 * @param[in] service D-Bus service name
196 * @param[in] certObjPath certificate D-Bus object path
197 * @param[in] csrObjPath CSR D-Bus object path
198 * @return None
199 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500200static void getCSR(const std::shared_ptr<AsyncResp>& asyncResp,
201 const std::string& certURI, const std::string& service,
202 const std::string& certObjPath,
203 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500204{
205 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
206 << " CSRObjectPath=" << csrObjPath
207 << " service=" << service;
208 crow::connections::systemBus->async_method_call(
209 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500210 const std::string& csr) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500211 if (ec)
212 {
213 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
214 messages::internalError(asyncResp->res);
215 return;
216 }
217 if (csr.empty())
218 {
219 BMCWEB_LOG_ERROR << "CSR read is empty";
220 messages::internalError(asyncResp->res);
221 return;
222 }
223 asyncResp->res.jsonValue["CSRString"] = csr;
224 asyncResp->res.jsonValue["CertificateCollection"] = {
225 {"@odata.id", certURI}};
226 },
227 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
228}
229
230/**
231 * Action to Generate CSR
232 */
233class CertificateActionGenerateCSR : public Node
234{
235 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700236 CertificateActionGenerateCSR(App& app) :
Marri Devender Rao30215812019-03-18 08:59:21 -0500237 Node(app, "/redfish/v1/CertificateService/Actions/"
238 "CertificateService.GenerateCSR/")
239 {
240 entityPrivileges = {
241 {boost::beast::http::verb::get, {{"Login"}}},
242 {boost::beast::http::verb::head, {{"Login"}}},
243 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
244 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
245 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
246 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
247 }
248
249 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500250 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +0000251 const std::vector<std::string>&) override
Marri Devender Rao30215812019-03-18 08:59:21 -0500252 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700253 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500254 auto asyncResp = std::make_shared<AsyncResp>(res);
255 // Required parameters
256 std::string city;
257 std::string commonName;
258 std::string country;
259 std::string organization;
260 std::string organizationalUnit;
261 std::string state;
262 nlohmann::json certificateCollection;
263
264 // Optional parameters
265 std::optional<std::vector<std::string>> optAlternativeNames =
266 std::vector<std::string>();
267 std::optional<std::string> optContactPerson = "";
268 std::optional<std::string> optChallengePassword = "";
269 std::optional<std::string> optEmail = "";
270 std::optional<std::string> optGivenName = "";
271 std::optional<std::string> optInitials = "";
Ed Tanous2c70f802020-09-28 14:29:23 -0700272 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
Vernon Maueryaaf32062020-03-09 10:41:31 -0700273 std::optional<std::string> optKeyCurveId = "secp384r1";
Marri Devender Rao30215812019-03-18 08:59:21 -0500274 std::optional<std::string> optKeyPairAlgorithm = "EC";
275 std::optional<std::vector<std::string>> optKeyUsage =
276 std::vector<std::string>();
277 std::optional<std::string> optSurname = "";
278 std::optional<std::string> optUnstructuredName = "";
279 if (!json_util::readJson(
280 req, asyncResp->res, "City", city, "CommonName", commonName,
281 "ContactPerson", optContactPerson, "Country", country,
282 "Organization", organization, "OrganizationalUnit",
283 organizationalUnit, "State", state, "CertificateCollection",
284 certificateCollection, "AlternativeNames", optAlternativeNames,
285 "ChallengePassword", optChallengePassword, "Email", optEmail,
286 "GivenName", optGivenName, "Initials", optInitials,
287 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
288 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
289 optKeyUsage, "Surname", optSurname, "UnstructuredName",
290 optUnstructuredName))
291 {
292 return;
293 }
294
295 // bmcweb has no way to store or decode a private key challenge
296 // password, which will likely cause bmcweb to crash on startup if this
297 // is not set on a post so not allowing the user to set value
298 if (*optChallengePassword != "")
299 {
300 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
301 "ChallengePassword");
302 return;
303 }
304
305 std::string certURI;
306 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
307 "@odata.id", certURI))
308 {
309 return;
310 }
311
312 std::string objectPath;
313 std::string service;
314 if (boost::starts_with(
315 certURI,
316 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
317 {
318 objectPath = certs::httpsObjectPath;
319 service = certs::httpsServiceName;
320 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500321 else if (boost::starts_with(
322 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
323 {
324 objectPath = certs::ldapObjectPath;
325 service = certs::ldapServiceName;
326 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500327 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 time
343 // consumed in generating private key
344 if (*optKeyPairAlgorithm == "RSA" &&
Ed Tanous2c70f802020-09-28 14:29:23 -0700345 *optKeyBitLength != rsaKeyBitLength)
Marri Devender Rao30215812019-03-18 08:59:21 -0500346 {
347 messages::propertyValueNotInList(asyncResp->res,
348 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 {
358 if (optKeyUsage->size() == 0)
359 {
360 optKeyUsage->push_back("ServerAuthentication");
361 }
362 else if (optKeyUsage->size() == 1)
363 {
364 if ((*optKeyUsage)[0] != "ServerAuthentication")
365 {
366 messages::propertyValueNotInList(
367 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
368 return;
369 }
370 }
371 else
372 {
373 messages::actionParameterNotSupported(
374 asyncResp->res, "KeyUsage", "GenerateCSR");
375 return;
376 }
377 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500378 else if (boost::starts_with(
379 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
380 {
381 if (optKeyUsage->size() == 0)
382 {
383 optKeyUsage->push_back("ClientAuthentication");
384 }
385 else if (optKeyUsage->size() == 1)
386 {
387 if ((*optKeyUsage)[0] != "ClientAuthentication")
388 {
389 messages::propertyValueNotInList(
390 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
391 return;
392 }
393 }
394 else
395 {
396 messages::actionParameterNotSupported(
397 asyncResp->res, "KeyUsage", "GenerateCSR");
398 return;
399 }
400 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500401
402 // Only allow one CSR matcher at a time so setting retry time-out and
403 // timer expiry to 10 seconds for now.
Ed Tanous2c70f802020-09-28 14:29:23 -0700404 static const int timeOut = 10;
Marri Devender Rao30215812019-03-18 08:59:21 -0500405 if (csrMatcher)
406 {
Marri Devender Rao30215812019-03-18 08:59:21 -0500407 messages::serviceTemporarilyUnavailable(asyncResp->res,
Ed Tanous2c70f802020-09-28 14:29:23 -0700408 std::to_string(timeOut));
Marri Devender Rao30215812019-03-18 08:59:21 -0500409 return;
410 }
411
412 // Make this static so it survives outside this method
413 static boost::asio::steady_timer timeout(*req.ioService);
Ed Tanous2c70f802020-09-28 14:29:23 -0700414 timeout.expires_after(std::chrono::seconds(timeOut));
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500415 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500416 csrMatcher = nullptr;
417 if (ec)
418 {
419 // operation_aborted is expected if timer is canceled before
420 // completion.
421 if (ec != boost::asio::error::operation_aborted)
422 {
423 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
424 }
425 return;
426 }
427 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
428 messages::internalError(asyncResp->res);
429 });
430
431 // create a matcher to wait on CSR object
432 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
433 std::string match("type='signal',"
434 "interface='org.freedesktop.DBus.ObjectManager',"
435 "path='" +
436 objectPath +
437 "',"
438 "member='InterfacesAdded'");
439 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
440 *crow::connections::systemBus, match,
441 [asyncResp, service, objectPath,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500442 certURI](sdbusplus::message::message& m) {
Ed Tanous271584a2019-07-09 16:24:22 -0700443 timeout.cancel();
Marri Devender Rao30215812019-03-18 08:59:21 -0500444 if (m.is_method_error())
445 {
446 BMCWEB_LOG_ERROR << "Dbus method error!!!";
447 messages::internalError(asyncResp->res);
448 return;
449 }
450 std::vector<std::pair<
451 std::string, std::vector<std::pair<
452 std::string, std::variant<std::string>>>>>
453 interfacesProperties;
454 sdbusplus::message::object_path csrObjectPath;
455 m.read(csrObjectPath, interfacesProperties);
456 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500457 for (auto& interface : interfacesProperties)
Marri Devender Rao30215812019-03-18 08:59:21 -0500458 {
459 if (interface.first == "xyz.openbmc_project.Certs.CSR")
460 {
461 getCSR(asyncResp, certURI, service, objectPath,
462 csrObjectPath.str);
463 break;
464 }
465 }
466 });
467 crow::connections::systemBus->async_method_call(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500468 [asyncResp](const boost::system::error_code& ec,
Ed Tanouscb13a392020-07-25 19:02:03 +0000469 const std::string&) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500470 if (ec)
471 {
472 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
473 messages::internalError(asyncResp->res);
474 return;
475 }
476 },
477 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
478 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
479 commonName, *optContactPerson, country, *optEmail, *optGivenName,
480 *optInitials, *optKeyBitLength, *optKeyCurveId,
481 *optKeyPairAlgorithm, *optKeyUsage, organization,
482 organizationalUnit, state, *optSurname, *optUnstructuredName);
483 }
484}; // CertificateActionGenerateCSR
485
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600486/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500487 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600488 *
489 * @param[in] asyncResp Shared pointer to the response message
490 * @param[in] str Issuer/Subject value in key=value pairs
491 * @param[in] type Issuer/Subject
492 * @return None
493 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500494static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600495 const std::string_view value)
496{
497 // example: O=openbmc-project.xyz,CN=localhost
498 std::string_view::iterator i = value.begin();
499 while (i != value.end())
500 {
501 std::string_view::iterator tokenBegin = i;
502 while (i != value.end() && *i != '=')
503 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530504 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600505 }
506 if (i == value.end())
507 {
508 break;
509 }
Ed Tanous271584a2019-07-09 16:24:22 -0700510 const std::string_view key(tokenBegin,
511 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530512 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600513 tokenBegin = i;
514 while (i != value.end() && *i != ',')
515 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530516 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600517 }
Ed Tanous271584a2019-07-09 16:24:22 -0700518 const std::string_view val(tokenBegin,
519 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600520 if (key == "L")
521 {
522 out["City"] = val;
523 }
524 else if (key == "CN")
525 {
526 out["CommonName"] = val;
527 }
528 else if (key == "C")
529 {
530 out["Country"] = val;
531 }
532 else if (key == "O")
533 {
534 out["Organization"] = val;
535 }
536 else if (key == "OU")
537 {
538 out["OrganizationalUnit"] = val;
539 }
540 else if (key == "ST")
541 {
542 out["State"] = val;
543 }
544 // skip comma character
545 if (i != value.end())
546 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530547 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600548 }
549 }
550}
551
552/**
553 * @brief Retrieve the certificates properties and append to the response
554 * message
555 *
556 * @param[in] asyncResp Shared pointer to the response message
557 * @param[in] objectPath Path of the D-Bus service object
558 * @param[in] certId Id of the certificate
559 * @param[in] certURL URL of the certificate object
560 * @param[in] name name of the certificate
561 * @return None
562 */
563static void getCertificateProperties(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500564 const std::shared_ptr<AsyncResp>& asyncResp, const std::string& objectPath,
565 const std::string& service, long certId, const std::string& certURL,
566 const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600567{
568 using PropertyType =
569 std::variant<std::string, uint64_t, std::vector<std::string>>;
570 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
571 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
572 << " certId=" << certId << " certURl=" << certURL;
573 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600574 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500575 const PropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600576 if (ec)
577 {
578 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500579 messages::resourceNotFound(asyncResp->res, name,
580 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600581 return;
582 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600583 asyncResp->res.jsonValue = {
584 {"@odata.id", certURL},
585 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600586 {"Id", std::to_string(certId)},
587 {"Name", name},
588 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500589 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600590 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600591 if (property.first == "CertificateString")
592 {
593 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500594 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600595 std::get_if<std::string>(&property.second);
596 if (value)
597 {
598 asyncResp->res.jsonValue["CertificateString"] = *value;
599 }
600 }
601 else if (property.first == "KeyUsage")
602 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500603 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600604 asyncResp->res.jsonValue["KeyUsage"];
605 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500606 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600607 std::get_if<std::vector<std::string>>(&property.second);
608 if (value)
609 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500610 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600611 {
612 keyUsage.push_back(usage);
613 }
614 }
615 }
616 else if (property.first == "Issuer")
617 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500618 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600619 std::get_if<std::string>(&property.second);
620 if (value)
621 {
622 updateCertIssuerOrSubject(
623 asyncResp->res.jsonValue["Issuer"], *value);
624 }
625 }
626 else if (property.first == "Subject")
627 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500628 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600629 std::get_if<std::string>(&property.second);
630 if (value)
631 {
632 updateCertIssuerOrSubject(
633 asyncResp->res.jsonValue["Subject"], *value);
634 }
635 }
636 else if (property.first == "ValidNotAfter")
637 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500638 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600639 std::get_if<uint64_t>(&property.second);
640 if (value)
641 {
642 std::time_t time = static_cast<std::time_t>(*value);
643 asyncResp->res.jsonValue["ValidNotAfter"] =
644 crow::utility::getDateTime(time);
645 }
646 }
647 else if (property.first == "ValidNotBefore")
648 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500649 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600650 std::get_if<uint64_t>(&property.second);
651 if (value)
652 {
653 std::time_t time = static_cast<std::time_t>(*value);
654 asyncResp->res.jsonValue["ValidNotBefore"] =
655 crow::utility::getDateTime(time);
656 }
657 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600658 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600659 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600660 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600661 service, objectPath, certs::dbusPropIntf, "GetAll",
662 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600663}
664
665using GetObjectType =
666 std::vector<std::pair<std::string, std::vector<std::string>>>;
667
668/**
669 * Action to replace an existing certificate
670 */
671class CertificateActionsReplaceCertificate : public Node
672{
673 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700674 CertificateActionsReplaceCertificate(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600675 Node(app, "/redfish/v1/CertificateService/Actions/"
676 "CertificateService.ReplaceCertificate/")
677 {
678 entityPrivileges = {
679 {boost::beast::http::verb::get, {{"Login"}}},
680 {boost::beast::http::verb::head, {{"Login"}}},
681 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
682 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
683 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
684 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
685 }
686
687 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500688 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +0000689 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600690 {
691 std::string certificate;
692 nlohmann::json certificateUri;
693 std::optional<std::string> certificateType = "PEM";
694 auto asyncResp = std::make_shared<AsyncResp>(res);
695 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
696 certificate, "CertificateUri", certificateUri,
697 "CertificateType", certificateType))
698 {
699 BMCWEB_LOG_ERROR << "Required parameters are missing";
700 messages::internalError(asyncResp->res);
701 return;
702 }
703
704 if (!certificateType)
705 {
706 // should never happen, but it never hurts to be paranoid.
707 return;
708 }
709 if (certificateType != "PEM")
710 {
711 messages::actionParameterNotSupported(
712 asyncResp->res, "CertificateType", "ReplaceCertificate");
713 return;
714 }
715
716 std::string certURI;
717 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
718 "@odata.id", certURI))
719 {
720 messages::actionParameterMissing(
721 asyncResp->res, "ReplaceCertificate", "CertificateUri");
722 return;
723 }
724
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600725 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
726 long id = getIDFromURL(certURI);
727 if (id < 0)
728 {
729 messages::actionParameterValueFormatError(asyncResp->res, certURI,
730 "CertificateUri",
731 "ReplaceCertificate");
732 return;
733 }
734 std::string objectPath;
735 std::string name;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600736 std::string service;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600737 if (boost::starts_with(
738 certURI,
739 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
740 {
741 objectPath =
742 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
743 name = "HTTPS certificate";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600744 service = certs::httpsServiceName;
745 }
746 else if (boost::starts_with(
747 certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
748 {
749 objectPath =
750 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
751 name = "LDAP certificate";
752 service = certs::ldapServiceName;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600753 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -0500754 else if (boost::starts_with(
755 certURI,
756 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
757 {
758 objectPath = std::string(certs::authorityObjectPath) + "/" +
759 std::to_string(id);
760 name = "TrustStore certificate";
761 service = certs::authorityServiceName;
762 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600763 else
764 {
765 messages::actionParameterNotSupported(
766 asyncResp->res, "CertificateUri", "ReplaceCertificate");
767 return;
768 }
769
770 std::shared_ptr<CertificateFile> certFile =
771 std::make_shared<CertificateFile>(certificate);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600772 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600773 [asyncResp, certFile, objectPath, service, certURI, id,
774 name](const boost::system::error_code ec) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600775 if (ec)
776 {
777 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500778 messages::resourceNotFound(asyncResp->res, name,
779 std::to_string(id));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600780 return;
781 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600782 getCertificateProperties(asyncResp, objectPath, service, id,
783 certURI, name);
784 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
785 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600786 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600787 service, objectPath, certs::certReplaceIntf, "Replace",
788 certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600789 }
790}; // CertificateActionsReplaceCertificate
791
792/**
793 * Certificate resource describes a certificate used to prove the identity
794 * of a component, account or service.
795 */
796class HTTPSCertificate : public Node
797{
798 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700799 HTTPSCertificate(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600800 Node(app,
801 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"
802 "<str>/",
803 std::string())
804 {
805 entityPrivileges = {
806 {boost::beast::http::verb::get, {{"Login"}}},
807 {boost::beast::http::verb::head, {{"Login"}}},
808 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
809 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
810 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
811 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
812 }
813
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500814 void doGet(crow::Response& res, const crow::Request& req,
815 const std::vector<std::string>& params) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600816 {
817 auto asyncResp = std::make_shared<AsyncResp>(res);
818 if (params.size() != 1)
819 {
820 messages::internalError(asyncResp->res);
821 return;
822 }
823 long id = getIDFromURL(req.url);
824
825 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" << std::to_string(id);
826 std::string certURL =
827 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
828 std::to_string(id);
829 std::string objectPath = certs::httpsObjectPath;
830 objectPath += "/";
831 objectPath += std::to_string(id);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600832 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
833 id, certURL, "HTTPS Certificate");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600834 }
835
836}; // namespace redfish
837
838/**
839 * Collection of HTTPS certificates
840 */
841class HTTPSCertificateCollection : public Node
842{
843 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700844 HTTPSCertificateCollection(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600845 Node(app,
846 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
847 {
848 entityPrivileges = {
849 {boost::beast::http::verb::get, {{"Login"}}},
850 {boost::beast::http::verb::head, {{"Login"}}},
851 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
852 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
853 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
854 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
855 }
Ed Tanouscb13a392020-07-25 19:02:03 +0000856 void doGet(crow::Response& res, const crow::Request&,
857 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600858 {
859 res.jsonValue = {
860 {"@odata.id",
861 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
862 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600863 {"Name", "HTTPS Certificates Collection"},
864 {"Description", "A Collection of HTTPS certificate instances"}};
865 auto asyncResp = std::make_shared<AsyncResp>(res);
866 crow::connections::systemBus->async_method_call(
867 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500868 const ManagedObjectType& certs) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600869 if (ec)
870 {
871 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
872 messages::internalError(asyncResp->res);
873 return;
874 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500875 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
Marri Devender Rao37cce912019-02-20 01:05:22 -0600876 members = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500877 for (const auto& cert : certs)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600878 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600879 long id = getIDFromURL(cert.first.str);
880 if (id >= 0)
881 {
882 members.push_back(
883 {{"@odata.id",
884 "/redfish/v1/Managers/bmc/"
885 "NetworkProtocol/HTTPS/Certificates/" +
886 std::to_string(id)}});
887 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600888 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600889 asyncResp->res.jsonValue["Members@odata.count"] =
890 members.size();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600891 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600892 certs::httpsServiceName, certs::httpsObjectPath,
893 certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600894 }
895
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500896 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +0000897 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600898 {
899 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
900 auto asyncResp = std::make_shared<AsyncResp>(res);
901 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
902 {"Description", "HTTPS Certificate"}};
903
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200904 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
905
906 if (certFileBody.empty())
907 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +0200908 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
909 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200910 return;
911 }
912
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600913 std::shared_ptr<CertificateFile> certFile =
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200914 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600915
916 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200917 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500918 const std::string& objectPath) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600919 if (ec)
920 {
921 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
922 messages::internalError(asyncResp->res);
923 return;
924 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200925 long certId = getIDFromURL(objectPath);
926 if (certId < 0)
927 {
928 BMCWEB_LOG_ERROR << "Invalid objectPath value"
929 << objectPath;
930 messages::internalError(asyncResp->res);
931 return;
932 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600933 std::string certURL =
934 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
935 "Certificates/" +
936 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600937 getCertificateProperties(asyncResp, objectPath,
938 certs::httpsServiceName, certId,
939 certURL, "HTTPS Certificate");
940 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
941 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600942 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600943 certs::httpsServiceName, certs::httpsObjectPath,
944 certs::certInstallIntf, "Install", certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600945 }
946}; // HTTPSCertificateCollection
947
948/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600949 * The certificate location schema defines a resource that an administrator
950 * can use in order to locate all certificates installed on a given service.
951 */
952class CertificateLocations : public Node
953{
954 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700955 CertificateLocations(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600956 Node(app, "/redfish/v1/CertificateService/CertificateLocations/")
957 {
958 entityPrivileges = {
959 {boost::beast::http::verb::get, {{"Login"}}},
960 {boost::beast::http::verb::head, {{"Login"}}},
961 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
962 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
963 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
964 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
965 }
966
967 private:
Ed Tanouscb13a392020-07-25 19:02:03 +0000968 void doGet(crow::Response& res, const crow::Request&,
969 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600970 {
971 res.jsonValue = {
972 {"@odata.id",
973 "/redfish/v1/CertificateService/CertificateLocations"},
974 {"@odata.type",
975 "#CertificateLocations.v1_0_0.CertificateLocations"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600976 {"Name", "Certificate Locations"},
977 {"Id", "CertificateLocations"},
978 {"Description",
979 "Defines a resource that an administrator can use in order to "
980 "locate all certificates installed on a given service"}};
981 auto asyncResp = std::make_shared<AsyncResp>(res);
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500982 nlohmann::json& links =
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600983 asyncResp->res.jsonValue["Links"]["Certificates"];
984 links = nlohmann::json::array();
985 getCertificateLocations(
986 asyncResp,
987 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
Marri Devender Rao37cce912019-02-20 01:05:22 -0600988 certs::httpsObjectPath, certs::httpsServiceName);
989 getCertificateLocations(asyncResp,
990 "/redfish/v1/AccountService/LDAP/Certificates/",
991 certs::ldapObjectPath, certs::ldapServiceName);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -0500992 getCertificateLocations(
993 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
994 certs::authorityObjectPath, certs::authorityServiceName);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600995 }
996 /**
997 * @brief Retrieve the certificates installed list and append to the
998 * response
999 *
1000 * @param[in] asyncResp Shared pointer to the response message
1001 * @param[in] certURL Path of the certificate object
1002 * @param[in] path Path of the D-Bus service object
1003 * @return None
1004 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001005 void getCertificateLocations(std::shared_ptr<AsyncResp>& asyncResp,
1006 const std::string& certURL,
1007 const std::string& path,
1008 const std::string& service)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001009 {
1010 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
1011 << " Path=" << path << " service= " << service;
1012 crow::connections::systemBus->async_method_call(
1013 [asyncResp, certURL](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001014 const ManagedObjectType& certs) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001015 if (ec)
1016 {
Jonathan Doman9c8e0392021-02-01 18:51:38 -08001017 BMCWEB_LOG_WARNING
1018 << "Certificate collection query failed: " << ec
1019 << ", skipping " << certURL;
Marri Devender Rao37cce912019-02-20 01:05:22 -06001020 return;
1021 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001022 nlohmann::json& links =
Marri Devender Rao37cce912019-02-20 01:05:22 -06001023 asyncResp->res.jsonValue["Links"]["Certificates"];
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001024 for (auto& cert : certs)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001025 {
1026 long id = getIDFromURL(cert.first.str);
1027 if (id >= 0)
1028 {
1029 links.push_back(
1030 {{"@odata.id", certURL + std::to_string(id)}});
1031 }
1032 }
1033 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
1034 links.size();
1035 },
1036 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001037 }
1038}; // CertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001039
1040/**
1041 * Collection of LDAP certificates
1042 */
1043class LDAPCertificateCollection : public Node
1044{
1045 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001046 LDAPCertificateCollection(App& app) :
Marri Devender Rao37cce912019-02-20 01:05:22 -06001047 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1048 {
1049 entityPrivileges = {
1050 {boost::beast::http::verb::get, {{"Login"}}},
1051 {boost::beast::http::verb::head, {{"Login"}}},
1052 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1053 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1054 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1055 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1056 }
Ed Tanouscb13a392020-07-25 19:02:03 +00001057 void doGet(crow::Response& res, const crow::Request&,
1058 const std::vector<std::string>&) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001059 {
1060 res.jsonValue = {
1061 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1062 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Rao37cce912019-02-20 01:05:22 -06001063 {"Name", "LDAP Certificates Collection"},
1064 {"Description", "A Collection of LDAP certificate instances"}};
1065 auto asyncResp = std::make_shared<AsyncResp>(res);
1066 crow::connections::systemBus->async_method_call(
1067 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001068 const ManagedObjectType& certs) {
Jonathan Doman9c8e0392021-02-01 18:51:38 -08001069 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
1070 nlohmann::json& count =
1071 asyncResp->res.jsonValue["Members@odata.count"];
1072 members = nlohmann::json::array();
1073 count = 0;
Marri Devender Rao37cce912019-02-20 01:05:22 -06001074 if (ec)
1075 {
Jonathan Doman9c8e0392021-02-01 18:51:38 -08001076 BMCWEB_LOG_WARNING << "LDAP certificate query failed: "
1077 << ec;
Marri Devender Rao37cce912019-02-20 01:05:22 -06001078 return;
1079 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001080 for (const auto& cert : certs)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001081 {
1082 long id = getIDFromURL(cert.first.str);
1083 if (id >= 0)
1084 {
1085 members.push_back(
1086 {{"@odata.id", "/redfish/v1/AccountService/"
1087 "LDAP/Certificates/" +
1088 std::to_string(id)}});
1089 }
1090 }
Jonathan Doman9c8e0392021-02-01 18:51:38 -08001091 count = members.size();
Marri Devender Rao37cce912019-02-20 01:05:22 -06001092 },
1093 certs::ldapServiceName, certs::ldapObjectPath,
1094 certs::dbusObjManagerIntf, "GetManagedObjects");
1095 }
1096
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001097 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001098 const std::vector<std::string>&) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001099 {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001100 auto asyncResp = std::make_shared<AsyncResp>(res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001101 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1102
1103 if (certFileBody.empty())
1104 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001105 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1106 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001107 return;
1108 }
1109
1110 std::shared_ptr<CertificateFile> certFile =
1111 std::make_shared<CertificateFile>(certFileBody);
1112
Marri Devender Rao37cce912019-02-20 01:05:22 -06001113 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001114 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001115 const std::string& objectPath) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001116 if (ec)
1117 {
1118 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1119 messages::internalError(asyncResp->res);
1120 return;
1121 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001122 long certId = getIDFromURL(objectPath);
1123 if (certId < 0)
1124 {
1125 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1126 << objectPath;
1127 messages::internalError(asyncResp->res);
1128 return;
1129 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001130 std::string certURL =
1131 "/redfish/v1/AccountService/LDAP/Certificates/" +
1132 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001133 getCertificateProperties(asyncResp, objectPath,
1134 certs::ldapServiceName, certId,
1135 certURL, "LDAP Certificate");
1136 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1137 << certFile->getCertFilePath();
1138 },
1139 certs::ldapServiceName, certs::ldapObjectPath,
1140 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1141 }
1142}; // LDAPCertificateCollection
1143
1144/**
1145 * Certificate resource describes a certificate used to prove the identity
1146 * of a component, account or service.
1147 */
1148class LDAPCertificate : public Node
1149{
1150 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001151 LDAPCertificate(App& app) :
Marri Devender Rao37cce912019-02-20 01:05:22 -06001152 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/",
1153 std::string())
1154 {
1155 entityPrivileges = {
1156 {boost::beast::http::verb::get, {{"Login"}}},
1157 {boost::beast::http::verb::head, {{"Login"}}},
1158 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1159 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1160 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1161 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1162 }
1163
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001164 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001165 const std::vector<std::string>&) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001166 {
1167 auto asyncResp = std::make_shared<AsyncResp>(res);
1168 long id = getIDFromURL(req.url);
1169 if (id < 0)
1170 {
1171 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1172 messages::internalError(asyncResp->res);
1173 return;
1174 }
1175 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1176 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1177 std::to_string(id);
1178 std::string objectPath = certs::ldapObjectPath;
1179 objectPath += "/";
1180 objectPath += std::to_string(id);
1181 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1182 id, certURL, "LDAP Certificate");
1183 }
1184}; // LDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001185/**
1186 * Collection of TrustStoreCertificate certificates
1187 */
1188class TrustStoreCertificateCollection : public Node
1189{
1190 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001191 TrustStoreCertificateCollection(App& app) :
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001192 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1193 {
1194 entityPrivileges = {
1195 {boost::beast::http::verb::get, {{"Login"}}},
1196 {boost::beast::http::verb::head, {{"Login"}}},
1197 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1198 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1199 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1200 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1201 }
Ed Tanouscb13a392020-07-25 19:02:03 +00001202 void doGet(crow::Response& res, const crow::Request&,
1203 const std::vector<std::string>&) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001204 {
1205 res.jsonValue = {
1206 {"@odata.id", "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1207 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001208 {"Name", "TrustStore Certificates Collection"},
1209 {"Description",
1210 "A Collection of TrustStore certificate instances"}};
1211 auto asyncResp = std::make_shared<AsyncResp>(res);
1212 crow::connections::systemBus->async_method_call(
1213 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001214 const ManagedObjectType& certs) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001215 if (ec)
1216 {
1217 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1218 messages::internalError(asyncResp->res);
1219 return;
1220 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001221 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001222 members = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001223 for (const auto& cert : certs)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001224 {
1225 long id = getIDFromURL(cert.first.str);
1226 if (id >= 0)
1227 {
1228 members.push_back(
1229 {{"@odata.id", "/redfish/v1/Managers/bmc/"
1230 "Truststore/Certificates/" +
1231 std::to_string(id)}});
1232 }
1233 }
1234 asyncResp->res.jsonValue["Members@odata.count"] =
1235 members.size();
1236 },
1237 certs::authorityServiceName, certs::authorityObjectPath,
1238 certs::dbusObjManagerIntf, "GetManagedObjects");
1239 }
1240
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001241 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001242 const std::vector<std::string>&) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001243 {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001244 auto asyncResp = std::make_shared<AsyncResp>(res);
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001245 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1246
1247 if (certFileBody.empty())
1248 {
1249 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1250 messages::unrecognizedRequestBody(asyncResp->res);
1251 return;
1252 }
1253
1254 std::shared_ptr<CertificateFile> certFile =
1255 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001256 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001257 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001258 const std::string& objectPath) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001259 if (ec)
1260 {
1261 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1262 messages::internalError(asyncResp->res);
1263 return;
1264 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001265 long certId = getIDFromURL(objectPath);
1266 if (certId < 0)
1267 {
1268 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1269 << objectPath;
1270 messages::internalError(asyncResp->res);
1271 return;
1272 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001273 std::string certURL = "/redfish/v1/Managers/bmc/"
1274 "Truststore/Certificates/" +
1275 std::to_string(certId);
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001276
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001277 getCertificateProperties(asyncResp, objectPath,
1278 certs::authorityServiceName, certId,
1279 certURL, "TrustStore Certificate");
1280 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1281 << certFile->getCertFilePath();
1282 },
1283 certs::authorityServiceName, certs::authorityObjectPath,
1284 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1285 }
1286}; // TrustStoreCertificateCollection
1287
1288/**
1289 * Certificate resource describes a certificate used to prove the identity
1290 * of a component, account or service.
1291 */
1292class TrustStoreCertificate : public Node
1293{
1294 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001295 TrustStoreCertificate(App& app) :
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001296 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/",
1297 std::string())
1298 {
1299 entityPrivileges = {
1300 {boost::beast::http::verb::get, {{"Login"}}},
1301 {boost::beast::http::verb::head, {{"Login"}}},
1302 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1303 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1304 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1305 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1306 }
1307
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001308 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001309 const std::vector<std::string>&) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001310 {
1311 auto asyncResp = std::make_shared<AsyncResp>(res);
1312 long id = getIDFromURL(req.url);
1313 if (id < 0)
1314 {
1315 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1316 messages::internalError(asyncResp->res);
1317 return;
1318 }
1319 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1320 << std::to_string(id);
1321 std::string certURL =
1322 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1323 std::to_string(id);
1324 std::string objectPath = certs::authorityObjectPath;
1325 objectPath += "/";
1326 objectPath += std::to_string(id);
1327 getCertificateProperties(asyncResp, objectPath,
1328 certs::authorityServiceName, id, certURL,
1329 "TrustStore Certificate");
1330 }
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001331
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001332 void doDelete(crow::Response& res, const crow::Request& req,
1333 const std::vector<std::string>& params) override
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001334 {
1335 auto asyncResp = std::make_shared<AsyncResp>(res);
1336
1337 if (params.size() != 1)
1338 {
1339 messages::internalError(asyncResp->res);
1340 return;
1341 }
1342
1343 long id = getIDFromURL(req.url);
1344 if (id < 0)
1345 {
1346 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1347 messages::resourceNotFound(asyncResp->res, "TrustStore Certificate",
1348 std::string(req.url));
1349 return;
1350 }
1351 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1352 << std::to_string(id);
1353 std::string certPath = certs::authorityObjectPath;
1354 certPath += "/";
1355 certPath += std::to_string(id);
1356
1357 crow::connections::systemBus->async_method_call(
1358 [asyncResp, id](const boost::system::error_code ec) {
1359 if (ec)
1360 {
1361 messages::resourceNotFound(asyncResp->res,
1362 "TrustStore Certificate",
1363 std::to_string(id));
1364 return;
1365 }
1366 BMCWEB_LOG_INFO << "Certificate deleted";
1367 asyncResp->res.result(boost::beast::http::status::no_content);
1368 },
1369 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1370 "Delete");
1371 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001372}; // TrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001373} // namespace redfish