blob: 0e53571be49f8a4d0d493a727f7e04d09a7e37c9 [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:
zhanghch058d1b46d2021-04-01 11:18:24 +080056 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
57 const crow::Request&, const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -060058 {
zhanghch058d1b46d2021-04-01 11:18:24 +080059 asyncResp->res.jsonValue = {
Marri Devender Rao5968cae2019-01-21 10:27:12 -060060 {"@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"}};
zhanghch058d1b46d2021-04-01 11:18:24 +080065 asyncResp->res.jsonValue["CertificateLocations"] = {
Marri Devender Rao5968cae2019-01-21 10:27:12 -060066 {"@odata.id",
67 "/redfish/v1/CertificateService/CertificateLocations"}};
zhanghch058d1b46d2021-04-01 11:18:24 +080068 asyncResp->res
69 .jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = {
Marri Devender Rao5968cae2019-01-21 10:27:12 -060070 {"target", "/redfish/v1/CertificateService/Actions/"
71 "CertificateService.ReplaceCertificate"},
72 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
zhanghch058d1b46d2021-04-01 11:18:24 +080073 asyncResp->res.jsonValue["Actions"]["#CertificateService.GenerateCSR"] =
74 {{"target", "/redfish/v1/CertificateService/Actions/"
75 "CertificateService.GenerateCSR"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -060076 }
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
zhanghch058d1b46d2021-04-01 11:18:24 +0800103inline std::string getCertificateFromReqBody(
104 const std::shared_ptr<bmcweb::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 */
zhanghch058d1b46d2021-04-01 11:18:24 +0800200static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500201 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:
zhanghch058d1b46d2021-04-01 11:18:24 +0800250 void doPost(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
251 const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +0000252 const std::vector<std::string>&) override
Marri Devender Rao30215812019-03-18 08:59:21 -0500253 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700254 static const int rsaKeyBitLength = 2048;
zhanghch058d1b46d2021-04-01 11:18:24 +0800255
Marri Devender Rao30215812019-03-18 08:59:21 -0500256 // Required parameters
257 std::string city;
258 std::string commonName;
259 std::string country;
260 std::string organization;
261 std::string organizationalUnit;
262 std::string state;
263 nlohmann::json certificateCollection;
264
265 // Optional parameters
266 std::optional<std::vector<std::string>> optAlternativeNames =
267 std::vector<std::string>();
268 std::optional<std::string> optContactPerson = "";
269 std::optional<std::string> optChallengePassword = "";
270 std::optional<std::string> optEmail = "";
271 std::optional<std::string> optGivenName = "";
272 std::optional<std::string> optInitials = "";
Ed Tanous2c70f802020-09-28 14:29:23 -0700273 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
Vernon Maueryaaf32062020-03-09 10:41:31 -0700274 std::optional<std::string> optKeyCurveId = "secp384r1";
Marri Devender Rao30215812019-03-18 08:59:21 -0500275 std::optional<std::string> optKeyPairAlgorithm = "EC";
276 std::optional<std::vector<std::string>> optKeyUsage =
277 std::vector<std::string>();
278 std::optional<std::string> optSurname = "";
279 std::optional<std::string> optUnstructuredName = "";
280 if (!json_util::readJson(
281 req, asyncResp->res, "City", city, "CommonName", commonName,
282 "ContactPerson", optContactPerson, "Country", country,
283 "Organization", organization, "OrganizationalUnit",
284 organizationalUnit, "State", state, "CertificateCollection",
285 certificateCollection, "AlternativeNames", optAlternativeNames,
286 "ChallengePassword", optChallengePassword, "Email", optEmail,
287 "GivenName", optGivenName, "Initials", optInitials,
288 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
289 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
290 optKeyUsage, "Surname", optSurname, "UnstructuredName",
291 optUnstructuredName))
292 {
293 return;
294 }
295
296 // bmcweb has no way to store or decode a private key challenge
297 // password, which will likely cause bmcweb to crash on startup if this
298 // is not set on a post so not allowing the user to set value
299 if (*optChallengePassword != "")
300 {
301 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
302 "ChallengePassword");
303 return;
304 }
305
306 std::string certURI;
307 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
308 "@odata.id", certURI))
309 {
310 return;
311 }
312
313 std::string objectPath;
314 std::string service;
315 if (boost::starts_with(
316 certURI,
317 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
318 {
319 objectPath = certs::httpsObjectPath;
320 service = certs::httpsServiceName;
321 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500322 else if (boost::starts_with(
323 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
324 {
325 objectPath = certs::ldapObjectPath;
326 service = certs::ldapServiceName;
327 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500328 else
329 {
330 messages::actionParameterNotSupported(
331 asyncResp->res, "CertificateCollection", "GenerateCSR");
332 return;
333 }
334
335 // supporting only EC and RSA algorithm
336 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
337 {
338 messages::actionParameterNotSupported(
339 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
340 return;
341 }
342
343 // supporting only 2048 key bit length for RSA algorithm due to time
344 // consumed in generating private key
345 if (*optKeyPairAlgorithm == "RSA" &&
Ed Tanous2c70f802020-09-28 14:29:23 -0700346 *optKeyBitLength != rsaKeyBitLength)
Marri Devender Rao30215812019-03-18 08:59:21 -0500347 {
348 messages::propertyValueNotInList(asyncResp->res,
349 std::to_string(*optKeyBitLength),
350 "KeyBitLength");
351 return;
352 }
353
354 // validate KeyUsage supporting only 1 type based on URL
355 if (boost::starts_with(
356 certURI,
357 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
358 {
359 if (optKeyUsage->size() == 0)
360 {
361 optKeyUsage->push_back("ServerAuthentication");
362 }
363 else if (optKeyUsage->size() == 1)
364 {
365 if ((*optKeyUsage)[0] != "ServerAuthentication")
366 {
367 messages::propertyValueNotInList(
368 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
369 return;
370 }
371 }
372 else
373 {
374 messages::actionParameterNotSupported(
375 asyncResp->res, "KeyUsage", "GenerateCSR");
376 return;
377 }
378 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500379 else if (boost::starts_with(
380 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
381 {
382 if (optKeyUsage->size() == 0)
383 {
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
396 {
397 messages::actionParameterNotSupported(
398 asyncResp->res, "KeyUsage", "GenerateCSR");
399 return;
400 }
401 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500402
403 // Only allow one CSR matcher at a time so setting retry time-out and
404 // timer expiry to 10 seconds for now.
Ed Tanous2c70f802020-09-28 14:29:23 -0700405 static const int timeOut = 10;
Marri Devender Rao30215812019-03-18 08:59:21 -0500406 if (csrMatcher)
407 {
Marri Devender Rao30215812019-03-18 08:59:21 -0500408 messages::serviceTemporarilyUnavailable(asyncResp->res,
Ed Tanous2c70f802020-09-28 14:29:23 -0700409 std::to_string(timeOut));
Marri Devender Rao30215812019-03-18 08:59:21 -0500410 return;
411 }
412
413 // Make this static so it survives outside this method
414 static boost::asio::steady_timer timeout(*req.ioService);
Ed Tanous2c70f802020-09-28 14:29:23 -0700415 timeout.expires_after(std::chrono::seconds(timeOut));
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500416 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500417 csrMatcher = nullptr;
418 if (ec)
419 {
420 // operation_aborted is expected if timer is canceled before
421 // completion.
422 if (ec != boost::asio::error::operation_aborted)
423 {
424 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
425 }
426 return;
427 }
428 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
429 messages::internalError(asyncResp->res);
430 });
431
432 // create a matcher to wait on CSR object
433 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
434 std::string match("type='signal',"
435 "interface='org.freedesktop.DBus.ObjectManager',"
436 "path='" +
437 objectPath +
438 "',"
439 "member='InterfacesAdded'");
440 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
441 *crow::connections::systemBus, match,
442 [asyncResp, service, objectPath,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500443 certURI](sdbusplus::message::message& m) {
Ed Tanous271584a2019-07-09 16:24:22 -0700444 timeout.cancel();
Marri Devender Rao30215812019-03-18 08:59:21 -0500445 if (m.is_method_error())
446 {
447 BMCWEB_LOG_ERROR << "Dbus method error!!!";
448 messages::internalError(asyncResp->res);
449 return;
450 }
451 std::vector<std::pair<
452 std::string, std::vector<std::pair<
453 std::string, std::variant<std::string>>>>>
454 interfacesProperties;
455 sdbusplus::message::object_path csrObjectPath;
456 m.read(csrObjectPath, interfacesProperties);
457 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500458 for (auto& interface : interfacesProperties)
Marri Devender Rao30215812019-03-18 08:59:21 -0500459 {
460 if (interface.first == "xyz.openbmc_project.Certs.CSR")
461 {
462 getCSR(asyncResp, certURI, service, objectPath,
463 csrObjectPath.str);
464 break;
465 }
466 }
467 });
468 crow::connections::systemBus->async_method_call(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500469 [asyncResp](const boost::system::error_code& ec,
Ed Tanouscb13a392020-07-25 19:02:03 +0000470 const std::string&) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500471 if (ec)
472 {
473 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
474 messages::internalError(asyncResp->res);
475 return;
476 }
477 },
478 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
479 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
480 commonName, *optContactPerson, country, *optEmail, *optGivenName,
481 *optInitials, *optKeyBitLength, *optKeyCurveId,
482 *optKeyPairAlgorithm, *optKeyUsage, organization,
483 organizationalUnit, state, *optSurname, *optUnstructuredName);
484 }
485}; // CertificateActionGenerateCSR
486
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600487/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500488 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600489 *
490 * @param[in] asyncResp Shared pointer to the response message
491 * @param[in] str Issuer/Subject value in key=value pairs
492 * @param[in] type Issuer/Subject
493 * @return None
494 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500495static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600496 const std::string_view value)
497{
498 // example: O=openbmc-project.xyz,CN=localhost
499 std::string_view::iterator i = value.begin();
500 while (i != value.end())
501 {
502 std::string_view::iterator tokenBegin = i;
503 while (i != value.end() && *i != '=')
504 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530505 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600506 }
507 if (i == value.end())
508 {
509 break;
510 }
Ed Tanous271584a2019-07-09 16:24:22 -0700511 const std::string_view key(tokenBegin,
512 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530513 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600514 tokenBegin = i;
515 while (i != value.end() && *i != ',')
516 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530517 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600518 }
Ed Tanous271584a2019-07-09 16:24:22 -0700519 const std::string_view val(tokenBegin,
520 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600521 if (key == "L")
522 {
523 out["City"] = val;
524 }
525 else if (key == "CN")
526 {
527 out["CommonName"] = val;
528 }
529 else if (key == "C")
530 {
531 out["Country"] = val;
532 }
533 else if (key == "O")
534 {
535 out["Organization"] = val;
536 }
537 else if (key == "OU")
538 {
539 out["OrganizationalUnit"] = val;
540 }
541 else if (key == "ST")
542 {
543 out["State"] = val;
544 }
545 // skip comma character
546 if (i != value.end())
547 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530548 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600549 }
550 }
551}
552
553/**
554 * @brief Retrieve the certificates properties and append to the response
555 * message
556 *
557 * @param[in] asyncResp Shared pointer to the response message
558 * @param[in] objectPath Path of the D-Bus service object
559 * @param[in] certId Id of the certificate
560 * @param[in] certURL URL of the certificate object
561 * @param[in] name name of the certificate
562 * @return None
563 */
564static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800565 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
566 const std::string& objectPath, const std::string& service, long certId,
567 const std::string& certURL, const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600568{
569 using PropertyType =
570 std::variant<std::string, uint64_t, std::vector<std::string>>;
571 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
572 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
573 << " certId=" << certId << " certURl=" << certURL;
574 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600575 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500576 const PropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600577 if (ec)
578 {
579 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500580 messages::resourceNotFound(asyncResp->res, name,
581 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600582 return;
583 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600584 asyncResp->res.jsonValue = {
585 {"@odata.id", certURL},
586 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600587 {"Id", std::to_string(certId)},
588 {"Name", name},
589 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500590 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600591 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600592 if (property.first == "CertificateString")
593 {
594 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500595 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600596 std::get_if<std::string>(&property.second);
597 if (value)
598 {
599 asyncResp->res.jsonValue["CertificateString"] = *value;
600 }
601 }
602 else if (property.first == "KeyUsage")
603 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500604 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600605 asyncResp->res.jsonValue["KeyUsage"];
606 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500607 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600608 std::get_if<std::vector<std::string>>(&property.second);
609 if (value)
610 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500611 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600612 {
613 keyUsage.push_back(usage);
614 }
615 }
616 }
617 else if (property.first == "Issuer")
618 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500619 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600620 std::get_if<std::string>(&property.second);
621 if (value)
622 {
623 updateCertIssuerOrSubject(
624 asyncResp->res.jsonValue["Issuer"], *value);
625 }
626 }
627 else if (property.first == "Subject")
628 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500629 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600630 std::get_if<std::string>(&property.second);
631 if (value)
632 {
633 updateCertIssuerOrSubject(
634 asyncResp->res.jsonValue["Subject"], *value);
635 }
636 }
637 else if (property.first == "ValidNotAfter")
638 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500639 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600640 std::get_if<uint64_t>(&property.second);
641 if (value)
642 {
643 std::time_t time = static_cast<std::time_t>(*value);
644 asyncResp->res.jsonValue["ValidNotAfter"] =
645 crow::utility::getDateTime(time);
646 }
647 }
648 else if (property.first == "ValidNotBefore")
649 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500650 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600651 std::get_if<uint64_t>(&property.second);
652 if (value)
653 {
654 std::time_t time = static_cast<std::time_t>(*value);
655 asyncResp->res.jsonValue["ValidNotBefore"] =
656 crow::utility::getDateTime(time);
657 }
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 */
672class CertificateActionsReplaceCertificate : public Node
673{
674 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700675 CertificateActionsReplaceCertificate(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600676 Node(app, "/redfish/v1/CertificateService/Actions/"
677 "CertificateService.ReplaceCertificate/")
678 {
679 entityPrivileges = {
680 {boost::beast::http::verb::get, {{"Login"}}},
681 {boost::beast::http::verb::head, {{"Login"}}},
682 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
683 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
684 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
685 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
686 }
687
688 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800689 void doPost(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
690 const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +0000691 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600692 {
693 std::string certificate;
694 nlohmann::json certificateUri;
695 std::optional<std::string> certificateType = "PEM";
zhanghch058d1b46d2021-04-01 11:18:24 +0800696
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600697 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
698 certificate, "CertificateUri", certificateUri,
699 "CertificateType", certificateType))
700 {
701 BMCWEB_LOG_ERROR << "Required parameters are missing";
702 messages::internalError(asyncResp->res);
703 return;
704 }
705
706 if (!certificateType)
707 {
708 // should never happen, but it never hurts to be paranoid.
709 return;
710 }
711 if (certificateType != "PEM")
712 {
713 messages::actionParameterNotSupported(
714 asyncResp->res, "CertificateType", "ReplaceCertificate");
715 return;
716 }
717
718 std::string certURI;
719 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
720 "@odata.id", certURI))
721 {
722 messages::actionParameterMissing(
723 asyncResp->res, "ReplaceCertificate", "CertificateUri");
724 return;
725 }
726
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600727 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
728 long id = getIDFromURL(certURI);
729 if (id < 0)
730 {
731 messages::actionParameterValueFormatError(asyncResp->res, certURI,
732 "CertificateUri",
733 "ReplaceCertificate");
734 return;
735 }
736 std::string objectPath;
737 std::string name;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600738 std::string service;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600739 if (boost::starts_with(
740 certURI,
741 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
742 {
743 objectPath =
744 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
745 name = "HTTPS certificate";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600746 service = certs::httpsServiceName;
747 }
748 else if (boost::starts_with(
749 certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
750 {
751 objectPath =
752 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
753 name = "LDAP certificate";
754 service = certs::ldapServiceName;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600755 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -0500756 else if (boost::starts_with(
757 certURI,
758 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
759 {
760 objectPath = std::string(certs::authorityObjectPath) + "/" +
761 std::to_string(id);
762 name = "TrustStore certificate";
763 service = certs::authorityServiceName;
764 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600765 else
766 {
767 messages::actionParameterNotSupported(
768 asyncResp->res, "CertificateUri", "ReplaceCertificate");
769 return;
770 }
771
772 std::shared_ptr<CertificateFile> certFile =
773 std::make_shared<CertificateFile>(certificate);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600774 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600775 [asyncResp, certFile, objectPath, service, certURI, id,
776 name](const boost::system::error_code ec) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600777 if (ec)
778 {
779 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500780 messages::resourceNotFound(asyncResp->res, name,
781 std::to_string(id));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600782 return;
783 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600784 getCertificateProperties(asyncResp, objectPath, service, id,
785 certURI, name);
786 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
787 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600788 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600789 service, objectPath, certs::certReplaceIntf, "Replace",
790 certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600791 }
792}; // CertificateActionsReplaceCertificate
793
794/**
795 * Certificate resource describes a certificate used to prove the identity
796 * of a component, account or service.
797 */
798class HTTPSCertificate : public Node
799{
800 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700801 HTTPSCertificate(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600802 Node(app,
803 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"
804 "<str>/",
805 std::string())
806 {
807 entityPrivileges = {
808 {boost::beast::http::verb::get, {{"Login"}}},
809 {boost::beast::http::verb::head, {{"Login"}}},
810 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
811 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
812 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
813 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
814 }
815
zhanghch058d1b46d2021-04-01 11:18:24 +0800816 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
817 const crow::Request& req,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500818 const std::vector<std::string>& params) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600819 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800820
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600821 if (params.size() != 1)
822 {
823 messages::internalError(asyncResp->res);
824 return;
825 }
826 long id = getIDFromURL(req.url);
827
828 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" << std::to_string(id);
829 std::string certURL =
830 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
831 std::to_string(id);
832 std::string objectPath = certs::httpsObjectPath;
833 objectPath += "/";
834 objectPath += std::to_string(id);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600835 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
836 id, certURL, "HTTPS Certificate");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600837 }
838
839}; // namespace redfish
840
841/**
842 * Collection of HTTPS certificates
843 */
844class HTTPSCertificateCollection : public Node
845{
846 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700847 HTTPSCertificateCollection(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600848 Node(app,
849 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
850 {
851 entityPrivileges = {
852 {boost::beast::http::verb::get, {{"Login"}}},
853 {boost::beast::http::verb::head, {{"Login"}}},
854 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
855 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
856 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
857 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
858 }
zhanghch058d1b46d2021-04-01 11:18:24 +0800859 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
860 const crow::Request&, const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600861 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800862 asyncResp->res.jsonValue = {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600863 {"@odata.id",
864 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
865 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600866 {"Name", "HTTPS Certificates Collection"},
867 {"Description", "A Collection of HTTPS certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +0800868
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600869 crow::connections::systemBus->async_method_call(
870 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500871 const ManagedObjectType& certs) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600872 if (ec)
873 {
874 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
875 messages::internalError(asyncResp->res);
876 return;
877 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500878 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
Marri Devender Rao37cce912019-02-20 01:05:22 -0600879 members = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500880 for (const auto& cert : certs)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600881 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600882 long id = getIDFromURL(cert.first.str);
883 if (id >= 0)
884 {
885 members.push_back(
886 {{"@odata.id",
887 "/redfish/v1/Managers/bmc/"
888 "NetworkProtocol/HTTPS/Certificates/" +
889 std::to_string(id)}});
890 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600891 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600892 asyncResp->res.jsonValue["Members@odata.count"] =
893 members.size();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600894 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600895 certs::httpsServiceName, certs::httpsObjectPath,
896 certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600897 }
898
zhanghch058d1b46d2021-04-01 11:18:24 +0800899 void doPost(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
900 const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +0000901 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600902 {
903 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
zhanghch058d1b46d2021-04-01 11:18:24 +0800904
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600905 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
906 {"Description", "HTTPS Certificate"}};
907
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200908 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
909
910 if (certFileBody.empty())
911 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +0200912 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
913 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200914 return;
915 }
916
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600917 std::shared_ptr<CertificateFile> certFile =
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200918 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600919
920 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200921 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500922 const std::string& objectPath) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600923 if (ec)
924 {
925 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
926 messages::internalError(asyncResp->res);
927 return;
928 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200929 long certId = getIDFromURL(objectPath);
930 if (certId < 0)
931 {
932 BMCWEB_LOG_ERROR << "Invalid objectPath value"
933 << objectPath;
934 messages::internalError(asyncResp->res);
935 return;
936 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600937 std::string certURL =
938 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
939 "Certificates/" +
940 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600941 getCertificateProperties(asyncResp, objectPath,
942 certs::httpsServiceName, certId,
943 certURL, "HTTPS Certificate");
944 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
945 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600946 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600947 certs::httpsServiceName, certs::httpsObjectPath,
948 certs::certInstallIntf, "Install", certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600949 }
950}; // HTTPSCertificateCollection
951
952/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600953 * The certificate location schema defines a resource that an administrator
954 * can use in order to locate all certificates installed on a given service.
955 */
956class CertificateLocations : public Node
957{
958 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700959 CertificateLocations(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600960 Node(app, "/redfish/v1/CertificateService/CertificateLocations/")
961 {
962 entityPrivileges = {
963 {boost::beast::http::verb::get, {{"Login"}}},
964 {boost::beast::http::verb::head, {{"Login"}}},
965 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
966 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
967 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
968 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
969 }
970
971 private:
zhanghch058d1b46d2021-04-01 11:18:24 +0800972 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
973 const crow::Request&, const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600974 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800975 asyncResp->res.jsonValue = {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600976 {"@odata.id",
977 "/redfish/v1/CertificateService/CertificateLocations"},
978 {"@odata.type",
979 "#CertificateLocations.v1_0_0.CertificateLocations"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600980 {"Name", "Certificate Locations"},
981 {"Id", "CertificateLocations"},
982 {"Description",
983 "Defines a resource that an administrator can use in order to "
984 "locate all certificates installed on a given service"}};
zhanghch058d1b46d2021-04-01 11:18:24 +0800985
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500986 nlohmann::json& links =
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600987 asyncResp->res.jsonValue["Links"]["Certificates"];
988 links = nlohmann::json::array();
989 getCertificateLocations(
990 asyncResp,
991 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
Marri Devender Rao37cce912019-02-20 01:05:22 -0600992 certs::httpsObjectPath, certs::httpsServiceName);
993 getCertificateLocations(asyncResp,
994 "/redfish/v1/AccountService/LDAP/Certificates/",
995 certs::ldapObjectPath, certs::ldapServiceName);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -0500996 getCertificateLocations(
997 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
998 certs::authorityObjectPath, certs::authorityServiceName);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600999 }
1000 /**
1001 * @brief Retrieve the certificates installed list and append to the
1002 * response
1003 *
1004 * @param[in] asyncResp Shared pointer to the response message
1005 * @param[in] certURL Path of the certificate object
1006 * @param[in] path Path of the D-Bus service object
1007 * @return None
1008 */
zhanghch058d1b46d2021-04-01 11:18:24 +08001009 void getCertificateLocations(
1010 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1011 const std::string& certURL, const std::string& path,
1012 const std::string& service)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001013 {
1014 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
1015 << " Path=" << path << " service= " << service;
1016 crow::connections::systemBus->async_method_call(
1017 [asyncResp, certURL](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001018 const ManagedObjectType& certs) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001019 if (ec)
1020 {
Jonathan Doman9c8e0392021-02-01 18:51:38 -08001021 BMCWEB_LOG_WARNING
1022 << "Certificate collection query failed: " << ec
1023 << ", skipping " << certURL;
Marri Devender Rao37cce912019-02-20 01:05:22 -06001024 return;
1025 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001026 nlohmann::json& links =
Marri Devender Rao37cce912019-02-20 01:05:22 -06001027 asyncResp->res.jsonValue["Links"]["Certificates"];
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001028 for (auto& cert : certs)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001029 {
1030 long id = getIDFromURL(cert.first.str);
1031 if (id >= 0)
1032 {
1033 links.push_back(
1034 {{"@odata.id", certURL + std::to_string(id)}});
1035 }
1036 }
1037 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
1038 links.size();
1039 },
1040 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001041 }
1042}; // CertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001043
1044/**
1045 * Collection of LDAP certificates
1046 */
1047class LDAPCertificateCollection : public Node
1048{
1049 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001050 LDAPCertificateCollection(App& app) :
Marri Devender Rao37cce912019-02-20 01:05:22 -06001051 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1052 {
1053 entityPrivileges = {
1054 {boost::beast::http::verb::get, {{"Login"}}},
1055 {boost::beast::http::verb::head, {{"Login"}}},
1056 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1057 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1058 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1059 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1060 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001061 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1062 const crow::Request&, const std::vector<std::string>&) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001063 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001064 asyncResp->res.jsonValue = {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001065 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1066 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Rao37cce912019-02-20 01:05:22 -06001067 {"Name", "LDAP Certificates Collection"},
1068 {"Description", "A Collection of LDAP certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001069
Marri Devender Rao37cce912019-02-20 01:05:22 -06001070 crow::connections::systemBus->async_method_call(
1071 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001072 const ManagedObjectType& certs) {
Jonathan Doman9c8e0392021-02-01 18:51:38 -08001073 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
1074 nlohmann::json& count =
1075 asyncResp->res.jsonValue["Members@odata.count"];
1076 members = nlohmann::json::array();
1077 count = 0;
Marri Devender Rao37cce912019-02-20 01:05:22 -06001078 if (ec)
1079 {
Jonathan Doman9c8e0392021-02-01 18:51:38 -08001080 BMCWEB_LOG_WARNING << "LDAP certificate query failed: "
1081 << ec;
Marri Devender Rao37cce912019-02-20 01:05:22 -06001082 return;
1083 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001084 for (const auto& cert : certs)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001085 {
1086 long id = getIDFromURL(cert.first.str);
1087 if (id >= 0)
1088 {
1089 members.push_back(
1090 {{"@odata.id", "/redfish/v1/AccountService/"
1091 "LDAP/Certificates/" +
1092 std::to_string(id)}});
1093 }
1094 }
Jonathan Doman9c8e0392021-02-01 18:51:38 -08001095 count = members.size();
Marri Devender Rao37cce912019-02-20 01:05:22 -06001096 },
1097 certs::ldapServiceName, certs::ldapObjectPath,
1098 certs::dbusObjManagerIntf, "GetManagedObjects");
1099 }
1100
zhanghch058d1b46d2021-04-01 11:18:24 +08001101 void doPost(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1102 const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001103 const std::vector<std::string>&) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001104 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001105
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001106 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1107
1108 if (certFileBody.empty())
1109 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001110 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1111 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001112 return;
1113 }
1114
1115 std::shared_ptr<CertificateFile> certFile =
1116 std::make_shared<CertificateFile>(certFileBody);
1117
Marri Devender Rao37cce912019-02-20 01:05:22 -06001118 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001119 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001120 const std::string& objectPath) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001121 if (ec)
1122 {
1123 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1124 messages::internalError(asyncResp->res);
1125 return;
1126 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001127 long certId = getIDFromURL(objectPath);
1128 if (certId < 0)
1129 {
1130 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1131 << objectPath;
1132 messages::internalError(asyncResp->res);
1133 return;
1134 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001135 std::string certURL =
1136 "/redfish/v1/AccountService/LDAP/Certificates/" +
1137 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001138 getCertificateProperties(asyncResp, objectPath,
1139 certs::ldapServiceName, certId,
1140 certURL, "LDAP Certificate");
1141 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1142 << certFile->getCertFilePath();
1143 },
1144 certs::ldapServiceName, certs::ldapObjectPath,
1145 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1146 }
1147}; // LDAPCertificateCollection
1148
1149/**
1150 * Certificate resource describes a certificate used to prove the identity
1151 * of a component, account or service.
1152 */
1153class LDAPCertificate : public Node
1154{
1155 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001156 LDAPCertificate(App& app) :
Marri Devender Rao37cce912019-02-20 01:05:22 -06001157 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/",
1158 std::string())
1159 {
1160 entityPrivileges = {
1161 {boost::beast::http::verb::get, {{"Login"}}},
1162 {boost::beast::http::verb::head, {{"Login"}}},
1163 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1164 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1165 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1166 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1167 }
1168
zhanghch058d1b46d2021-04-01 11:18:24 +08001169 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1170 const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001171 const std::vector<std::string>&) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001172 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001173
Marri Devender Rao37cce912019-02-20 01:05:22 -06001174 long id = getIDFromURL(req.url);
1175 if (id < 0)
1176 {
1177 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1178 messages::internalError(asyncResp->res);
1179 return;
1180 }
1181 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1182 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1183 std::to_string(id);
1184 std::string objectPath = certs::ldapObjectPath;
1185 objectPath += "/";
1186 objectPath += std::to_string(id);
1187 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1188 id, certURL, "LDAP Certificate");
1189 }
1190}; // LDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001191/**
1192 * Collection of TrustStoreCertificate certificates
1193 */
1194class TrustStoreCertificateCollection : public Node
1195{
1196 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001197 TrustStoreCertificateCollection(App& app) :
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001198 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1199 {
1200 entityPrivileges = {
1201 {boost::beast::http::verb::get, {{"Login"}}},
1202 {boost::beast::http::verb::head, {{"Login"}}},
1203 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1204 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1205 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1206 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1207 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001208 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1209 const crow::Request&, const std::vector<std::string>&) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001210 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001211 asyncResp->res.jsonValue = {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001212 {"@odata.id", "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1213 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001214 {"Name", "TrustStore Certificates Collection"},
1215 {"Description",
1216 "A Collection of TrustStore certificate instances"}};
zhanghch058d1b46d2021-04-01 11:18:24 +08001217
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001218 crow::connections::systemBus->async_method_call(
1219 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001220 const ManagedObjectType& certs) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001221 if (ec)
1222 {
1223 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1224 messages::internalError(asyncResp->res);
1225 return;
1226 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001227 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001228 members = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001229 for (const auto& cert : certs)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001230 {
1231 long id = getIDFromURL(cert.first.str);
1232 if (id >= 0)
1233 {
1234 members.push_back(
1235 {{"@odata.id", "/redfish/v1/Managers/bmc/"
1236 "Truststore/Certificates/" +
1237 std::to_string(id)}});
1238 }
1239 }
1240 asyncResp->res.jsonValue["Members@odata.count"] =
1241 members.size();
1242 },
1243 certs::authorityServiceName, certs::authorityObjectPath,
1244 certs::dbusObjManagerIntf, "GetManagedObjects");
1245 }
1246
zhanghch058d1b46d2021-04-01 11:18:24 +08001247 void doPost(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1248 const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001249 const std::vector<std::string>&) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001250 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001251
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001252 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1253
1254 if (certFileBody.empty())
1255 {
1256 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1257 messages::unrecognizedRequestBody(asyncResp->res);
1258 return;
1259 }
1260
1261 std::shared_ptr<CertificateFile> certFile =
1262 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001263 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001264 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001265 const std::string& objectPath) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001266 if (ec)
1267 {
1268 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1269 messages::internalError(asyncResp->res);
1270 return;
1271 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001272 long certId = getIDFromURL(objectPath);
1273 if (certId < 0)
1274 {
1275 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1276 << objectPath;
1277 messages::internalError(asyncResp->res);
1278 return;
1279 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001280 std::string certURL = "/redfish/v1/Managers/bmc/"
1281 "Truststore/Certificates/" +
1282 std::to_string(certId);
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001283
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001284 getCertificateProperties(asyncResp, objectPath,
1285 certs::authorityServiceName, certId,
1286 certURL, "TrustStore Certificate");
1287 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1288 << certFile->getCertFilePath();
1289 },
1290 certs::authorityServiceName, certs::authorityObjectPath,
1291 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1292 }
1293}; // TrustStoreCertificateCollection
1294
1295/**
1296 * Certificate resource describes a certificate used to prove the identity
1297 * of a component, account or service.
1298 */
1299class TrustStoreCertificate : public Node
1300{
1301 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001302 TrustStoreCertificate(App& app) :
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001303 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/",
1304 std::string())
1305 {
1306 entityPrivileges = {
1307 {boost::beast::http::verb::get, {{"Login"}}},
1308 {boost::beast::http::verb::head, {{"Login"}}},
1309 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1310 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1311 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1312 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1313 }
1314
zhanghch058d1b46d2021-04-01 11:18:24 +08001315 void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1316 const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001317 const std::vector<std::string>&) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001318 {
zhanghch058d1b46d2021-04-01 11:18:24 +08001319
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001320 long id = getIDFromURL(req.url);
1321 if (id < 0)
1322 {
1323 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1324 messages::internalError(asyncResp->res);
1325 return;
1326 }
1327 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1328 << std::to_string(id);
1329 std::string certURL =
1330 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1331 std::to_string(id);
1332 std::string objectPath = certs::authorityObjectPath;
1333 objectPath += "/";
1334 objectPath += std::to_string(id);
1335 getCertificateProperties(asyncResp, objectPath,
1336 certs::authorityServiceName, id, certURL,
1337 "TrustStore Certificate");
1338 }
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001339
zhanghch058d1b46d2021-04-01 11:18:24 +08001340 void doDelete(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1341 const crow::Request& req,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001342 const std::vector<std::string>& params) override
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001343 {
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001344
1345 if (params.size() != 1)
1346 {
1347 messages::internalError(asyncResp->res);
1348 return;
1349 }
1350
1351 long id = getIDFromURL(req.url);
1352 if (id < 0)
1353 {
1354 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1355 messages::resourceNotFound(asyncResp->res, "TrustStore Certificate",
1356 std::string(req.url));
1357 return;
1358 }
1359 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1360 << std::to_string(id);
1361 std::string certPath = certs::authorityObjectPath;
1362 certPath += "/";
1363 certPath += std::to_string(id);
1364
1365 crow::connections::systemBus->async_method_call(
1366 [asyncResp, id](const boost::system::error_code ec) {
1367 if (ec)
1368 {
1369 messages::resourceNotFound(asyncResp->res,
1370 "TrustStore Certificate",
1371 std::to_string(id));
1372 return;
1373 }
1374 BMCWEB_LOG_INFO << "Certificate deleted";
1375 asyncResp->res.result(boost::beast::http::status::no_content);
1376 },
1377 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1378 "Delete");
1379 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001380}; // TrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001381} // namespace redfish