blob: d4df6c8e0184b2cffabdd16771477cd8b4c6a7d9 [file] [log] [blame]
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001#pragma once
2
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "app.hpp"
4#include "async_resp.hpp"
George Liu7a1dbc42022-12-07 16:03:22 +08005#include "dbus_utility.hpp"
Ed Tanous1aa0c2b2022-02-08 12:24:30 +01006#include "http/parsing.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08007#include "http_response.hpp"
8#include "query.hpp"
9#include "registries/privilege_registry.hpp"
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +020010#include "utils/dbus_utils.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080011#include "utils/json_utils.hpp"
12#include "utils/time_utils.hpp"
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +020013
Jiaqing Zhao90d2d1e2022-04-13 17:01:57 +080014#include <boost/system/linux_error.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070015#include <boost/url/format.hpp>
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +020016#include <sdbusplus/asio/property.hpp>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080017#include <sdbusplus/bus/match.hpp>
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +020018#include <sdbusplus/unpack_properties.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050019
George Liu7a1dbc42022-12-07 16:03:22 +080020#include <array>
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080021#include <memory>
George Liu7a1dbc42022-12-07 16:03:22 +080022#include <string_view>
23
Marri Devender Rao5968cae2019-01-21 10:27:12 -060024namespace redfish
25{
26namespace certs
27{
Patrick Williams89492a12023-05-10 07:51:34 -050028constexpr const char* certInstallIntf = "xyz.openbmc_project.Certs.Install";
29constexpr const char* certReplaceIntf = "xyz.openbmc_project.Certs.Replace";
30constexpr const char* objDeleteIntf = "xyz.openbmc_project.Object.Delete";
31constexpr const char* certPropIntf = "xyz.openbmc_project.Certs.Certificate";
32constexpr const char* dbusPropIntf = "org.freedesktop.DBus.Properties";
33constexpr const char* dbusObjManagerIntf = "org.freedesktop.DBus.ObjectManager";
34constexpr const char* httpsServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060035 "xyz.openbmc_project.Certs.Manager.Server.Https";
Patrick Williams89492a12023-05-10 07:51:34 -050036constexpr const char* ldapServiceName =
Marri Devender Rao37cce912019-02-20 01:05:22 -060037 "xyz.openbmc_project.Certs.Manager.Client.Ldap";
Patrick Williams89492a12023-05-10 07:51:34 -050038constexpr const char* authorityServiceName =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050039 "xyz.openbmc_project.Certs.Manager.Authority.Ldap";
Patrick Williams89492a12023-05-10 07:51:34 -050040constexpr const char* baseObjectPath = "/xyz/openbmc_project/certs";
41constexpr const char* httpsObjectPath =
Jiaqing Zhaoc6a8dfb2022-06-03 10:44:23 +080042 "/xyz/openbmc_project/certs/server/https";
Patrick Williams89492a12023-05-10 07:51:34 -050043constexpr const char* ldapObjectPath = "/xyz/openbmc_project/certs/client/ldap";
44constexpr const char* authorityObjectPath =
Marri Devender Raocfcd5f62019-05-17 08:34:37 -050045 "/xyz/openbmc_project/certs/authority/ldap";
Marri Devender Rao5968cae2019-01-21 10:27:12 -060046} // namespace certs
47
48/**
49 * The Certificate schema defines a Certificate Service which represents the
50 * actions available to manage certificates and links to where certificates
51 * are installed.
52 */
Marri Devender Rao5968cae2019-01-21 10:27:12 -060053
zhanghch058d1b46d2021-04-01 11:18:24 +080054inline std::string getCertificateFromReqBody(
55 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
56 const crow::Request& req)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020057{
Ed Tanous1aa0c2b2022-02-08 12:24:30 +010058 nlohmann::json reqJson;
59 JsonParseResult ret = parseRequestAsJson(req, reqJson);
60 if (ret != JsonParseResult::Success)
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020061 {
62 // We did not receive JSON request, proceed as it is RAW data
Ed Tanous33c6b582023-02-14 15:05:48 -080063 return req.body();
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020064 }
65
66 std::string certificate;
67 std::optional<std::string> certificateType = "PEM";
68
Willy Tu15ed6782021-12-14 11:03:16 -080069 if (!json_util::readJsonPatch(req, asyncResp->res, "CertificateString",
70 certificate, "CertificateType",
71 certificateType))
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020072 {
73 BMCWEB_LOG_ERROR << "Required parameters are missing";
74 messages::internalError(asyncResp->res);
Ed Tanousabb93cd2021-09-02 14:34:57 -070075 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020076 }
77
78 if (*certificateType != "PEM")
79 {
80 messages::propertyValueNotInList(asyncResp->res, *certificateType,
81 "CertificateType");
Ed Tanousabb93cd2021-09-02 14:34:57 -070082 return {};
Kowalski, Kamil58eb2382019-08-12 11:54:31 +020083 }
84
85 return certificate;
86}
87
Marri Devender Rao5968cae2019-01-21 10:27:12 -060088/**
89 * Class to create a temporary certificate file for uploading to system
90 */
91class CertificateFile
92{
93 public:
94 CertificateFile() = delete;
Gunnar Mills1214b7e2020-06-04 10:11:30 -050095 CertificateFile(const CertificateFile&) = delete;
96 CertificateFile& operator=(const CertificateFile&) = delete;
97 CertificateFile(CertificateFile&&) = delete;
98 CertificateFile& operator=(CertificateFile&&) = delete;
Ed Tanous4e23a442022-06-06 09:57:26 -070099 explicit CertificateFile(const std::string& certString)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600100 {
Ed Tanous72d52d22020-10-12 07:46:27 -0700101 std::array<char, 18> dirTemplate = {'/', 't', 'm', 'p', '/', 'C',
Ed Tanous52074382020-09-28 19:16:18 -0700102 'e', 'r', 't', 's', '.', 'X',
103 'X', 'X', 'X', 'X', 'X', '\0'};
104 char* tempDirectory = mkdtemp(dirTemplate.data());
Ed Tanouse662eae2022-01-25 10:39:19 -0800105 if (tempDirectory != nullptr)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600106 {
107 certDirectory = tempDirectory;
108 certificateFile = certDirectory / "cert.pem";
109 std::ofstream out(certificateFile, std::ofstream::out |
110 std::ofstream::binary |
111 std::ofstream::trunc);
112 out << certString;
113 out.close();
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800114 BMCWEB_LOG_DEBUG << "Creating certificate file"
115 << certificateFile.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600116 }
117 }
118 ~CertificateFile()
119 {
120 if (std::filesystem::exists(certDirectory))
121 {
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800122 BMCWEB_LOG_DEBUG << "Removing certificate file"
123 << certificateFile.string();
Ed Tanous23a21a12020-07-25 04:45:05 +0000124 std::error_code ec;
125 std::filesystem::remove_all(certDirectory, ec);
126 if (ec)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600127 {
128 BMCWEB_LOG_ERROR << "Failed to remove temp directory"
Ed Tanous8cc8ede2022-02-28 10:20:59 -0800129 << certDirectory.string();
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600130 }
131 }
132 }
133 std::string getCertFilePath()
134 {
135 return certificateFile;
136 }
137
138 private:
139 std::filesystem::path certificateFile;
140 std::filesystem::path certDirectory;
141};
142
143/**
Gunnar Mills4e0453b2020-07-08 14:00:30 -0500144 * @brief Parse and update Certificate Issue/Subject property
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600145 *
146 * @param[in] asyncResp Shared pointer to the response message
147 * @param[in] str Issuer/Subject value in key=value pairs
148 * @param[in] type Issuer/Subject
149 * @return None
150 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500151static void updateCertIssuerOrSubject(nlohmann::json& out,
Ed Tanous26ccae32023-02-16 10:28:44 -0800152 std::string_view value)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600153{
154 // example: O=openbmc-project.xyz,CN=localhost
155 std::string_view::iterator i = value.begin();
156 while (i != value.end())
157 {
158 std::string_view::iterator tokenBegin = i;
159 while (i != value.end() && *i != '=')
160 {
Patrick Williams6da47ba2023-05-11 11:53:56 -0500161 std::advance(i, 1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600162 }
163 if (i == value.end())
164 {
165 break;
166 }
Ed Tanous26ccae32023-02-16 10:28:44 -0800167 std::string_view key(tokenBegin, static_cast<size_t>(i - tokenBegin));
Patrick Williams6da47ba2023-05-11 11:53:56 -0500168 std::advance(i, 1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600169 tokenBegin = i;
170 while (i != value.end() && *i != ',')
171 {
Patrick Williams6da47ba2023-05-11 11:53:56 -0500172 std::advance(i, 1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600173 }
Ed Tanous26ccae32023-02-16 10:28:44 -0800174 std::string_view val(tokenBegin, static_cast<size_t>(i - tokenBegin));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600175 if (key == "L")
176 {
177 out["City"] = val;
178 }
179 else if (key == "CN")
180 {
181 out["CommonName"] = val;
182 }
183 else if (key == "C")
184 {
185 out["Country"] = val;
186 }
187 else if (key == "O")
188 {
189 out["Organization"] = val;
190 }
191 else if (key == "OU")
192 {
193 out["OrganizationalUnit"] = val;
194 }
195 else if (key == "ST")
196 {
197 out["State"] = val;
198 }
199 // skip comma character
200 if (i != value.end())
201 {
Patrick Williams6da47ba2023-05-11 11:53:56 -0500202 std::advance(i, 1);
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600203 }
204 }
205}
206
207/**
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800208 * @brief Retrieve the installed certificate list
209 *
210 * @param[in] asyncResp Shared pointer to the response message
211 * @param[in] basePath DBus object path to search
212 * @param[in] listPtr Json pointer to the list in asyncResp
213 * @param[in] countPtr Json pointer to the count in asyncResp
214 * @return None
215 */
216static void
217 getCertificateList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
218 const std::string& basePath,
219 const nlohmann::json::json_pointer& listPtr,
220 const nlohmann::json::json_pointer& countPtr)
221{
George Liu7a1dbc42022-12-07 16:03:22 +0800222 constexpr std::array<std::string_view, 1> interfaces = {
223 certs::certPropIntf};
224 dbus::utility::getSubTreePaths(
225 basePath, 0, interfaces,
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800226 [asyncResp, listPtr, countPtr](
George Liu7a1dbc42022-12-07 16:03:22 +0800227 const boost::system::error_code& ec,
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800228 const dbus::utility::MapperGetSubTreePathsResponse& certPaths) {
229 if (ec)
230 {
231 BMCWEB_LOG_ERROR << "Certificate collection query failed: " << ec;
232 messages::internalError(asyncResp->res);
233 return;
234 }
235
236 nlohmann::json& links = asyncResp->res.jsonValue[listPtr];
237 links = nlohmann::json::array();
238 for (const auto& certPath : certPaths)
239 {
240 sdbusplus::message::object_path objPath(certPath);
241 std::string certId = objPath.filename();
242 if (certId.empty())
243 {
244 BMCWEB_LOG_ERROR << "Invalid certificate objPath " << certPath;
245 continue;
246 }
247
248 boost::urls::url certURL;
249 if (objPath.parent_path() == certs::httpsObjectPath)
250 {
Ed Tanousef4c65b2023-04-24 15:28:50 -0700251 certURL = boost::urls::format(
252 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/{}",
253 certId);
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800254 }
255 else if (objPath.parent_path() == certs::ldapObjectPath)
256 {
Ed Tanousef4c65b2023-04-24 15:28:50 -0700257 certURL = boost::urls::format(
258 "/redfish/v1/AccountService/LDAP/Certificates/{}", certId);
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800259 }
260 else if (objPath.parent_path() == certs::authorityObjectPath)
261 {
Ed Tanousef4c65b2023-04-24 15:28:50 -0700262 certURL = boost::urls::format(
263 "/redfish/v1/Managers/bmc/Truststore/Certificates/{}",
264 certId);
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800265 }
266 else
267 {
268 continue;
269 }
270
271 nlohmann::json::object_t link;
272 link["@odata.id"] = certURL;
273 links.emplace_back(std::move(link));
274 }
275
276 asyncResp->res.jsonValue[countPtr] = links.size();
George Liu7a1dbc42022-12-07 16:03:22 +0800277 });
Jiaqing Zhaod3f92ce2022-06-03 11:46:12 +0800278}
279
280/**
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600281 * @brief Retrieve the certificates properties and append to the response
282 * message
283 *
284 * @param[in] asyncResp Shared pointer to the response message
285 * @param[in] objectPath Path of the D-Bus service object
286 * @param[in] certId Id of the certificate
287 * @param[in] certURL URL of the certificate object
288 * @param[in] name name of the certificate
289 * @return None
290 */
291static void getCertificateProperties(
zhanghch058d1b46d2021-04-01 11:18:24 +0800292 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800293 const std::string& objectPath, const std::string& service,
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800294 const std::string& certId, const boost::urls::url& certURL,
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800295 const std::string& name)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600296{
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600297 BMCWEB_LOG_DEBUG << "getCertificateProperties Path=" << objectPath
298 << " certId=" << certId << " certURl=" << certURL;
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200299 sdbusplus::asio::getAllProperties(
300 *crow::connections::systemBus, service, objectPath, certs::certPropIntf,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800301 [asyncResp, certURL, certId,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800302 name](const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800303 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700304 if (ec)
305 {
306 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800307 messages::resourceNotFound(asyncResp->res, "Certificate", certId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700308 return;
309 }
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200310
311 const std::string* certificateString = nullptr;
312 const std::vector<std::string>* keyUsage = nullptr;
313 const std::string* issuer = nullptr;
314 const std::string* subject = nullptr;
315 const uint64_t* validNotAfter = nullptr;
316 const uint64_t* validNotBefore = nullptr;
317
318 const bool success = sdbusplus::unpackPropertiesNoThrow(
319 dbus_utils::UnpackErrorPrinter(), properties, "CertificateString",
320 certificateString, "KeyUsage", keyUsage, "Issuer", issuer,
321 "Subject", subject, "ValidNotAfter", validNotAfter,
322 "ValidNotBefore", validNotBefore);
323
324 if (!success)
325 {
326 messages::internalError(asyncResp->res);
327 return;
328 }
329
Ed Tanous002d39b2022-05-31 08:59:27 -0700330 asyncResp->res.jsonValue["@odata.id"] = certURL;
331 asyncResp->res.jsonValue["@odata.type"] =
332 "#Certificate.v1_0_0.Certificate";
Jiaqing Zhaoe19e97e2022-06-06 19:43:39 +0800333 asyncResp->res.jsonValue["Id"] = certId;
Ed Tanous002d39b2022-05-31 08:59:27 -0700334 asyncResp->res.jsonValue["Name"] = name;
335 asyncResp->res.jsonValue["Description"] = name;
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200336 asyncResp->res.jsonValue["CertificateString"] = "";
337 asyncResp->res.jsonValue["KeyUsage"] = nlohmann::json::array();
338
339 if (certificateString != nullptr)
Ed Tanous002d39b2022-05-31 08:59:27 -0700340 {
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200341 asyncResp->res.jsonValue["CertificateString"] = *certificateString;
Ed Tanous002d39b2022-05-31 08:59:27 -0700342 }
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200343
344 if (keyUsage != nullptr)
345 {
346 asyncResp->res.jsonValue["KeyUsage"] = *keyUsage;
347 }
348
349 if (issuer != nullptr)
350 {
351 updateCertIssuerOrSubject(asyncResp->res.jsonValue["Issuer"],
352 *issuer);
353 }
354
355 if (subject != nullptr)
356 {
357 updateCertIssuerOrSubject(asyncResp->res.jsonValue["Subject"],
358 *subject);
359 }
360
361 if (validNotAfter != nullptr)
362 {
363 asyncResp->res.jsonValue["ValidNotAfter"] =
364 redfish::time_utils::getDateTimeUint(*validNotAfter);
365 }
366
367 if (validNotBefore != nullptr)
368 {
369 asyncResp->res.jsonValue["ValidNotBefore"] =
370 redfish::time_utils::getDateTimeUint(*validNotBefore);
371 }
372
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800373 asyncResp->res.addHeader(
Ed Tanousd9f6c622022-03-17 09:12:17 -0700374 boost::beast::http::field::location,
375 std::string_view(certURL.data(), certURL.size()));
Krzysztof Grobelny9b12d1f2022-08-11 09:52:56 +0200376 });
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600377}
378
Jiaqing Zhao7a3a8f72022-09-29 15:15:58 +0800379static void
380 deleteCertificate(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
381 const std::string& service,
382 const sdbusplus::message::object_path& objectPath)
383{
384 crow::connections::systemBus->async_method_call(
385 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800386 id{objectPath.filename()}](const boost::system::error_code& ec) {
Jiaqing Zhao7a3a8f72022-09-29 15:15:58 +0800387 if (ec)
388 {
389 messages::resourceNotFound(asyncResp->res, "Certificate", id);
390 return;
391 }
392 BMCWEB_LOG_INFO << "Certificate deleted";
393 asyncResp->res.result(boost::beast::http::status::no_content);
394 },
395 service, objectPath, certs::objDeleteIntf, "Delete");
396}
397
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800398inline void handleCertificateServiceGet(
399 App& app, const crow::Request& req,
400 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600401{
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800402 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
403 {
404 return;
405 }
406
Ninad Palsule3e72c202023-03-27 17:19:55 -0500407 if (req.session == nullptr)
408 {
409 messages::internalError(asyncResp->res);
410 return;
411 }
412
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800413 asyncResp->res.jsonValue["@odata.type"] =
414 "#CertificateService.v1_0_0.CertificateService";
415 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/CertificateService";
416 asyncResp->res.jsonValue["Id"] = "CertificateService";
417 asyncResp->res.jsonValue["Name"] = "Certificate Service";
418 asyncResp->res.jsonValue["Description"] =
419 "Actions available to manage certificates";
420 // /redfish/v1/CertificateService/CertificateLocations is something
421 // only ConfigureManager can access then only display when the user
422 // has permissions ConfigureManager
423 Privileges effectiveUserPrivileges =
Ninad Palsule3e72c202023-03-27 17:19:55 -0500424 redfish::getUserPrivileges(*req.session);
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800425 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
426 effectiveUserPrivileges))
427 {
428 asyncResp->res.jsonValue["CertificateLocations"]["@odata.id"] =
429 "/redfish/v1/CertificateService/CertificateLocations";
430 }
431 nlohmann::json& actions = asyncResp->res.jsonValue["Actions"];
432 nlohmann::json& replace = actions["#CertificateService.ReplaceCertificate"];
433 replace["target"] =
434 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate";
435 nlohmann::json::array_t allowed;
Patrick Williamsad539542023-05-12 10:10:08 -0500436 allowed.emplace_back("PEM");
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800437 replace["CertificateType@Redfish.AllowableValues"] = std::move(allowed);
438 actions["#CertificateService.GenerateCSR"]["target"] =
439 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR";
440}
441
442inline void handleCertificateLocationsGet(
443 App& app, const crow::Request& req,
444 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
445{
446 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
447 {
448 return;
449 }
450 asyncResp->res.jsonValue["@odata.id"] =
451 "/redfish/v1/CertificateService/CertificateLocations";
452 asyncResp->res.jsonValue["@odata.type"] =
453 "#CertificateLocations.v1_0_0.CertificateLocations";
454 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
455 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
456 asyncResp->res.jsonValue["Description"] =
457 "Defines a resource that an administrator can use in order to "
458 "locate all certificates installed on a given service";
459
460 getCertificateList(asyncResp, certs::baseObjectPath,
461 "/Links/Certificates"_json_pointer,
462 "/Links/Certificates@odata.count"_json_pointer);
463}
464
465inline void handleReplaceCertificateAction(
466 App& app, const crow::Request& req,
467 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
468{
469 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
470 {
471 return;
472 }
473 std::string certificate;
474 nlohmann::json certificateUri;
475 std::optional<std::string> certificateType = "PEM";
476
477 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
478 certificate, "CertificateUri",
479 certificateUri, "CertificateType",
480 certificateType))
481 {
482 BMCWEB_LOG_ERROR << "Required parameters are missing";
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800483 return;
484 }
485
486 if (!certificateType)
487 {
488 // should never happen, but it never hurts to be paranoid.
489 return;
490 }
491 if (certificateType != "PEM")
492 {
493 messages::actionParameterNotSupported(asyncResp->res, "CertificateType",
494 "ReplaceCertificate");
495 return;
496 }
497
498 std::string certURI;
499 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
500 "@odata.id", certURI))
501 {
502 messages::actionParameterMissing(asyncResp->res, "ReplaceCertificate",
503 "CertificateUri");
504 return;
505 }
506 BMCWEB_LOG_INFO << "Certificate URI to replace: " << certURI;
507
508 boost::urls::result<boost::urls::url_view> parsedUrl =
509 boost::urls::parse_relative_ref(certURI);
510 if (!parsedUrl)
511 {
512 messages::actionParameterValueFormatError(
513 asyncResp->res, certURI, "CertificateUri", "ReplaceCertificate");
514 return;
515 }
516
517 std::string id;
518 sdbusplus::message::object_path objectPath;
519 std::string name;
520 std::string service;
521 if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1", "Managers",
522 "bmc", "NetworkProtocol", "HTTPS",
523 "Certificates", std::ref(id)))
524 {
Patrick Williams89492a12023-05-10 07:51:34 -0500525 objectPath = sdbusplus::message::object_path(certs::httpsObjectPath) /
526 id;
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800527 name = "HTTPS certificate";
528 service = certs::httpsServiceName;
529 }
530 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
531 "AccountService", "LDAP",
532 "Certificates", std::ref(id)))
533 {
Patrick Williams89492a12023-05-10 07:51:34 -0500534 objectPath = sdbusplus::message::object_path(certs::ldapObjectPath) /
535 id;
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800536 name = "LDAP certificate";
537 service = certs::ldapServiceName;
538 }
539 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
540 "Managers", "bmc", "Truststore",
541 "Certificates", std::ref(id)))
542 {
543 objectPath =
544 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
545 name = "TrustStore certificate";
546 service = certs::authorityServiceName;
547 }
548 else
549 {
550 messages::actionParameterNotSupported(asyncResp->res, "CertificateUri",
551 "ReplaceCertificate");
552 return;
553 }
554
555 std::shared_ptr<CertificateFile> certFile =
556 std::make_shared<CertificateFile>(certificate);
557 crow::connections::systemBus->async_method_call(
558 [asyncResp, certFile, objectPath, service, url{*parsedUrl}, id,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800559 name](const boost::system::error_code& ec) {
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800560 if (ec)
561 {
562 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
563 if (ec.value() ==
564 boost::system::linux_error::bad_request_descriptor)
565 {
566 messages::resourceNotFound(asyncResp->res, "Certificate", id);
567 return;
568 }
569 messages::internalError(asyncResp->res);
570 return;
571 }
572 getCertificateProperties(asyncResp, objectPath, service, id, url, name);
573 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
574 << certFile->getCertFilePath();
575 },
576 service, objectPath, certs::certReplaceIntf, "Replace",
577 certFile->getCertFilePath());
578}
579
Ed Tanouscf9e4172022-12-21 09:30:16 -0800580// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800581static std::unique_ptr<sdbusplus::bus::match_t> csrMatcher;
582/**
583 * @brief Read data from CSR D-bus object and set to response
584 *
585 * @param[in] asyncResp Shared pointer to the response message
586 * @param[in] certURI Link to certifiate collection URI
587 * @param[in] service D-Bus service name
588 * @param[in] certObjPath certificate D-Bus object path
589 * @param[in] csrObjPath CSR D-Bus object path
590 * @return None
591 */
592static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
593 const std::string& certURI, const std::string& service,
594 const std::string& certObjPath,
595 const std::string& csrObjPath)
596{
597 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
598 << " CSRObjectPath=" << csrObjPath
599 << " service=" << service;
600 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800601 [asyncResp, certURI](const boost::system::error_code& ec,
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800602 const std::string& csr) {
603 if (ec)
604 {
605 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
606 messages::internalError(asyncResp->res);
607 return;
608 }
609 if (csr.empty())
610 {
611 BMCWEB_LOG_ERROR << "CSR read is empty";
612 messages::internalError(asyncResp->res);
613 return;
614 }
615 asyncResp->res.jsonValue["CSRString"] = csr;
616 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
617 certURI;
618 },
619 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
620}
621
622inline void
623 handleGenerateCSRAction(App& app, const crow::Request& req,
624 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
625{
626 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
627 {
628 return;
629 }
630 static const int rsaKeyBitLength = 2048;
631
632 // Required parameters
633 std::string city;
634 std::string commonName;
635 std::string country;
636 std::string organization;
637 std::string organizationalUnit;
638 std::string state;
639 nlohmann::json certificateCollection;
640
641 // Optional parameters
642 std::optional<std::vector<std::string>> optAlternativeNames =
643 std::vector<std::string>();
644 std::optional<std::string> optContactPerson = "";
645 std::optional<std::string> optChallengePassword = "";
646 std::optional<std::string> optEmail = "";
647 std::optional<std::string> optGivenName = "";
648 std::optional<std::string> optInitials = "";
649 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
650 std::optional<std::string> optKeyCurveId = "secp384r1";
651 std::optional<std::string> optKeyPairAlgorithm = "EC";
652 std::optional<std::vector<std::string>> optKeyUsage =
653 std::vector<std::string>();
654 std::optional<std::string> optSurname = "";
655 std::optional<std::string> optUnstructuredName = "";
656 if (!json_util::readJsonAction(
657 req, asyncResp->res, "City", city, "CommonName", commonName,
658 "ContactPerson", optContactPerson, "Country", country,
659 "Organization", organization, "OrganizationalUnit",
660 organizationalUnit, "State", state, "CertificateCollection",
661 certificateCollection, "AlternativeNames", optAlternativeNames,
662 "ChallengePassword", optChallengePassword, "Email", optEmail,
663 "GivenName", optGivenName, "Initials", optInitials, "KeyBitLength",
664 optKeyBitLength, "KeyCurveId", optKeyCurveId, "KeyPairAlgorithm",
665 optKeyPairAlgorithm, "KeyUsage", optKeyUsage, "Surname", optSurname,
666 "UnstructuredName", optUnstructuredName))
667 {
668 return;
669 }
670
671 // bmcweb has no way to store or decode a private key challenge
672 // password, which will likely cause bmcweb to crash on startup
673 // if this is not set on a post so not allowing the user to set
674 // value
675 if (!optChallengePassword->empty())
676 {
677 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
678 "ChallengePassword");
679 return;
680 }
681
682 std::string certURI;
683 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
684 "@odata.id", certURI))
685 {
686 return;
687 }
688
689 std::string objectPath;
690 std::string service;
691 if (certURI.starts_with(
692 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
693 {
694 objectPath = certs::httpsObjectPath;
695 service = certs::httpsServiceName;
696 }
697 else if (certURI.starts_with(
698 "/redfish/v1/AccountService/LDAP/Certificates"))
699 {
700 objectPath = certs::ldapObjectPath;
701 service = certs::ldapServiceName;
702 }
703 else
704 {
705 messages::actionParameterNotSupported(
706 asyncResp->res, "CertificateCollection", "GenerateCSR");
707 return;
708 }
709
710 // supporting only EC and RSA algorithm
711 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
712 {
713 messages::actionParameterNotSupported(
714 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
715 return;
716 }
717
718 // supporting only 2048 key bit length for RSA algorithm due to
719 // time consumed in generating private key
720 if (*optKeyPairAlgorithm == "RSA" && *optKeyBitLength != rsaKeyBitLength)
721 {
722 messages::propertyValueNotInList(
723 asyncResp->res, std::to_string(*optKeyBitLength), "KeyBitLength");
724 return;
725 }
726
727 // validate KeyUsage supporting only 1 type based on URL
728 if (certURI.starts_with(
729 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
730 {
731 if (optKeyUsage->empty())
732 {
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500733 optKeyUsage->emplace_back("ServerAuthentication");
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800734 }
735 else if (optKeyUsage->size() == 1)
736 {
737 if ((*optKeyUsage)[0] != "ServerAuthentication")
738 {
739 messages::propertyValueNotInList(asyncResp->res,
740 (*optKeyUsage)[0], "KeyUsage");
741 return;
742 }
743 }
744 else
745 {
746 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage",
747 "GenerateCSR");
748 return;
749 }
750 }
751 else if (certURI.starts_with(
752 "/redfish/v1/AccountService/LDAP/Certificates"))
753 {
754 if (optKeyUsage->empty())
755 {
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500756 optKeyUsage->emplace_back("ClientAuthentication");
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800757 }
758 else if (optKeyUsage->size() == 1)
759 {
760 if ((*optKeyUsage)[0] != "ClientAuthentication")
761 {
762 messages::propertyValueNotInList(asyncResp->res,
763 (*optKeyUsage)[0], "KeyUsage");
764 return;
765 }
766 }
767 else
768 {
769 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage",
770 "GenerateCSR");
771 return;
772 }
773 }
774
775 // Only allow one CSR matcher at a time so setting retry
776 // time-out and timer expiry to 10 seconds for now.
777 static const int timeOut = 10;
778 if (csrMatcher)
779 {
780 messages::serviceTemporarilyUnavailable(asyncResp->res,
781 std::to_string(timeOut));
782 return;
783 }
784
785 // Make this static so it survives outside this method
786 static boost::asio::steady_timer timeout(*req.ioService);
787 timeout.expires_after(std::chrono::seconds(timeOut));
788 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
789 csrMatcher = nullptr;
790 if (ec)
791 {
792 // operation_aborted is expected if timer is canceled
793 // before completion.
794 if (ec != boost::asio::error::operation_aborted)
795 {
796 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
797 }
798 return;
799 }
800 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
801 messages::internalError(asyncResp->res);
802 });
803
804 // create a matcher to wait on CSR object
805 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
806 std::string match("type='signal',"
807 "interface='org.freedesktop.DBus.ObjectManager',"
808 "path='" +
809 objectPath +
810 "',"
811 "member='InterfacesAdded'");
812 csrMatcher = std::make_unique<sdbusplus::bus::match_t>(
813 *crow::connections::systemBus, match,
814 [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) {
815 timeout.cancel();
816 if (m.is_method_error())
817 {
818 BMCWEB_LOG_ERROR << "Dbus method error!!!";
819 messages::internalError(asyncResp->res);
820 return;
821 }
822
823 dbus::utility::DBusInteracesMap interfacesProperties;
824
825 sdbusplus::message::object_path csrObjectPath;
826 m.read(csrObjectPath, interfacesProperties);
827 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
828 for (const auto& interface : interfacesProperties)
829 {
830 if (interface.first == "xyz.openbmc_project.Certs.CSR")
831 {
832 getCSR(asyncResp, certURI, service, objectPath,
833 csrObjectPath.str);
834 break;
835 }
836 }
837 });
838 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800839 [asyncResp](const boost::system::error_code& ec, const std::string&) {
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800840 if (ec)
841 {
842 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
843 messages::internalError(asyncResp->res);
844 return;
845 }
846 },
847 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
848 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
849 commonName, *optContactPerson, country, *optEmail, *optGivenName,
850 *optInitials, *optKeyBitLength, *optKeyCurveId, *optKeyPairAlgorithm,
851 *optKeyUsage, organization, organizationalUnit, state, *optSurname,
852 *optUnstructuredName);
853}
854
855inline void requestRoutesCertificateService(App& app)
856{
857 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
858 .privileges(redfish::privileges::getCertificateService)
859 .methods(boost::beast::http::verb::get)(
860 std::bind_front(handleCertificateServiceGet, std::ref(app)));
861
862 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
863 .privileges(redfish::privileges::getCertificateLocations)
864 .methods(boost::beast::http::verb::get)(
865 std::bind_front(handleCertificateLocationsGet, std::ref(app)));
866
George Liu0fda0f12021-11-16 10:06:17 +0800867 BMCWEB_ROUTE(
868 app,
869 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700870 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700871 .methods(boost::beast::http::verb::post)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800872 std::bind_front(handleReplaceCertificateAction, std::ref(app)));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600873
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800874 BMCWEB_ROUTE(
875 app,
876 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
877 .privileges(redfish::privileges::postCertificateService)
878 .methods(boost::beast::http::verb::post)(
879 std::bind_front(handleGenerateCSRAction, std::ref(app)));
880} // requestRoutesCertificateService
881
882inline void handleHTTPSCertificateCollectionGet(
883 App& app, const crow::Request& req,
884 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
885{
886 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
887 {
888 return;
889 }
890
891 asyncResp->res.jsonValue["@odata.id"] =
892 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
893 asyncResp->res.jsonValue["@odata.type"] =
894 "#CertificateCollection.CertificateCollection";
895 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
896 asyncResp->res.jsonValue["Description"] =
897 "A Collection of HTTPS certificate instances";
898
899 getCertificateList(asyncResp, certs::httpsObjectPath,
900 "/Members"_json_pointer,
901 "/Members@odata.count"_json_pointer);
902}
903
904inline void handleHTTPSCertificateCollectionPost(
905 App& app, const crow::Request& req,
906 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
907{
908 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
909 {
910 return;
911 }
912 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
913
914 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
915 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
916
917 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
918
919 if (certFileBody.empty())
920 {
921 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
922 messages::unrecognizedRequestBody(asyncResp->res);
923 return;
924 }
925
926 std::shared_ptr<CertificateFile> certFile =
927 std::make_shared<CertificateFile>(certFileBody);
928
929 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800930 [asyncResp, certFile](const boost::system::error_code& ec,
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800931 const std::string& objectPath) {
932 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700933 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800934 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Ed Tanous002d39b2022-05-31 08:59:27 -0700935 messages::internalError(asyncResp->res);
936 return;
937 }
938
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800939 sdbusplus::message::object_path path(objectPath);
940 std::string certId = path.filename();
Ed Tanousef4c65b2023-04-24 15:28:50 -0700941 const boost::urls::url certURL = boost::urls::format(
942 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/{}",
943 certId);
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800944 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
945 certId, certURL, "HTTPS Certificate");
946 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
947 << certFile->getCertFilePath();
948 },
949 certs::httpsServiceName, certs::httpsObjectPath, certs::certInstallIntf,
950 "Install", certFile->getCertFilePath());
951}
Ed Tanous002d39b2022-05-31 08:59:27 -0700952
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800953inline void handleHTTPSCertificateGet(
954 App& app, const crow::Request& req,
955 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
956{
957 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
958 {
959 return;
960 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700961
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800962 BMCWEB_LOG_DEBUG << "HTTPS Certificate ID=" << id;
Ed Tanousef4c65b2023-04-24 15:28:50 -0700963 const boost::urls::url certURL = boost::urls::format(
964 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/{}", id);
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800965 std::string objPath =
966 sdbusplus::message::object_path(certs::httpsObjectPath) / id;
967 getCertificateProperties(asyncResp, objPath, certs::httpsServiceName, id,
968 certURL, "HTTPS Certificate");
969}
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700970
971inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600972{
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800973 BMCWEB_ROUTE(app,
974 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
975 .privileges(redfish::privileges::getCertificateCollection)
976 .methods(boost::beast::http::verb::get)(std::bind_front(
977 handleHTTPSCertificateCollectionGet, std::ref(app)));
978
979 BMCWEB_ROUTE(app,
980 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
981 .privileges(redfish::privileges::postCertificateCollection)
982 .methods(boost::beast::http::verb::post)(std::bind_front(
983 handleHTTPSCertificateCollectionPost, std::ref(app)));
984
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700985 BMCWEB_ROUTE(
986 app,
987 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700988 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800989 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800990 std::bind_front(handleHTTPSCertificateGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700991}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600992
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800993inline void handleLDAPCertificateCollectionGet(
994 App& app, const crow::Request& req,
995 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600996{
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800997 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
998 {
999 return;
1000 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001001
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001002 asyncResp->res.jsonValue["@odata.id"] =
1003 "/redfish/v1/AccountService/LDAP/Certificates";
1004 asyncResp->res.jsonValue["@odata.type"] =
1005 "#CertificateCollection.CertificateCollection";
1006 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1007 asyncResp->res.jsonValue["Description"] =
1008 "A Collection of LDAP certificate instances";
Ed Tanous002d39b2022-05-31 08:59:27 -07001009
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001010 getCertificateList(asyncResp, certs::ldapObjectPath,
1011 "/Members"_json_pointer,
1012 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001013}
Marri Devender Rao37cce912019-02-20 01:05:22 -06001014
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001015inline void handleLDAPCertificateCollectionPost(
1016 App& app, const crow::Request& req,
1017 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001018{
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001019 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1020 {
1021 return;
1022 }
1023 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1024
1025 if (certFileBody.empty())
1026 {
1027 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1028 messages::unrecognizedRequestBody(asyncResp->res);
1029 return;
1030 }
1031
1032 std::shared_ptr<CertificateFile> certFile =
1033 std::make_shared<CertificateFile>(certFileBody);
1034
1035 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001036 [asyncResp, certFile](const boost::system::error_code& ec,
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001037 const std::string& objectPath) {
1038 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001039 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001040 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1041 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001042 return;
1043 }
1044
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001045 sdbusplus::message::object_path path(objectPath);
1046 std::string certId = path.filename();
Ed Tanousef4c65b2023-04-24 15:28:50 -07001047 const boost::urls::url certURL = boost::urls::format(
1048 "/redfish/v1/AccountService/LDAP/Certificates/{}", certId);
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001049 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1050 certId, certURL, "LDAP Certificate");
1051 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1052 << certFile->getCertFilePath();
1053 },
1054 certs::ldapServiceName, certs::ldapObjectPath, certs::certInstallIntf,
1055 "Install", certFile->getCertFilePath());
1056}
Ed Tanous002d39b2022-05-31 08:59:27 -07001057
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001058inline void handleLDAPCertificateGet(
1059 App& app, const crow::Request& req,
1060 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1061{
1062 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1063 {
1064 return;
1065 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001066
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001067 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << id;
Ed Tanousef4c65b2023-04-24 15:28:50 -07001068 const boost::urls::url certURL = boost::urls::format(
1069 "/redfish/v1/AccountService/LDAP/Certificates/{}", id);
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001070 std::string objPath =
1071 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1072 getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id,
1073 certURL, "LDAP Certificate");
1074}
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001075
Jiaqing Zhao99612242022-09-29 15:31:09 +08001076inline void handleLDAPCertificateDelete(
1077 App& app, const crow::Request& req,
1078 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1079{
1080 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1081 {
1082 return;
1083 }
1084
1085 BMCWEB_LOG_DEBUG << "Delete LDAP Certificate ID=" << id;
1086 std::string objPath =
1087 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1088
1089 deleteCertificate(asyncResp, certs::ldapServiceName, objPath);
1090}
1091
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001092inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001093{
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001094 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1095 .privileges(redfish::privileges::getCertificateCollection)
1096 .methods(boost::beast::http::verb::get)(
1097 std::bind_front(handleLDAPCertificateCollectionGet, std::ref(app)));
1098
1099 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1100 .privileges(redfish::privileges::postCertificateCollection)
1101 .methods(boost::beast::http::verb::post)(std::bind_front(
1102 handleLDAPCertificateCollectionPost, std::ref(app)));
1103
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001104 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001105 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001106 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001107 std::bind_front(handleLDAPCertificateGet, std::ref(app)));
Jiaqing Zhao99612242022-09-29 15:31:09 +08001108
1109 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
1110 .privileges(redfish::privileges::deleteCertificate)
1111 .methods(boost::beast::http::verb::delete_)(
1112 std::bind_front(handleLDAPCertificateDelete, std::ref(app)));
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001113} // requestRoutesLDAPCertificate
1114
1115inline void handleTrustStoreCertificateCollectionGet(
1116 App& app, const crow::Request& req,
1117 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1118{
1119 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1120 {
1121 return;
1122 }
1123
1124 asyncResp->res.jsonValue["@odata.id"] =
1125 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1126 asyncResp->res.jsonValue["@odata.type"] =
1127 "#CertificateCollection.CertificateCollection";
1128 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1129 asyncResp->res.jsonValue["Description"] =
1130 "A Collection of TrustStore certificate instances";
1131
1132 getCertificateList(asyncResp, certs::authorityObjectPath,
1133 "/Members"_json_pointer,
1134 "/Members@odata.count"_json_pointer);
1135}
1136
1137inline void handleTrustStoreCertificateCollectionPost(
1138 App& app, const crow::Request& req,
1139 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1140{
1141 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1142 {
1143 return;
1144 }
1145 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1146
1147 if (certFileBody.empty())
1148 {
1149 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1150 messages::unrecognizedRequestBody(asyncResp->res);
1151 return;
1152 }
1153
1154 std::shared_ptr<CertificateFile> certFile =
1155 std::make_shared<CertificateFile>(certFileBody);
1156 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001157 [asyncResp, certFile](const boost::system::error_code& ec,
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001158 const std::string& objectPath) {
1159 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001160 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001161 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1162 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001163 return;
1164 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001165
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001166 sdbusplus::message::object_path path(objectPath);
1167 std::string certId = path.filename();
Ed Tanousef4c65b2023-04-24 15:28:50 -07001168 const boost::urls::url certURL = boost::urls::format(
1169 "/redfish/v1/Managers/bmc/Truststore/Certificates/{}", certId);
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001170 getCertificateProperties(asyncResp, objectPath,
1171 certs::authorityServiceName, certId, certURL,
1172 "TrustStore Certificate");
1173 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1174 << certFile->getCertFilePath();
1175 },
1176 certs::authorityServiceName, certs::authorityObjectPath,
1177 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1178}
1179
1180inline void handleTrustStoreCertificateGet(
1181 App& app, const crow::Request& req,
1182 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1183{
1184 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1185 {
1186 return;
1187 }
1188
1189 BMCWEB_LOG_DEBUG << "Truststore Certificate ID=" << id;
Ed Tanousef4c65b2023-04-24 15:28:50 -07001190 const boost::urls::url certURL = boost::urls::format(
1191 "/redfish/v1/Managers/bmc/Truststore/Certificates/{}", id);
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001192 std::string objPath =
1193 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1194 getCertificateProperties(asyncResp, objPath, certs::authorityServiceName,
1195 id, certURL, "TrustStore Certificate");
1196}
1197
1198inline void handleTrustStoreCertificateDelete(
1199 App& app, const crow::Request& req,
1200 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1201{
1202 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1203 {
1204 return;
1205 }
1206
1207 BMCWEB_LOG_DEBUG << "Delete TrustStore Certificate ID=" << id;
1208 std::string objPath =
1209 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1210
Jiaqing Zhao7a3a8f72022-09-29 15:15:58 +08001211 deleteCertificate(asyncResp, certs::authorityServiceName, objPath);
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001212}
1213
1214inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001215{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001216 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001217 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001218 .methods(boost::beast::http::verb::get)(std::bind_front(
1219 handleTrustStoreCertificateCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001220
1221 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001222 .privileges(redfish::privileges::postCertificateCollection)
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001223 .methods(boost::beast::http::verb::post)(std::bind_front(
1224 handleTrustStoreCertificateCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07001225
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001226 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001227 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001228 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001229 std::bind_front(handleTrustStoreCertificateGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001230
1231 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001232 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001233 .methods(boost::beast::http::verb::delete_)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001234 std::bind_front(handleTrustStoreCertificateDelete, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001235} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001236} // namespace redfish