blob: d690dcf857d62f9eb1284953115453e8a68c8616 [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001/*
2// Copyright (c) 2018 IBM Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
19
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +020020#include <boost/convert.hpp>
21#include <boost/convert/strtol.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050022
Marri Devender Rao5968cae2019-01-21 10:27:12 -060023#include <variant>
24namespace redfish
25{
26namespace certs
27{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050028constexpr char const* httpsObjectPath =
Marri Devender Rao5968cae2019-01-21 10:27:12 -060029 "/xyz/openbmc_project/certs/server/https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050030constexpr char const* certInstallIntf = "xyz.openbmc_project.Certs.Install";
31constexpr char const* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
32constexpr char const* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
33constexpr char const* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
34constexpr char const* dbusPropIntf = "org.freedesktop.DBus.Properties";
35constexpr char const* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
36constexpr char const* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
37constexpr char const* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060038 "xyz.openbmc_project.Certs.Manager.Server.Https";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039constexpr char const* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060040 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050041constexpr char const* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050042 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050043constexpr char const* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050044 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060045} // namespace certs
46
47/**
48 * The Certificate schema defines a Certificate Service which represents the
49 * actions available to manage certificates and links to where certificates
50 * are installed.
51 */
52class CertificateService : public Node
53{
54 public:
Ed Tanous52cc1122020-07-18 13:51:21 -070055 CertificateService(App& app) : Node(app, "/redfish/v1/CertificateService/")
Marri Devender Rao5968cae2019-01-21 10:27:12 -060056 {
57 // TODO: Issue#61 No entries are available for Certificate
Gunnar Mills4e0453b2020-07-08 14:00:30 -050058 // service at https://www.dmtf.org/standards/redfish
Marri Devender Rao5968cae2019-01-21 10:27:12 -060059 // "redfish standard registries". Need to modify after DMTF
60 // publish Privilege details for certificate service
61 entityPrivileges = {
62 {boost::beast::http::verb::get, {{"Login"}}},
63 {boost::beast::http::verb::head, {{"Login"}}},
64 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
65 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
66 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
67 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
68 }
69
70 private:
Ed Tanouscb13a392020-07-25 19:02:03 +000071 void doGet(crow::Response& res, const crow::Request&,
72 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -060073 {
74 res.jsonValue = {
75 {"@odata.type", "#CertificateService.v1_0_0.CertificateService"},
76 {"@odata.id", "/redfish/v1/CertificateService"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -060077 {"Id", "CertificateService"},
78 {"Name", "Certificate Service"},
79 {"Description", "Actions available to manage certificates"}};
80 res.jsonValue["CertificateLocations"] = {
81 {"@odata.id",
82 "/redfish/v1/CertificateService/CertificateLocations"}};
83 res.jsonValue["Actions"]["#CertificateService.ReplaceCertificate"] = {
84 {"target", "/redfish/v1/CertificateService/Actions/"
85 "CertificateService.ReplaceCertificate"},
86 {"CertificateType@Redfish.AllowableValues", {"PEM"}}};
Marri Devender Rao30215812019-03-18 08:59:21 -050087 res.jsonValue["Actions"]["#CertificateService.GenerateCSR"] = {
88 {"target", "/redfish/v1/CertificateService/Actions/"
89 "CertificateService.GenerateCSR"}};
Marri Devender Rao5968cae2019-01-21 10:27:12 -060090 res.end();
91 }
92}; // CertificateService
Marri Devender Rao37cce912019-02-20 01:05:22 -060093
Marri Devender Rao5968cae2019-01-21 10:27:12 -060094/**
95 * @brief Find the ID specified in the URL
96 * Finds the numbers specified after the last "/" in the URL and returns.
97 * @param[in] path URL
98 * @return -1 on failure and number on success
99 */
Ed Tanous23a21a12020-07-25 04:45:05 +0000100inline long getIDFromURL(const std::string_view url)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600101{
Ed Tanousf23b7292020-10-15 09:41:17 -0700102 std::size_t found = url.rfind('/');
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600103 if (found == std::string::npos)
104 {
105 return -1;
106 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200107
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600108 if ((found + 1) < url.length())
109 {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600110 std::string_view str = url.substr(found + 1);
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200111
112 return boost::convert<long>(str, boost::cnv::strtol()).value_or(-1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600113 }
Iwona Klimaszewskae6604b12019-10-23 00:52:55 +0200114
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600115 return -1;
116}
117
Ed Tanous23a21a12020-07-25 04:45:05 +0000118inline std::string
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500119 getCertificateFromReqBody(const std::shared_ptr<AsyncResp>& asyncResp,
120 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200121{
122 nlohmann::json reqJson = nlohmann::json::parse(req.body, nullptr, false);
123
124 if (reqJson.is_discarded())
125 {
126 // We did not receive JSON request, proceed as it is RAW data
127 return req.body;
128 }
129
130 std::string certificate;
131 std::optional<std::string> certificateType = "PEM";
132
133 if (!json_util::readJson(reqJson, asyncResp->res, "CertificateString",
134 certificate, "CertificateType", certificateType))
135 {
136 BMCWEB_LOG_ERROR << "Required parameters are missing";
137 messages::internalError(asyncResp->res);
138 return std::string();
139 }
140
141 if (*certificateType != "PEM")
142 {
143 messages::propertyValueNotInList(asyncResp->res, *certificateType,
144 "CertificateType");
145 return std::string();
146 }
147
148 return certificate;
149}
150
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600151/**
152 * Class to create a temporary certificate file for uploading to system
153 */
154class CertificateFile
155{
156 public:
157 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500158 CertificateFile(const CertificateFile&) = delete;
159 CertificateFile& operator=(const CertificateFile&) = delete;
160 CertificateFile(CertificateFile&&) = delete;
161 CertificateFile& operator=(CertificateFile&&) = delete;
162 CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600163 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700164 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700165 'e', 'r', 't', 's', '.', 'X',
166 'X', 'X', 'X', 'X', 'X', '\0'};
167 char* tempDirectory = mkdtemp(dirTemplate.data());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600168 if (tempDirectory)
169 {
170 certDirectory = tempDirectory;
171 certificateFile = certDirectory / "cert.pem";
172 std::ofstream out(certificateFile, std::ofstream::out |
173 std::ofstream::binary |
174 std::ofstream::trunc);
175 out << certString;
176 out.close();
177 BMCWEB_LOG_DEBUG << "Creating certificate file" << certificateFile;
178 }
179 }
180 ~CertificateFile()
181 {
182 if (std::filesystem::exists(certDirectory))
183 {
184 BMCWEB_LOG_DEBUG << "Removing certificate file" << certificateFile;
Ed Tanous23a21a12020-07-25 04:45:05 +0000185 std::error_code ec;
186 std::filesystem::remove_all(certDirectory, ec);
187 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600188 {
189 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
190 << certDirectory;
191 }
192 }
193 }
194 std::string getCertFilePath()
195 {
196 return certificateFile;
197 }
198
199 private:
200 std::filesystem::path certificateFile;
201 std::filesystem::path certDirectory;
202};
203
Marri Devender Rao30215812019-03-18 08:59:21 -0500204static std::unique_ptr<sdbusplus::bus::match::match> csrMatcher;
205/**
206 * @brief Read data from CSR D-bus object and set to response
207 *
208 * @param[in] asyncResp Shared pointer to the response message
209 * @param[in] certURI Link to certifiate collection URI
210 * @param[in] service D-Bus service name
211 * @param[in] certObjPath certificate D-Bus object path
212 * @param[in] csrObjPath CSR D-Bus object path
213 * @return None
214 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500215static void getCSR(const std::shared_ptr<AsyncResp>& asyncResp,
216 const std::string& certURI, const std::string& service,
217 const std::string& certObjPath,
218 const std::string& csrObjPath)
Marri Devender Rao30215812019-03-18 08:59:21 -0500219{
220 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
221 << " CSRObjectPath=" << csrObjPath
222 << " service=" << service;
223 crow::connections::systemBus->async_method_call(
224 [asyncResp, certURI](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500225 const std::string& csr) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500226 if (ec)
227 {
228 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
229 messages::internalError(asyncResp->res);
230 return;
231 }
232 if (csr.empty())
233 {
234 BMCWEB_LOG_ERROR << "CSR read is empty";
235 messages::internalError(asyncResp->res);
236 return;
237 }
238 asyncResp->res.jsonValue["CSRString"] = csr;
239 asyncResp->res.jsonValue["CertificateCollection"] = {
240 {"@odata.id", certURI}};
241 },
242 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
243}
244
245/**
246 * Action to Generate CSR
247 */
248class CertificateActionGenerateCSR : public Node
249{
250 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700251 CertificateActionGenerateCSR(App& app) :
Marri Devender Rao30215812019-03-18 08:59:21 -0500252 Node(app, "/redfish/v1/CertificateService/Actions/"
253 "CertificateService.GenerateCSR/")
254 {
255 entityPrivileges = {
256 {boost::beast::http::verb::get, {{"Login"}}},
257 {boost::beast::http::verb::head, {{"Login"}}},
258 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
259 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
260 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
261 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
262 }
263
264 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500265 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +0000266 const std::vector<std::string>&) override
Marri Devender Rao30215812019-03-18 08:59:21 -0500267 {
Ed Tanous2c70f802020-09-28 14:29:23 -0700268 static const int rsaKeyBitLength = 2048;
Marri Devender Rao30215812019-03-18 08:59:21 -0500269 auto asyncResp = std::make_shared<AsyncResp>(res);
270 // Required parameters
271 std::string city;
272 std::string commonName;
273 std::string country;
274 std::string organization;
275 std::string organizationalUnit;
276 std::string state;
277 nlohmann::json certificateCollection;
278
279 // Optional parameters
280 std::optional<std::vector<std::string>> optAlternativeNames =
281 std::vector<std::string>();
282 std::optional<std::string> optContactPerson = "";
283 std::optional<std::string> optChallengePassword = "";
284 std::optional<std::string> optEmail = "";
285 std::optional<std::string> optGivenName = "";
286 std::optional<std::string> optInitials = "";
Ed Tanous2c70f802020-09-28 14:29:23 -0700287 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
Vernon Maueryaaf32062020-03-09 10:41:31 -0700288 std::optional<std::string> optKeyCurveId = "secp384r1";
Marri Devender Rao30215812019-03-18 08:59:21 -0500289 std::optional<std::string> optKeyPairAlgorithm = "EC";
290 std::optional<std::vector<std::string>> optKeyUsage =
291 std::vector<std::string>();
292 std::optional<std::string> optSurname = "";
293 std::optional<std::string> optUnstructuredName = "";
294 if (!json_util::readJson(
295 req, asyncResp->res, "City", city, "CommonName", commonName,
296 "ContactPerson", optContactPerson, "Country", country,
297 "Organization", organization, "OrganizationalUnit",
298 organizationalUnit, "State", state, "CertificateCollection",
299 certificateCollection, "AlternativeNames", optAlternativeNames,
300 "ChallengePassword", optChallengePassword, "Email", optEmail,
301 "GivenName", optGivenName, "Initials", optInitials,
302 "KeyBitLength", optKeyBitLength, "KeyCurveId", optKeyCurveId,
303 "KeyPairAlgorithm", optKeyPairAlgorithm, "KeyUsage",
304 optKeyUsage, "Surname", optSurname, "UnstructuredName",
305 optUnstructuredName))
306 {
307 return;
308 }
309
310 // bmcweb has no way to store or decode a private key challenge
311 // password, which will likely cause bmcweb to crash on startup if this
312 // is not set on a post so not allowing the user to set value
313 if (*optChallengePassword != "")
314 {
315 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
316 "ChallengePassword");
317 return;
318 }
319
320 std::string certURI;
321 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
322 "@odata.id", certURI))
323 {
324 return;
325 }
326
327 std::string objectPath;
328 std::string service;
329 if (boost::starts_with(
330 certURI,
331 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
332 {
333 objectPath = certs::httpsObjectPath;
334 service = certs::httpsServiceName;
335 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500336 else if (boost::starts_with(
337 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
338 {
339 objectPath = certs::ldapObjectPath;
340 service = certs::ldapServiceName;
341 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500342 else
343 {
344 messages::actionParameterNotSupported(
345 asyncResp->res, "CertificateCollection", "GenerateCSR");
346 return;
347 }
348
349 // supporting only EC and RSA algorithm
350 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
351 {
352 messages::actionParameterNotSupported(
353 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
354 return;
355 }
356
357 // supporting only 2048 key bit length for RSA algorithm due to time
358 // consumed in generating private key
359 if (*optKeyPairAlgorithm == "RSA" &&
Ed Tanous2c70f802020-09-28 14:29:23 -0700360 *optKeyBitLength != rsaKeyBitLength)
Marri Devender Rao30215812019-03-18 08:59:21 -0500361 {
362 messages::propertyValueNotInList(asyncResp->res,
363 std::to_string(*optKeyBitLength),
364 "KeyBitLength");
365 return;
366 }
367
368 // validate KeyUsage supporting only 1 type based on URL
369 if (boost::starts_with(
370 certURI,
371 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
372 {
373 if (optKeyUsage->size() == 0)
374 {
375 optKeyUsage->push_back("ServerAuthentication");
376 }
377 else if (optKeyUsage->size() == 1)
378 {
379 if ((*optKeyUsage)[0] != "ServerAuthentication")
380 {
381 messages::propertyValueNotInList(
382 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
383 return;
384 }
385 }
386 else
387 {
388 messages::actionParameterNotSupported(
389 asyncResp->res, "KeyUsage", "GenerateCSR");
390 return;
391 }
392 }
Marri Devender Rao3b7f0142019-05-17 02:53:23 -0500393 else if (boost::starts_with(
394 certURI, "/redfish/v1/AccountService/LDAP/Certificates"))
395 {
396 if (optKeyUsage->size() == 0)
397 {
398 optKeyUsage->push_back("ClientAuthentication");
399 }
400 else if (optKeyUsage->size() == 1)
401 {
402 if ((*optKeyUsage)[0] != "ClientAuthentication")
403 {
404 messages::propertyValueNotInList(
405 asyncResp->res, (*optKeyUsage)[0], "KeyUsage");
406 return;
407 }
408 }
409 else
410 {
411 messages::actionParameterNotSupported(
412 asyncResp->res, "KeyUsage", "GenerateCSR");
413 return;
414 }
415 }
Marri Devender Rao30215812019-03-18 08:59:21 -0500416
417 // Only allow one CSR matcher at a time so setting retry time-out and
418 // timer expiry to 10 seconds for now.
Ed Tanous2c70f802020-09-28 14:29:23 -0700419 static const int timeOut = 10;
Marri Devender Rao30215812019-03-18 08:59:21 -0500420 if (csrMatcher)
421 {
Marri Devender Rao30215812019-03-18 08:59:21 -0500422 messages::serviceTemporarilyUnavailable(asyncResp->res,
Ed Tanous2c70f802020-09-28 14:29:23 -0700423 std::to_string(timeOut));
Marri Devender Rao30215812019-03-18 08:59:21 -0500424 return;
425 }
426
427 // Make this static so it survives outside this method
428 static boost::asio::steady_timer timeout(*req.ioService);
Ed Tanous2c70f802020-09-28 14:29:23 -0700429 timeout.expires_after(std::chrono::seconds(timeOut));
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500430 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500431 csrMatcher = nullptr;
432 if (ec)
433 {
434 // operation_aborted is expected if timer is canceled before
435 // completion.
436 if (ec != boost::asio::error::operation_aborted)
437 {
438 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
439 }
440 return;
441 }
442 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
443 messages::internalError(asyncResp->res);
444 });
445
446 // create a matcher to wait on CSR object
447 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
448 std::string match("type='signal',"
449 "interface='org.freedesktop.DBus.ObjectManager',"
450 "path='" +
451 objectPath +
452 "',"
453 "member='InterfacesAdded'");
454 csrMatcher = std::make_unique<sdbusplus::bus::match::match>(
455 *crow::connections::systemBus, match,
456 [asyncResp, service, objectPath,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500457 certURI](sdbusplus::message::message& m) {
Ed Tanous271584a2019-07-09 16:24:22 -0700458 timeout.cancel();
Marri Devender Rao30215812019-03-18 08:59:21 -0500459 if (m.is_method_error())
460 {
461 BMCWEB_LOG_ERROR << "Dbus method error!!!";
462 messages::internalError(asyncResp->res);
463 return;
464 }
465 std::vector<std::pair<
466 std::string, std::vector<std::pair<
467 std::string, std::variant<std::string>>>>>
468 interfacesProperties;
469 sdbusplus::message::object_path csrObjectPath;
470 m.read(csrObjectPath, interfacesProperties);
471 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500472 for (auto& interface : interfacesProperties)
Marri Devender Rao30215812019-03-18 08:59:21 -0500473 {
474 if (interface.first == "xyz.openbmc_project.Certs.CSR")
475 {
476 getCSR(asyncResp, certURI, service, objectPath,
477 csrObjectPath.str);
478 break;
479 }
480 }
481 });
482 crow::connections::systemBus->async_method_call(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500483 [asyncResp](const boost::system::error_code& ec,
Ed Tanouscb13a392020-07-25 19:02:03 +0000484 const std::string&) {
Marri Devender Rao30215812019-03-18 08:59:21 -0500485 if (ec)
486 {
487 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
488 messages::internalError(asyncResp->res);
489 return;
490 }
491 },
492 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
493 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
494 commonName, *optContactPerson, country, *optEmail, *optGivenName,
495 *optInitials, *optKeyBitLength, *optKeyCurveId,
496 *optKeyPairAlgorithm, *optKeyUsage, organization,
497 organizationalUnit, state, *optSurname, *optUnstructuredName);
498 }
499}; // CertificateActionGenerateCSR
500
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600501/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500502 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600503 *
504 * @param[in] asyncResp Shared pointer to the response message
505 * @param[in] str Issuer/Subject value in key=value pairs
506 * @param[in] type Issuer/Subject
507 * @return None
508 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500509static void updateCertIssuerOrSubject(nlohmann::json& out,
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600510 const std::string_view value)
511{
512 // example: O=openbmc-project.xyz,CN=localhost
513 std::string_view::iterator i = value.begin();
514 while (i != value.end())
515 {
516 std::string_view::iterator tokenBegin = i;
517 while (i != value.end() && *i != '=')
518 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530519 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600520 }
521 if (i == value.end())
522 {
523 break;
524 }
Ed Tanous271584a2019-07-09 16:24:22 -0700525 const std::string_view key(tokenBegin,
526 static_cast<size_t>(i - tokenBegin));
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530527 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600528 tokenBegin = i;
529 while (i != value.end() && *i != ',')
530 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530531 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600532 }
Ed Tanous271584a2019-07-09 16:24:22 -0700533 const std::string_view val(tokenBegin,
534 static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600535 if (key == "L")
536 {
537 out["City"] = val;
538 }
539 else if (key == "CN")
540 {
541 out["CommonName"] = val;
542 }
543 else if (key == "C")
544 {
545 out["Country"] = val;
546 }
547 else if (key == "O")
548 {
549 out["Organization"] = val;
550 }
551 else if (key == "OU")
552 {
553 out["OrganizationalUnit"] = val;
554 }
555 else if (key == "ST")
556 {
557 out["State"] = val;
558 }
559 // skip comma character
560 if (i != value.end())
561 {
Manojkiran Eda17a897d2020-09-12 15:31:58 +0530562 ++i;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600563 }
564 }
565}
566
567/**
568 * @brief Retrieve the certificates properties and append to the response
569 * message
570 *
571 * @param[in] asyncResp Shared pointer to the response message
572 * @param[in] objectPath Path of the D-Bus service object
573 * @param[in] certId Id of the certificate
574 * @param[in] certURL URL of the certificate object
575 * @param[in] name name of the certificate
576 * @return None
577 */
578static void getCertificateProperties(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500579 const std::shared_ptr<AsyncResp>& asyncResp, const std::string& objectPath,
580 const std::string& service, long certId, const std::string& certURL,
581 const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600582{
583 using PropertyType =
584 std::variant<std::string, uint64_t, std::vector<std::string>>;
585 using PropertiesMap = boost::container::flat_map<std::string, PropertyType>;
586 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
587 << " certId=" << certId << " certURl=" << certURL;
588 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600589 [asyncResp, certURL, certId, name](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500590 const PropertiesMap& properties) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600591 if (ec)
592 {
593 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500594 messages::resourceNotFound(asyncResp->res, name,
595 std::to_string(certId));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600596 return;
597 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600598 asyncResp->res.jsonValue = {
599 {"@odata.id", certURL},
600 {"@odata.type", "#Certificate.v1_0_0.Certificate"},
Marri Devender Rao37cce912019-02-20 01:05:22 -0600601 {"Id", std::to_string(certId)},
602 {"Name", name},
603 {"Description", name}};
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500604 for (const auto& property : properties)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600605 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600606 if (property.first == "CertificateString")
607 {
608 asyncResp->res.jsonValue["CertificateString"] = "";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500609 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600610 std::get_if<std::string>(&property.second);
611 if (value)
612 {
613 asyncResp->res.jsonValue["CertificateString"] = *value;
614 }
615 }
616 else if (property.first == "KeyUsage")
617 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500618 nlohmann::json& keyUsage =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600619 asyncResp->res.jsonValue["KeyUsage"];
620 keyUsage = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500621 const std::vector<std::string>* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600622 std::get_if<std::vector<std::string>>(&property.second);
623 if (value)
624 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500625 for (const std::string& usage : *value)
Marri Devender Rao37cce912019-02-20 01:05:22 -0600626 {
627 keyUsage.push_back(usage);
628 }
629 }
630 }
631 else if (property.first == "Issuer")
632 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500633 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600634 std::get_if<std::string>(&property.second);
635 if (value)
636 {
637 updateCertIssuerOrSubject(
638 asyncResp->res.jsonValue["Issuer"], *value);
639 }
640 }
641 else if (property.first == "Subject")
642 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500643 const std::string* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600644 std::get_if<std::string>(&property.second);
645 if (value)
646 {
647 updateCertIssuerOrSubject(
648 asyncResp->res.jsonValue["Subject"], *value);
649 }
650 }
651 else if (property.first == "ValidNotAfter")
652 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500653 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600654 std::get_if<uint64_t>(&property.second);
655 if (value)
656 {
657 std::time_t time = static_cast<std::time_t>(*value);
658 asyncResp->res.jsonValue["ValidNotAfter"] =
659 crow::utility::getDateTime(time);
660 }
661 }
662 else if (property.first == "ValidNotBefore")
663 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500664 const uint64_t* value =
Marri Devender Rao37cce912019-02-20 01:05:22 -0600665 std::get_if<uint64_t>(&property.second);
666 if (value)
667 {
668 std::time_t time = static_cast<std::time_t>(*value);
669 asyncResp->res.jsonValue["ValidNotBefore"] =
670 crow::utility::getDateTime(time);
671 }
672 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600673 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600674 asyncResp->res.addHeader("Location", certURL);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600675 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600676 service, objectPath, certs::dbusPropIntf, "GetAll",
677 certs::certPropIntf);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600678}
679
680using GetObjectType =
681 std::vector<std::pair<std::string, std::vector<std::string>>>;
682
683/**
684 * Action to replace an existing certificate
685 */
686class CertificateActionsReplaceCertificate : public Node
687{
688 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700689 CertificateActionsReplaceCertificate(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600690 Node(app, "/redfish/v1/CertificateService/Actions/"
691 "CertificateService.ReplaceCertificate/")
692 {
693 entityPrivileges = {
694 {boost::beast::http::verb::get, {{"Login"}}},
695 {boost::beast::http::verb::head, {{"Login"}}},
696 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
697 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
698 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
699 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
700 }
701
702 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500703 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +0000704 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600705 {
706 std::string certificate;
707 nlohmann::json certificateUri;
708 std::optional<std::string> certificateType = "PEM";
709 auto asyncResp = std::make_shared<AsyncResp>(res);
710 if (!json_util::readJson(req, asyncResp->res, "CertificateString",
711 certificate, "CertificateUri", certificateUri,
712 "CertificateType", certificateType))
713 {
714 BMCWEB_LOG_ERROR << "Required parameters are missing";
715 messages::internalError(asyncResp->res);
716 return;
717 }
718
719 if (!certificateType)
720 {
721 // should never happen, but it never hurts to be paranoid.
722 return;
723 }
724 if (certificateType != "PEM")
725 {
726 messages::actionParameterNotSupported(
727 asyncResp->res, "CertificateType", "ReplaceCertificate");
728 return;
729 }
730
731 std::string certURI;
732 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
733 "@odata.id", certURI))
734 {
735 messages::actionParameterMissing(
736 asyncResp->res, "ReplaceCertificate", "CertificateUri");
737 return;
738 }
739
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600740 BMCWEB_LOG_INFO << "Certificate URI to replace" << certURI;
741 long id = getIDFromURL(certURI);
742 if (id < 0)
743 {
744 messages::actionParameterValueFormatError(asyncResp->res, certURI,
745 "CertificateUri",
746 "ReplaceCertificate");
747 return;
748 }
749 std::string objectPath;
750 std::string name;
Marri Devender Rao37cce912019-02-20 01:05:22 -0600751 std::string service;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600752 if (boost::starts_with(
753 certURI,
754 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"))
755 {
756 objectPath =
757 std::string(certs::httpsObjectPath) + "/" + std::to_string(id);
758 name = "HTTPS certificate";
Marri Devender Rao37cce912019-02-20 01:05:22 -0600759 service = certs::httpsServiceName;
760 }
761 else if (boost::starts_with(
762 certURI, "/redfish/v1/AccountService/LDAP/Certificates/"))
763 {
764 objectPath =
765 std::string(certs::ldapObjectPath) + "/" + std::to_string(id);
766 name = "LDAP certificate";
767 service = certs::ldapServiceName;
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600768 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -0500769 else if (boost::starts_with(
770 certURI,
771 "/redfish/v1/Managers/bmc/Truststore/Certificates/"))
772 {
773 objectPath = std::string(certs::authorityObjectPath) + "/" +
774 std::to_string(id);
775 name = "TrustStore certificate";
776 service = certs::authorityServiceName;
777 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600778 else
779 {
780 messages::actionParameterNotSupported(
781 asyncResp->res, "CertificateUri", "ReplaceCertificate");
782 return;
783 }
784
785 std::shared_ptr<CertificateFile> certFile =
786 std::make_shared<CertificateFile>(certificate);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600787 crow::connections::systemBus->async_method_call(
Marri Devender Rao37cce912019-02-20 01:05:22 -0600788 [asyncResp, certFile, objectPath, service, certURI, id,
789 name](const boost::system::error_code ec) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600790 if (ec)
791 {
792 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Marri Devender Rao8aae75a2019-07-16 03:26:50 -0500793 messages::resourceNotFound(asyncResp->res, name,
794 std::to_string(id));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600795 return;
796 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600797 getCertificateProperties(asyncResp, objectPath, service, id,
798 certURI, name);
799 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
800 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600801 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600802 service, objectPath, certs::certReplaceIntf, "Replace",
803 certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600804 }
805}; // CertificateActionsReplaceCertificate
806
807/**
808 * Certificate resource describes a certificate used to prove the identity
809 * of a component, account or service.
810 */
811class HTTPSCertificate : public Node
812{
813 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700814 HTTPSCertificate(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600815 Node(app,
816 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/"
817 "<str>/",
818 std::string())
819 {
820 entityPrivileges = {
821 {boost::beast::http::verb::get, {{"Login"}}},
822 {boost::beast::http::verb::head, {{"Login"}}},
823 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
824 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
825 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
826 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
827 }
828
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500829 void doGet(crow::Response& res, const crow::Request& req,
830 const std::vector<std::string>& params) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600831 {
832 auto asyncResp = std::make_shared<AsyncResp>(res);
833 if (params.size() != 1)
834 {
835 messages::internalError(asyncResp->res);
836 return;
837 }
838 long id = getIDFromURL(req.url);
839
840 BMCWEB_LOG_DEBUG << "HTTPSCertificate::doGet ID=" << std::to_string(id);
841 std::string certURL =
842 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/" +
843 std::to_string(id);
844 std::string objectPath = certs::httpsObjectPath;
845 objectPath += "/";
846 objectPath += std::to_string(id);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600847 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
848 id, certURL, "HTTPS Certificate");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600849 }
850
851}; // namespace redfish
852
853/**
854 * Collection of HTTPS certificates
855 */
856class HTTPSCertificateCollection : public Node
857{
858 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700859 HTTPSCertificateCollection(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600860 Node(app,
861 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
862 {
863 entityPrivileges = {
864 {boost::beast::http::verb::get, {{"Login"}}},
865 {boost::beast::http::verb::head, {{"Login"}}},
866 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
867 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
868 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
869 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
870 }
Ed Tanouscb13a392020-07-25 19:02:03 +0000871 void doGet(crow::Response& res, const crow::Request&,
872 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600873 {
874 res.jsonValue = {
875 {"@odata.id",
876 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"},
877 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600878 {"Name", "HTTPS Certificates Collection"},
879 {"Description", "A Collection of HTTPS certificate instances"}};
880 auto asyncResp = std::make_shared<AsyncResp>(res);
881 crow::connections::systemBus->async_method_call(
882 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500883 const ManagedObjectType& certs) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600884 if (ec)
885 {
886 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
887 messages::internalError(asyncResp->res);
888 return;
889 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500890 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
Marri Devender Rao37cce912019-02-20 01:05:22 -0600891 members = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500892 for (const auto& cert : certs)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600893 {
Marri Devender Rao37cce912019-02-20 01:05:22 -0600894 long id = getIDFromURL(cert.first.str);
895 if (id >= 0)
896 {
897 members.push_back(
898 {{"@odata.id",
899 "/redfish/v1/Managers/bmc/"
900 "NetworkProtocol/HTTPS/Certificates/" +
901 std::to_string(id)}});
902 }
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600903 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600904 asyncResp->res.jsonValue["Members@odata.count"] =
905 members.size();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600906 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600907 certs::httpsServiceName, certs::httpsObjectPath,
908 certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600909 }
910
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500911 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +0000912 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600913 {
914 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
915 auto asyncResp = std::make_shared<AsyncResp>(res);
916 asyncResp->res.jsonValue = {{"Name", "HTTPS Certificate"},
917 {"Description", "HTTPS Certificate"}};
918
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200919 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
920
921 if (certFileBody.empty())
922 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +0200923 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
924 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200925 return;
926 }
927
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600928 std::shared_ptr<CertificateFile> certFile =
Kowalski, Kamil58eb2382019-08-12 11:54:31 +0200929 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600930
931 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200932 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500933 const std::string& objectPath) {
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600934 if (ec)
935 {
936 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
937 messages::internalError(asyncResp->res);
938 return;
939 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +0200940 long certId = getIDFromURL(objectPath);
941 if (certId < 0)
942 {
943 BMCWEB_LOG_ERROR << "Invalid objectPath value"
944 << objectPath;
945 messages::internalError(asyncResp->res);
946 return;
947 }
Marri Devender Rao37cce912019-02-20 01:05:22 -0600948 std::string certURL =
949 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/"
950 "Certificates/" +
951 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -0600952 getCertificateProperties(asyncResp, objectPath,
953 certs::httpsServiceName, certId,
954 certURL, "HTTPS Certificate");
955 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
956 << certFile->getCertFilePath();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600957 },
Marri Devender Rao37cce912019-02-20 01:05:22 -0600958 certs::httpsServiceName, certs::httpsObjectPath,
959 certs::certInstallIntf, "Install", certFile->getCertFilePath());
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600960 }
961}; // HTTPSCertificateCollection
962
963/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600964 * The certificate location schema defines a resource that an administrator
965 * can use in order to locate all certificates installed on a given service.
966 */
967class CertificateLocations : public Node
968{
969 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700970 CertificateLocations(App& app) :
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600971 Node(app, "/redfish/v1/CertificateService/CertificateLocations/")
972 {
973 entityPrivileges = {
974 {boost::beast::http::verb::get, {{"Login"}}},
975 {boost::beast::http::verb::head, {{"Login"}}},
976 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
977 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
978 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
979 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
980 }
981
982 private:
Ed Tanouscb13a392020-07-25 19:02:03 +0000983 void doGet(crow::Response& res, const crow::Request&,
984 const std::vector<std::string>&) override
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600985 {
986 res.jsonValue = {
987 {"@odata.id",
988 "/redfish/v1/CertificateService/CertificateLocations"},
989 {"@odata.type",
990 "#CertificateLocations.v1_0_0.CertificateLocations"},
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600991 {"Name", "Certificate Locations"},
992 {"Id", "CertificateLocations"},
993 {"Description",
994 "Defines a resource that an administrator can use in order to "
995 "locate all certificates installed on a given service"}};
996 auto asyncResp = std::make_shared<AsyncResp>(res);
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500997 nlohmann::json& links =
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600998 asyncResp->res.jsonValue["Links"]["Certificates"];
999 links = nlohmann::json::array();
1000 getCertificateLocations(
1001 asyncResp,
1002 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/",
Marri Devender Rao37cce912019-02-20 01:05:22 -06001003 certs::httpsObjectPath, certs::httpsServiceName);
1004 getCertificateLocations(asyncResp,
1005 "/redfish/v1/AccountService/LDAP/Certificates/",
1006 certs::ldapObjectPath, certs::ldapServiceName);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001007 getCertificateLocations(
1008 asyncResp, "/redfish/v1/Managers/bmc/Truststore/Certificates/",
1009 certs::authorityObjectPath, certs::authorityServiceName);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001010 }
1011 /**
1012 * @brief Retrieve the certificates installed list and append to the
1013 * response
1014 *
1015 * @param[in] asyncResp Shared pointer to the response message
1016 * @param[in] certURL Path of the certificate object
1017 * @param[in] path Path of the D-Bus service object
1018 * @return None
1019 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001020 void getCertificateLocations(std::shared_ptr<AsyncResp>& asyncResp,
1021 const std::string& certURL,
1022 const std::string& path,
1023 const std::string& service)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001024 {
1025 BMCWEB_LOG_DEBUG << "getCertificateLocations URI=" << certURL
1026 << " Path=" << path << " service= " << service;
1027 crow::connections::systemBus->async_method_call(
1028 [asyncResp, certURL](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001029 const ManagedObjectType& certs) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001030 if (ec)
1031 {
Jonathan Doman9c8e0392021-02-01 18:51:38 -08001032 BMCWEB_LOG_WARNING
1033 << "Certificate collection query failed: " << ec
1034 << ", skipping " << certURL;
Marri Devender Rao37cce912019-02-20 01:05:22 -06001035 return;
1036 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001037 nlohmann::json& links =
Marri Devender Rao37cce912019-02-20 01:05:22 -06001038 asyncResp->res.jsonValue["Links"]["Certificates"];
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001039 for (auto& cert : certs)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001040 {
1041 long id = getIDFromURL(cert.first.str);
1042 if (id >= 0)
1043 {
1044 links.push_back(
1045 {{"@odata.id", certURL + std::to_string(id)}});
1046 }
1047 }
1048 asyncResp->res.jsonValue["Links"]["Certificates@odata.count"] =
1049 links.size();
1050 },
1051 service, path, certs::dbusObjManagerIntf, "GetManagedObjects");
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001052 }
1053}; // CertificateLocations
Marri Devender Rao37cce912019-02-20 01:05:22 -06001054
1055/**
1056 * Collection of LDAP certificates
1057 */
1058class LDAPCertificateCollection : public Node
1059{
1060 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001061 LDAPCertificateCollection(App& app) :
Marri Devender Rao37cce912019-02-20 01:05:22 -06001062 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1063 {
1064 entityPrivileges = {
1065 {boost::beast::http::verb::get, {{"Login"}}},
1066 {boost::beast::http::verb::head, {{"Login"}}},
1067 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1068 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1069 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1070 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1071 }
Ed Tanouscb13a392020-07-25 19:02:03 +00001072 void doGet(crow::Response& res, const crow::Request&,
1073 const std::vector<std::string>&) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001074 {
1075 res.jsonValue = {
1076 {"@odata.id", "/redfish/v1/AccountService/LDAP/Certificates"},
1077 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Rao37cce912019-02-20 01:05:22 -06001078 {"Name", "LDAP Certificates Collection"},
1079 {"Description", "A Collection of LDAP certificate instances"}};
1080 auto asyncResp = std::make_shared<AsyncResp>(res);
1081 crow::connections::systemBus->async_method_call(
1082 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001083 const ManagedObjectType& certs) {
Jonathan Doman9c8e0392021-02-01 18:51:38 -08001084 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
1085 nlohmann::json& count =
1086 asyncResp->res.jsonValue["Members@odata.count"];
1087 members = nlohmann::json::array();
1088 count = 0;
Marri Devender Rao37cce912019-02-20 01:05:22 -06001089 if (ec)
1090 {
Jonathan Doman9c8e0392021-02-01 18:51:38 -08001091 BMCWEB_LOG_WARNING << "LDAP certificate query failed: "
1092 << ec;
Marri Devender Rao37cce912019-02-20 01:05:22 -06001093 return;
1094 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001095 for (const auto& cert : certs)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001096 {
1097 long id = getIDFromURL(cert.first.str);
1098 if (id >= 0)
1099 {
1100 members.push_back(
1101 {{"@odata.id", "/redfish/v1/AccountService/"
1102 "LDAP/Certificates/" +
1103 std::to_string(id)}});
1104 }
1105 }
Jonathan Doman9c8e0392021-02-01 18:51:38 -08001106 count = members.size();
Marri Devender Rao37cce912019-02-20 01:05:22 -06001107 },
1108 certs::ldapServiceName, certs::ldapObjectPath,
1109 certs::dbusObjManagerIntf, "GetManagedObjects");
1110 }
1111
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001112 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001113 const std::vector<std::string>&) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001114 {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001115 auto asyncResp = std::make_shared<AsyncResp>(res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001116 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1117
1118 if (certFileBody.empty())
1119 {
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001120 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1121 messages::unrecognizedRequestBody(asyncResp->res);
Kowalski, Kamil58eb2382019-08-12 11:54:31 +02001122 return;
1123 }
1124
1125 std::shared_ptr<CertificateFile> certFile =
1126 std::make_shared<CertificateFile>(certFileBody);
1127
Marri Devender Rao37cce912019-02-20 01:05:22 -06001128 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001129 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001130 const std::string& objectPath) {
Marri Devender Rao37cce912019-02-20 01:05:22 -06001131 if (ec)
1132 {
1133 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1134 messages::internalError(asyncResp->res);
1135 return;
1136 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001137 long certId = getIDFromURL(objectPath);
1138 if (certId < 0)
1139 {
1140 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1141 << objectPath;
1142 messages::internalError(asyncResp->res);
1143 return;
1144 }
Marri Devender Rao37cce912019-02-20 01:05:22 -06001145 std::string certURL =
1146 "/redfish/v1/AccountService/LDAP/Certificates/" +
1147 std::to_string(certId);
Marri Devender Rao37cce912019-02-20 01:05:22 -06001148 getCertificateProperties(asyncResp, objectPath,
1149 certs::ldapServiceName, certId,
1150 certURL, "LDAP Certificate");
1151 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1152 << certFile->getCertFilePath();
1153 },
1154 certs::ldapServiceName, certs::ldapObjectPath,
1155 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1156 }
1157}; // LDAPCertificateCollection
1158
1159/**
1160 * Certificate resource describes a certificate used to prove the identity
1161 * of a component, account or service.
1162 */
1163class LDAPCertificate : public Node
1164{
1165 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001166 LDAPCertificate(App& app) :
Marri Devender Rao37cce912019-02-20 01:05:22 -06001167 Node(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/",
1168 std::string())
1169 {
1170 entityPrivileges = {
1171 {boost::beast::http::verb::get, {{"Login"}}},
1172 {boost::beast::http::verb::head, {{"Login"}}},
1173 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1174 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1175 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1176 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1177 }
1178
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001179 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001180 const std::vector<std::string>&) override
Marri Devender Rao37cce912019-02-20 01:05:22 -06001181 {
1182 auto asyncResp = std::make_shared<AsyncResp>(res);
1183 long id = getIDFromURL(req.url);
1184 if (id < 0)
1185 {
1186 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1187 messages::internalError(asyncResp->res);
1188 return;
1189 }
1190 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << std::to_string(id);
1191 std::string certURL = "/redfish/v1/AccountService/LDAP/Certificates/" +
1192 std::to_string(id);
1193 std::string objectPath = certs::ldapObjectPath;
1194 objectPath += "/";
1195 objectPath += std::to_string(id);
1196 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1197 id, certURL, "LDAP Certificate");
1198 }
1199}; // LDAPCertificate
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001200/**
1201 * Collection of TrustStoreCertificate certificates
1202 */
1203class TrustStoreCertificateCollection : public Node
1204{
1205 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001206 TrustStoreCertificateCollection(App& app) :
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001207 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
1208 {
1209 entityPrivileges = {
1210 {boost::beast::http::verb::get, {{"Login"}}},
1211 {boost::beast::http::verb::head, {{"Login"}}},
1212 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1213 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1214 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1215 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1216 }
Ed Tanouscb13a392020-07-25 19:02:03 +00001217 void doGet(crow::Response& res, const crow::Request&,
1218 const std::vector<std::string>&) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001219 {
1220 res.jsonValue = {
1221 {"@odata.id", "/redfish/v1/Managers/bmc/Truststore/Certificates/"},
1222 {"@odata.type", "#CertificateCollection.CertificateCollection"},
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001223 {"Name", "TrustStore Certificates Collection"},
1224 {"Description",
1225 "A Collection of TrustStore certificate instances"}};
1226 auto asyncResp = std::make_shared<AsyncResp>(res);
1227 crow::connections::systemBus->async_method_call(
1228 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001229 const ManagedObjectType& certs) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001230 if (ec)
1231 {
1232 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1233 messages::internalError(asyncResp->res);
1234 return;
1235 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001236 nlohmann::json& members = asyncResp->res.jsonValue["Members"];
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001237 members = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001238 for (const auto& cert : certs)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001239 {
1240 long id = getIDFromURL(cert.first.str);
1241 if (id >= 0)
1242 {
1243 members.push_back(
1244 {{"@odata.id", "/redfish/v1/Managers/bmc/"
1245 "Truststore/Certificates/" +
1246 std::to_string(id)}});
1247 }
1248 }
1249 asyncResp->res.jsonValue["Members@odata.count"] =
1250 members.size();
1251 },
1252 certs::authorityServiceName, certs::authorityObjectPath,
1253 certs::dbusObjManagerIntf, "GetManagedObjects");
1254 }
1255
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001256 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001257 const std::vector<std::string>&) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001258 {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001259 auto asyncResp = std::make_shared<AsyncResp>(res);
Zbigniew Kurzynskia08752f2019-10-10 18:27:18 +02001260 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1261
1262 if (certFileBody.empty())
1263 {
1264 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1265 messages::unrecognizedRequestBody(asyncResp->res);
1266 return;
1267 }
1268
1269 std::shared_ptr<CertificateFile> certFile =
1270 std::make_shared<CertificateFile>(certFileBody);
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001271 crow::connections::systemBus->async_method_call(
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001272 [asyncResp, certFile](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001273 const std::string& objectPath) {
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001274 if (ec)
1275 {
1276 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1277 messages::internalError(asyncResp->res);
1278 return;
1279 }
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001280 long certId = getIDFromURL(objectPath);
1281 if (certId < 0)
1282 {
1283 BMCWEB_LOG_ERROR << "Invalid objectPath value"
1284 << objectPath;
1285 messages::internalError(asyncResp->res);
1286 return;
1287 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001288 std::string certURL = "/redfish/v1/Managers/bmc/"
1289 "Truststore/Certificates/" +
1290 std::to_string(certId);
Zbigniew Kurzynski656ec7e2019-09-30 18:43:28 +02001291
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001292 getCertificateProperties(asyncResp, objectPath,
1293 certs::authorityServiceName, certId,
1294 certURL, "TrustStore Certificate");
1295 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1296 << certFile->getCertFilePath();
1297 },
1298 certs::authorityServiceName, certs::authorityObjectPath,
1299 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1300 }
1301}; // TrustStoreCertificateCollection
1302
1303/**
1304 * Certificate resource describes a certificate used to prove the identity
1305 * of a component, account or service.
1306 */
1307class TrustStoreCertificate : public Node
1308{
1309 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001310 TrustStoreCertificate(App& app) :
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001311 Node(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/",
1312 std::string())
1313 {
1314 entityPrivileges = {
1315 {boost::beast::http::verb::get, {{"Login"}}},
1316 {boost::beast::http::verb::head, {{"Login"}}},
1317 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1318 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1319 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1320 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1321 }
1322
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001323 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001324 const std::vector<std::string>&) override
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001325 {
1326 auto asyncResp = std::make_shared<AsyncResp>(res);
1327 long id = getIDFromURL(req.url);
1328 if (id < 0)
1329 {
1330 BMCWEB_LOG_ERROR << "Invalid url value" << req.url;
1331 messages::internalError(asyncResp->res);
1332 return;
1333 }
1334 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doGet ID="
1335 << std::to_string(id);
1336 std::string certURL =
1337 "/redfish/v1/Managers/bmc/Truststore/Certificates/" +
1338 std::to_string(id);
1339 std::string objectPath = certs::authorityObjectPath;
1340 objectPath += "/";
1341 objectPath += std::to_string(id);
1342 getCertificateProperties(asyncResp, objectPath,
1343 certs::authorityServiceName, id, certURL,
1344 "TrustStore Certificate");
1345 }
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001346
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001347 void doDelete(crow::Response& res, const crow::Request& req,
1348 const std::vector<std::string>& params) override
Zbigniew Kurzynski07a60292019-09-17 15:56:16 +02001349 {
1350 auto asyncResp = std::make_shared<AsyncResp>(res);
1351
1352 if (params.size() != 1)
1353 {
1354 messages::internalError(asyncResp->res);
1355 return;
1356 }
1357
1358 long id = getIDFromURL(req.url);
1359 if (id < 0)
1360 {
1361 BMCWEB_LOG_ERROR << "Invalid url value: " << req.url;
1362 messages::resourceNotFound(asyncResp->res, "TrustStore Certificate",
1363 std::string(req.url));
1364 return;
1365 }
1366 BMCWEB_LOG_DEBUG << "TrustStoreCertificate::doDelete ID="
1367 << std::to_string(id);
1368 std::string certPath = certs::authorityObjectPath;
1369 certPath += "/";
1370 certPath += std::to_string(id);
1371
1372 crow::connections::systemBus->async_method_call(
1373 [asyncResp, id](const boost::system::error_code ec) {
1374 if (ec)
1375 {
1376 messages::resourceNotFound(asyncResp->res,
1377 "TrustStore Certificate",
1378 std::to_string(id));
1379 return;
1380 }
1381 BMCWEB_LOG_INFO << "Certificate deleted";
1382 asyncResp->res.result(boost::beast::http::status::no_content);
1383 },
1384 certs::authorityServiceName, certPath, certs::objDeleteIntf,
1385 "Delete");
1386 }
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001387}; // TrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001388} // namespace redfish