blob: 027e05ddb479da5211a7849374f1f5a9a9a23ce0 [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
407 asyncResp->res.jsonValue["@odata.type"] =
408 "#CertificateService.v1_0_0.CertificateService";
409 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/CertificateService";
410 asyncResp->res.jsonValue["Id"] = "CertificateService";
411 asyncResp->res.jsonValue["Name"] = "Certificate Service";
412 asyncResp->res.jsonValue["Description"] =
413 "Actions available to manage certificates";
414 // /redfish/v1/CertificateService/CertificateLocations is something
415 // only ConfigureManager can access then only display when the user
416 // has permissions ConfigureManager
417 Privileges effectiveUserPrivileges =
418 redfish::getUserPrivileges(req.userRole);
419 if (isOperationAllowedWithPrivileges({{"ConfigureManager"}},
420 effectiveUserPrivileges))
421 {
422 asyncResp->res.jsonValue["CertificateLocations"]["@odata.id"] =
423 "/redfish/v1/CertificateService/CertificateLocations";
424 }
425 nlohmann::json& actions = asyncResp->res.jsonValue["Actions"];
426 nlohmann::json& replace = actions["#CertificateService.ReplaceCertificate"];
427 replace["target"] =
428 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate";
429 nlohmann::json::array_t allowed;
Patrick Williamsad539542023-05-12 10:10:08 -0500430 allowed.emplace_back("PEM");
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800431 replace["CertificateType@Redfish.AllowableValues"] = std::move(allowed);
432 actions["#CertificateService.GenerateCSR"]["target"] =
433 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR";
434}
435
436inline void handleCertificateLocationsGet(
437 App& app, const crow::Request& req,
438 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
439{
440 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
441 {
442 return;
443 }
444 asyncResp->res.jsonValue["@odata.id"] =
445 "/redfish/v1/CertificateService/CertificateLocations";
446 asyncResp->res.jsonValue["@odata.type"] =
447 "#CertificateLocations.v1_0_0.CertificateLocations";
448 asyncResp->res.jsonValue["Name"] = "Certificate Locations";
449 asyncResp->res.jsonValue["Id"] = "CertificateLocations";
450 asyncResp->res.jsonValue["Description"] =
451 "Defines a resource that an administrator can use in order to "
452 "locate all certificates installed on a given service";
453
454 getCertificateList(asyncResp, certs::baseObjectPath,
455 "/Links/Certificates"_json_pointer,
456 "/Links/Certificates@odata.count"_json_pointer);
457}
458
459inline void handleReplaceCertificateAction(
460 App& app, const crow::Request& req,
461 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
462{
463 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
464 {
465 return;
466 }
467 std::string certificate;
468 nlohmann::json certificateUri;
469 std::optional<std::string> certificateType = "PEM";
470
471 if (!json_util::readJsonAction(req, asyncResp->res, "CertificateString",
472 certificate, "CertificateUri",
473 certificateUri, "CertificateType",
474 certificateType))
475 {
476 BMCWEB_LOG_ERROR << "Required parameters are missing";
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800477 return;
478 }
479
480 if (!certificateType)
481 {
482 // should never happen, but it never hurts to be paranoid.
483 return;
484 }
485 if (certificateType != "PEM")
486 {
487 messages::actionParameterNotSupported(asyncResp->res, "CertificateType",
488 "ReplaceCertificate");
489 return;
490 }
491
492 std::string certURI;
493 if (!redfish::json_util::readJson(certificateUri, asyncResp->res,
494 "@odata.id", certURI))
495 {
496 messages::actionParameterMissing(asyncResp->res, "ReplaceCertificate",
497 "CertificateUri");
498 return;
499 }
500 BMCWEB_LOG_INFO << "Certificate URI to replace: " << certURI;
501
502 boost::urls::result<boost::urls::url_view> parsedUrl =
503 boost::urls::parse_relative_ref(certURI);
504 if (!parsedUrl)
505 {
506 messages::actionParameterValueFormatError(
507 asyncResp->res, certURI, "CertificateUri", "ReplaceCertificate");
508 return;
509 }
510
511 std::string id;
512 sdbusplus::message::object_path objectPath;
513 std::string name;
514 std::string service;
515 if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1", "Managers",
516 "bmc", "NetworkProtocol", "HTTPS",
517 "Certificates", std::ref(id)))
518 {
Patrick Williams89492a12023-05-10 07:51:34 -0500519 objectPath = sdbusplus::message::object_path(certs::httpsObjectPath) /
520 id;
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800521 name = "HTTPS certificate";
522 service = certs::httpsServiceName;
523 }
524 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
525 "AccountService", "LDAP",
526 "Certificates", std::ref(id)))
527 {
Patrick Williams89492a12023-05-10 07:51:34 -0500528 objectPath = sdbusplus::message::object_path(certs::ldapObjectPath) /
529 id;
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800530 name = "LDAP certificate";
531 service = certs::ldapServiceName;
532 }
533 else if (crow::utility::readUrlSegments(*parsedUrl, "redfish", "v1",
534 "Managers", "bmc", "Truststore",
535 "Certificates", std::ref(id)))
536 {
537 objectPath =
538 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
539 name = "TrustStore certificate";
540 service = certs::authorityServiceName;
541 }
542 else
543 {
544 messages::actionParameterNotSupported(asyncResp->res, "CertificateUri",
545 "ReplaceCertificate");
546 return;
547 }
548
549 std::shared_ptr<CertificateFile> certFile =
550 std::make_shared<CertificateFile>(certificate);
551 crow::connections::systemBus->async_method_call(
552 [asyncResp, certFile, objectPath, service, url{*parsedUrl}, id,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800553 name](const boost::system::error_code& ec) {
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800554 if (ec)
555 {
556 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
557 if (ec.value() ==
558 boost::system::linux_error::bad_request_descriptor)
559 {
560 messages::resourceNotFound(asyncResp->res, "Certificate", id);
561 return;
562 }
563 messages::internalError(asyncResp->res);
564 return;
565 }
566 getCertificateProperties(asyncResp, objectPath, service, id, url, name);
567 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
568 << certFile->getCertFilePath();
569 },
570 service, objectPath, certs::certReplaceIntf, "Replace",
571 certFile->getCertFilePath());
572}
573
Ed Tanouscf9e4172022-12-21 09:30:16 -0800574// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800575static std::unique_ptr<sdbusplus::bus::match_t> csrMatcher;
576/**
577 * @brief Read data from CSR D-bus object and set to response
578 *
579 * @param[in] asyncResp Shared pointer to the response message
580 * @param[in] certURI Link to certifiate collection URI
581 * @param[in] service D-Bus service name
582 * @param[in] certObjPath certificate D-Bus object path
583 * @param[in] csrObjPath CSR D-Bus object path
584 * @return None
585 */
586static void getCSR(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
587 const std::string& certURI, const std::string& service,
588 const std::string& certObjPath,
589 const std::string& csrObjPath)
590{
591 BMCWEB_LOG_DEBUG << "getCSR CertObjectPath" << certObjPath
592 << " CSRObjectPath=" << csrObjPath
593 << " service=" << service;
594 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800595 [asyncResp, certURI](const boost::system::error_code& ec,
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800596 const std::string& csr) {
597 if (ec)
598 {
599 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
600 messages::internalError(asyncResp->res);
601 return;
602 }
603 if (csr.empty())
604 {
605 BMCWEB_LOG_ERROR << "CSR read is empty";
606 messages::internalError(asyncResp->res);
607 return;
608 }
609 asyncResp->res.jsonValue["CSRString"] = csr;
610 asyncResp->res.jsonValue["CertificateCollection"]["@odata.id"] =
611 certURI;
612 },
613 service, csrObjPath, "xyz.openbmc_project.Certs.CSR", "CSR");
614}
615
616inline void
617 handleGenerateCSRAction(App& app, const crow::Request& req,
618 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
619{
620 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
621 {
622 return;
623 }
624 static const int rsaKeyBitLength = 2048;
625
626 // Required parameters
627 std::string city;
628 std::string commonName;
629 std::string country;
630 std::string organization;
631 std::string organizationalUnit;
632 std::string state;
633 nlohmann::json certificateCollection;
634
635 // Optional parameters
636 std::optional<std::vector<std::string>> optAlternativeNames =
637 std::vector<std::string>();
638 std::optional<std::string> optContactPerson = "";
639 std::optional<std::string> optChallengePassword = "";
640 std::optional<std::string> optEmail = "";
641 std::optional<std::string> optGivenName = "";
642 std::optional<std::string> optInitials = "";
643 std::optional<int64_t> optKeyBitLength = rsaKeyBitLength;
644 std::optional<std::string> optKeyCurveId = "secp384r1";
645 std::optional<std::string> optKeyPairAlgorithm = "EC";
646 std::optional<std::vector<std::string>> optKeyUsage =
647 std::vector<std::string>();
648 std::optional<std::string> optSurname = "";
649 std::optional<std::string> optUnstructuredName = "";
650 if (!json_util::readJsonAction(
651 req, asyncResp->res, "City", city, "CommonName", commonName,
652 "ContactPerson", optContactPerson, "Country", country,
653 "Organization", organization, "OrganizationalUnit",
654 organizationalUnit, "State", state, "CertificateCollection",
655 certificateCollection, "AlternativeNames", optAlternativeNames,
656 "ChallengePassword", optChallengePassword, "Email", optEmail,
657 "GivenName", optGivenName, "Initials", optInitials, "KeyBitLength",
658 optKeyBitLength, "KeyCurveId", optKeyCurveId, "KeyPairAlgorithm",
659 optKeyPairAlgorithm, "KeyUsage", optKeyUsage, "Surname", optSurname,
660 "UnstructuredName", optUnstructuredName))
661 {
662 return;
663 }
664
665 // bmcweb has no way to store or decode a private key challenge
666 // password, which will likely cause bmcweb to crash on startup
667 // if this is not set on a post so not allowing the user to set
668 // value
669 if (!optChallengePassword->empty())
670 {
671 messages::actionParameterNotSupported(asyncResp->res, "GenerateCSR",
672 "ChallengePassword");
673 return;
674 }
675
676 std::string certURI;
677 if (!redfish::json_util::readJson(certificateCollection, asyncResp->res,
678 "@odata.id", certURI))
679 {
680 return;
681 }
682
683 std::string objectPath;
684 std::string service;
685 if (certURI.starts_with(
686 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
687 {
688 objectPath = certs::httpsObjectPath;
689 service = certs::httpsServiceName;
690 }
691 else if (certURI.starts_with(
692 "/redfish/v1/AccountService/LDAP/Certificates"))
693 {
694 objectPath = certs::ldapObjectPath;
695 service = certs::ldapServiceName;
696 }
697 else
698 {
699 messages::actionParameterNotSupported(
700 asyncResp->res, "CertificateCollection", "GenerateCSR");
701 return;
702 }
703
704 // supporting only EC and RSA algorithm
705 if (*optKeyPairAlgorithm != "EC" && *optKeyPairAlgorithm != "RSA")
706 {
707 messages::actionParameterNotSupported(
708 asyncResp->res, "KeyPairAlgorithm", "GenerateCSR");
709 return;
710 }
711
712 // supporting only 2048 key bit length for RSA algorithm due to
713 // time consumed in generating private key
714 if (*optKeyPairAlgorithm == "RSA" && *optKeyBitLength != rsaKeyBitLength)
715 {
716 messages::propertyValueNotInList(
717 asyncResp->res, std::to_string(*optKeyBitLength), "KeyBitLength");
718 return;
719 }
720
721 // validate KeyUsage supporting only 1 type based on URL
722 if (certURI.starts_with(
723 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates"))
724 {
725 if (optKeyUsage->empty())
726 {
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500727 optKeyUsage->emplace_back("ServerAuthentication");
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800728 }
729 else if (optKeyUsage->size() == 1)
730 {
731 if ((*optKeyUsage)[0] != "ServerAuthentication")
732 {
733 messages::propertyValueNotInList(asyncResp->res,
734 (*optKeyUsage)[0], "KeyUsage");
735 return;
736 }
737 }
738 else
739 {
740 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage",
741 "GenerateCSR");
742 return;
743 }
744 }
745 else if (certURI.starts_with(
746 "/redfish/v1/AccountService/LDAP/Certificates"))
747 {
748 if (optKeyUsage->empty())
749 {
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500750 optKeyUsage->emplace_back("ClientAuthentication");
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800751 }
752 else if (optKeyUsage->size() == 1)
753 {
754 if ((*optKeyUsage)[0] != "ClientAuthentication")
755 {
756 messages::propertyValueNotInList(asyncResp->res,
757 (*optKeyUsage)[0], "KeyUsage");
758 return;
759 }
760 }
761 else
762 {
763 messages::actionParameterNotSupported(asyncResp->res, "KeyUsage",
764 "GenerateCSR");
765 return;
766 }
767 }
768
769 // Only allow one CSR matcher at a time so setting retry
770 // time-out and timer expiry to 10 seconds for now.
771 static const int timeOut = 10;
772 if (csrMatcher)
773 {
774 messages::serviceTemporarilyUnavailable(asyncResp->res,
775 std::to_string(timeOut));
776 return;
777 }
778
779 // Make this static so it survives outside this method
780 static boost::asio::steady_timer timeout(*req.ioService);
781 timeout.expires_after(std::chrono::seconds(timeOut));
782 timeout.async_wait([asyncResp](const boost::system::error_code& ec) {
783 csrMatcher = nullptr;
784 if (ec)
785 {
786 // operation_aborted is expected if timer is canceled
787 // before completion.
788 if (ec != boost::asio::error::operation_aborted)
789 {
790 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
791 }
792 return;
793 }
794 BMCWEB_LOG_ERROR << "Timed out waiting for Generating CSR";
795 messages::internalError(asyncResp->res);
796 });
797
798 // create a matcher to wait on CSR object
799 BMCWEB_LOG_DEBUG << "create matcher with path " << objectPath;
800 std::string match("type='signal',"
801 "interface='org.freedesktop.DBus.ObjectManager',"
802 "path='" +
803 objectPath +
804 "',"
805 "member='InterfacesAdded'");
806 csrMatcher = std::make_unique<sdbusplus::bus::match_t>(
807 *crow::connections::systemBus, match,
808 [asyncResp, service, objectPath, certURI](sdbusplus::message_t& m) {
809 timeout.cancel();
810 if (m.is_method_error())
811 {
812 BMCWEB_LOG_ERROR << "Dbus method error!!!";
813 messages::internalError(asyncResp->res);
814 return;
815 }
816
817 dbus::utility::DBusInteracesMap interfacesProperties;
818
819 sdbusplus::message::object_path csrObjectPath;
820 m.read(csrObjectPath, interfacesProperties);
821 BMCWEB_LOG_DEBUG << "CSR object added" << csrObjectPath.str;
822 for (const auto& interface : interfacesProperties)
823 {
824 if (interface.first == "xyz.openbmc_project.Certs.CSR")
825 {
826 getCSR(asyncResp, certURI, service, objectPath,
827 csrObjectPath.str);
828 break;
829 }
830 }
831 });
832 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800833 [asyncResp](const boost::system::error_code& ec, const std::string&) {
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800834 if (ec)
835 {
836 BMCWEB_LOG_ERROR << "DBUS response error: " << ec.message();
837 messages::internalError(asyncResp->res);
838 return;
839 }
840 },
841 service, objectPath, "xyz.openbmc_project.Certs.CSR.Create",
842 "GenerateCSR", *optAlternativeNames, *optChallengePassword, city,
843 commonName, *optContactPerson, country, *optEmail, *optGivenName,
844 *optInitials, *optKeyBitLength, *optKeyCurveId, *optKeyPairAlgorithm,
845 *optKeyUsage, organization, organizationalUnit, state, *optSurname,
846 *optUnstructuredName);
847}
848
849inline void requestRoutesCertificateService(App& app)
850{
851 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/")
852 .privileges(redfish::privileges::getCertificateService)
853 .methods(boost::beast::http::verb::get)(
854 std::bind_front(handleCertificateServiceGet, std::ref(app)));
855
856 BMCWEB_ROUTE(app, "/redfish/v1/CertificateService/CertificateLocations/")
857 .privileges(redfish::privileges::getCertificateLocations)
858 .methods(boost::beast::http::verb::get)(
859 std::bind_front(handleCertificateLocationsGet, std::ref(app)));
860
George Liu0fda0f12021-11-16 10:06:17 +0800861 BMCWEB_ROUTE(
862 app,
863 "/redfish/v1/CertificateService/Actions/CertificateService.ReplaceCertificate/")
Ed Tanoused398212021-06-09 17:05:54 -0700864 .privileges(redfish::privileges::postCertificateService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700865 .methods(boost::beast::http::verb::post)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800866 std::bind_front(handleReplaceCertificateAction, std::ref(app)));
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600867
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800868 BMCWEB_ROUTE(
869 app,
870 "/redfish/v1/CertificateService/Actions/CertificateService.GenerateCSR/")
871 .privileges(redfish::privileges::postCertificateService)
872 .methods(boost::beast::http::verb::post)(
873 std::bind_front(handleGenerateCSRAction, std::ref(app)));
874} // requestRoutesCertificateService
875
876inline void handleHTTPSCertificateCollectionGet(
877 App& app, const crow::Request& req,
878 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
879{
880 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
881 {
882 return;
883 }
884
885 asyncResp->res.jsonValue["@odata.id"] =
886 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates";
887 asyncResp->res.jsonValue["@odata.type"] =
888 "#CertificateCollection.CertificateCollection";
889 asyncResp->res.jsonValue["Name"] = "HTTPS Certificates Collection";
890 asyncResp->res.jsonValue["Description"] =
891 "A Collection of HTTPS certificate instances";
892
893 getCertificateList(asyncResp, certs::httpsObjectPath,
894 "/Members"_json_pointer,
895 "/Members@odata.count"_json_pointer);
896}
897
898inline void handleHTTPSCertificateCollectionPost(
899 App& app, const crow::Request& req,
900 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
901{
902 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
903 {
904 return;
905 }
906 BMCWEB_LOG_DEBUG << "HTTPSCertificateCollection::doPost";
907
908 asyncResp->res.jsonValue["Name"] = "HTTPS Certificate";
909 asyncResp->res.jsonValue["Description"] = "HTTPS Certificate";
910
911 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
912
913 if (certFileBody.empty())
914 {
915 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
916 messages::unrecognizedRequestBody(asyncResp->res);
917 return;
918 }
919
920 std::shared_ptr<CertificateFile> certFile =
921 std::make_shared<CertificateFile>(certFileBody);
922
923 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800924 [asyncResp, certFile](const boost::system::error_code& ec,
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800925 const std::string& objectPath) {
926 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -0700927 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800928 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
Ed Tanous002d39b2022-05-31 08:59:27 -0700929 messages::internalError(asyncResp->res);
930 return;
931 }
932
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800933 sdbusplus::message::object_path path(objectPath);
934 std::string certId = path.filename();
Ed Tanousef4c65b2023-04-24 15:28:50 -0700935 const boost::urls::url certURL = boost::urls::format(
936 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/{}",
937 certId);
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800938 getCertificateProperties(asyncResp, objectPath, certs::httpsServiceName,
939 certId, certURL, "HTTPS Certificate");
940 BMCWEB_LOG_DEBUG << "HTTPS certificate install file="
941 << certFile->getCertFilePath();
942 },
943 certs::httpsServiceName, certs::httpsObjectPath, certs::certInstallIntf,
944 "Install", certFile->getCertFilePath());
945}
Ed Tanous002d39b2022-05-31 08:59:27 -0700946
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800947inline void handleHTTPSCertificateGet(
948 App& app, const crow::Request& req,
949 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
950{
951 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
952 {
953 return;
954 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700955
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800956 BMCWEB_LOG_DEBUG << "HTTPS Certificate ID=" << id;
Ed Tanousef4c65b2023-04-24 15:28:50 -0700957 const boost::urls::url certURL = boost::urls::format(
958 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/{}", id);
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800959 std::string objPath =
960 sdbusplus::message::object_path(certs::httpsObjectPath) / id;
961 getCertificateProperties(asyncResp, objPath, certs::httpsServiceName, id,
962 certURL, "HTTPS Certificate");
963}
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700964
965inline void requestRoutesHTTPSCertificate(App& app)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600966{
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800967 BMCWEB_ROUTE(app,
968 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
969 .privileges(redfish::privileges::getCertificateCollection)
970 .methods(boost::beast::http::verb::get)(std::bind_front(
971 handleHTTPSCertificateCollectionGet, std::ref(app)));
972
973 BMCWEB_ROUTE(app,
974 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/")
975 .privileges(redfish::privileges::postCertificateCollection)
976 .methods(boost::beast::http::verb::post)(std::bind_front(
977 handleHTTPSCertificateCollectionPost, std::ref(app)));
978
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700979 BMCWEB_ROUTE(
980 app,
981 "/redfish/v1/Managers/bmc/NetworkProtocol/HTTPS/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700982 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao1e312592022-06-14 12:58:09 +0800983 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800984 std::bind_front(handleHTTPSCertificateGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700985}
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600986
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800987inline void handleLDAPCertificateCollectionGet(
988 App& app, const crow::Request& req,
989 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Marri Devender Rao5968cae2019-01-21 10:27:12 -0600990{
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800991 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
992 {
993 return;
994 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700995
Jiaqing Zhao828252d2022-09-30 13:59:19 +0800996 asyncResp->res.jsonValue["@odata.id"] =
997 "/redfish/v1/AccountService/LDAP/Certificates";
998 asyncResp->res.jsonValue["@odata.type"] =
999 "#CertificateCollection.CertificateCollection";
1000 asyncResp->res.jsonValue["Name"] = "LDAP Certificates Collection";
1001 asyncResp->res.jsonValue["Description"] =
1002 "A Collection of LDAP certificate instances";
Ed Tanous002d39b2022-05-31 08:59:27 -07001003
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001004 getCertificateList(asyncResp, certs::ldapObjectPath,
1005 "/Members"_json_pointer,
1006 "/Members@odata.count"_json_pointer);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001007}
Marri Devender Rao37cce912019-02-20 01:05:22 -06001008
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001009inline void handleLDAPCertificateCollectionPost(
1010 App& app, const crow::Request& req,
1011 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001012{
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001013 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1014 {
1015 return;
1016 }
1017 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1018
1019 if (certFileBody.empty())
1020 {
1021 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1022 messages::unrecognizedRequestBody(asyncResp->res);
1023 return;
1024 }
1025
1026 std::shared_ptr<CertificateFile> certFile =
1027 std::make_shared<CertificateFile>(certFileBody);
1028
1029 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001030 [asyncResp, certFile](const boost::system::error_code& ec,
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001031 const std::string& objectPath) {
1032 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001033 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001034 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1035 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001036 return;
1037 }
1038
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001039 sdbusplus::message::object_path path(objectPath);
1040 std::string certId = path.filename();
Ed Tanousef4c65b2023-04-24 15:28:50 -07001041 const boost::urls::url certURL = boost::urls::format(
1042 "/redfish/v1/AccountService/LDAP/Certificates/{}", certId);
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001043 getCertificateProperties(asyncResp, objectPath, certs::ldapServiceName,
1044 certId, certURL, "LDAP Certificate");
1045 BMCWEB_LOG_DEBUG << "LDAP certificate install file="
1046 << certFile->getCertFilePath();
1047 },
1048 certs::ldapServiceName, certs::ldapObjectPath, certs::certInstallIntf,
1049 "Install", certFile->getCertFilePath());
1050}
Ed Tanous002d39b2022-05-31 08:59:27 -07001051
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001052inline void handleLDAPCertificateGet(
1053 App& app, const crow::Request& req,
1054 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1055{
1056 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1057 {
1058 return;
1059 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001060
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001061 BMCWEB_LOG_DEBUG << "LDAP Certificate ID=" << id;
Ed Tanousef4c65b2023-04-24 15:28:50 -07001062 const boost::urls::url certURL = boost::urls::format(
1063 "/redfish/v1/AccountService/LDAP/Certificates/{}", id);
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001064 std::string objPath =
1065 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1066 getCertificateProperties(asyncResp, objPath, certs::ldapServiceName, id,
1067 certURL, "LDAP Certificate");
1068}
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001069
Jiaqing Zhao99612242022-09-29 15:31:09 +08001070inline void handleLDAPCertificateDelete(
1071 App& app, const crow::Request& req,
1072 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1073{
1074 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1075 {
1076 return;
1077 }
1078
1079 BMCWEB_LOG_DEBUG << "Delete LDAP Certificate ID=" << id;
1080 std::string objPath =
1081 sdbusplus::message::object_path(certs::ldapObjectPath) / id;
1082
1083 deleteCertificate(asyncResp, certs::ldapServiceName, objPath);
1084}
1085
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001086inline void requestRoutesLDAPCertificate(App& app)
Marri Devender Rao37cce912019-02-20 01:05:22 -06001087{
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001088 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1089 .privileges(redfish::privileges::getCertificateCollection)
1090 .methods(boost::beast::http::verb::get)(
1091 std::bind_front(handleLDAPCertificateCollectionGet, std::ref(app)));
1092
1093 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/")
1094 .privileges(redfish::privileges::postCertificateCollection)
1095 .methods(boost::beast::http::verb::post)(std::bind_front(
1096 handleLDAPCertificateCollectionPost, std::ref(app)));
1097
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001098 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001099 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001100 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001101 std::bind_front(handleLDAPCertificateGet, std::ref(app)));
Jiaqing Zhao99612242022-09-29 15:31:09 +08001102
1103 BMCWEB_ROUTE(app, "/redfish/v1/AccountService/LDAP/Certificates/<str>/")
1104 .privileges(redfish::privileges::deleteCertificate)
1105 .methods(boost::beast::http::verb::delete_)(
1106 std::bind_front(handleLDAPCertificateDelete, std::ref(app)));
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001107} // requestRoutesLDAPCertificate
1108
1109inline void handleTrustStoreCertificateCollectionGet(
1110 App& app, const crow::Request& req,
1111 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1112{
1113 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1114 {
1115 return;
1116 }
1117
1118 asyncResp->res.jsonValue["@odata.id"] =
1119 "/redfish/v1/Managers/bmc/Truststore/Certificates/";
1120 asyncResp->res.jsonValue["@odata.type"] =
1121 "#CertificateCollection.CertificateCollection";
1122 asyncResp->res.jsonValue["Name"] = "TrustStore Certificates Collection";
1123 asyncResp->res.jsonValue["Description"] =
1124 "A Collection of TrustStore certificate instances";
1125
1126 getCertificateList(asyncResp, certs::authorityObjectPath,
1127 "/Members"_json_pointer,
1128 "/Members@odata.count"_json_pointer);
1129}
1130
1131inline void handleTrustStoreCertificateCollectionPost(
1132 App& app, const crow::Request& req,
1133 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1134{
1135 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1136 {
1137 return;
1138 }
1139 std::string certFileBody = getCertificateFromReqBody(asyncResp, req);
1140
1141 if (certFileBody.empty())
1142 {
1143 BMCWEB_LOG_ERROR << "Cannot get certificate from request body.";
1144 messages::unrecognizedRequestBody(asyncResp->res);
1145 return;
1146 }
1147
1148 std::shared_ptr<CertificateFile> certFile =
1149 std::make_shared<CertificateFile>(certFileBody);
1150 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001151 [asyncResp, certFile](const boost::system::error_code& ec,
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001152 const std::string& objectPath) {
1153 if (ec)
Ed Tanous002d39b2022-05-31 08:59:27 -07001154 {
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001155 BMCWEB_LOG_ERROR << "DBUS response error: " << ec;
1156 messages::internalError(asyncResp->res);
Ed Tanous002d39b2022-05-31 08:59:27 -07001157 return;
1158 }
Jiaqing Zhao717b9802022-06-06 20:24:04 +08001159
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001160 sdbusplus::message::object_path path(objectPath);
1161 std::string certId = path.filename();
Ed Tanousef4c65b2023-04-24 15:28:50 -07001162 const boost::urls::url certURL = boost::urls::format(
1163 "/redfish/v1/Managers/bmc/Truststore/Certificates/{}", certId);
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001164 getCertificateProperties(asyncResp, objectPath,
1165 certs::authorityServiceName, certId, certURL,
1166 "TrustStore Certificate");
1167 BMCWEB_LOG_DEBUG << "TrustStore certificate install file="
1168 << certFile->getCertFilePath();
1169 },
1170 certs::authorityServiceName, certs::authorityObjectPath,
1171 certs::certInstallIntf, "Install", certFile->getCertFilePath());
1172}
1173
1174inline void handleTrustStoreCertificateGet(
1175 App& app, const crow::Request& req,
1176 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1177{
1178 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1179 {
1180 return;
1181 }
1182
1183 BMCWEB_LOG_DEBUG << "Truststore Certificate ID=" << id;
Ed Tanousef4c65b2023-04-24 15:28:50 -07001184 const boost::urls::url certURL = boost::urls::format(
1185 "/redfish/v1/Managers/bmc/Truststore/Certificates/{}", id);
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001186 std::string objPath =
1187 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1188 getCertificateProperties(asyncResp, objPath, certs::authorityServiceName,
1189 id, certURL, "TrustStore Certificate");
1190}
1191
1192inline void handleTrustStoreCertificateDelete(
1193 App& app, const crow::Request& req,
1194 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1195{
1196 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1197 {
1198 return;
1199 }
1200
1201 BMCWEB_LOG_DEBUG << "Delete TrustStore Certificate ID=" << id;
1202 std::string objPath =
1203 sdbusplus::message::object_path(certs::authorityObjectPath) / id;
1204
Jiaqing Zhao7a3a8f72022-09-29 15:15:58 +08001205 deleteCertificate(asyncResp, certs::authorityServiceName, objPath);
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001206}
1207
1208inline void requestRoutesTrustStoreCertificate(App& app)
Marri Devender Raocfcd5f62019-05-17 08:34:37 -05001209{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001210 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001211 .privileges(redfish::privileges::getCertificate)
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001212 .methods(boost::beast::http::verb::get)(std::bind_front(
1213 handleTrustStoreCertificateCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001214
1215 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/")
Ed Tanoused398212021-06-09 17:05:54 -07001216 .privileges(redfish::privileges::postCertificateCollection)
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001217 .methods(boost::beast::http::verb::post)(std::bind_front(
1218 handleTrustStoreCertificateCollectionPost, std::ref(app)));
Ed Tanous002d39b2022-05-31 08:59:27 -07001219
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001220 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001221 .privileges(redfish::privileges::getCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001222 .methods(boost::beast::http::verb::get)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001223 std::bind_front(handleTrustStoreCertificateGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001224
1225 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/Truststore/Certificates/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001226 .privileges(redfish::privileges::deleteCertificate)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001227 .methods(boost::beast::http::verb::delete_)(
Jiaqing Zhao828252d2022-09-30 13:59:19 +08001228 std::bind_front(handleTrustStoreCertificateDelete, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001229} // requestRoutesTrustStoreCertificate
Marri Devender Rao5968cae2019-01-21 10:27:12 -06001230} // namespace redfish